// bool checkString(string);
// bool checkAddress(string);
// bool checkZip(string);
// bool checkEmail(string);
// bool checkPhone(string);
// bool checkCVV2(string);
// bool CheckCardNumber(form);

	function checkString(string) {
		var valid = true;
		var GoodChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -.";

		var i = 0;
		if(string == "") {
			// Return false if number is empty
			valid = false;
		}
		for (i = 0; i <= string.length - 1; i++) {
			if (GoodChars.indexOf(string.charAt(i)) == -1) {
				valid = false;
			}
		}
		return valid;
	}

	function checkAddress(string) {
		var valid = true;
		var GoodChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -.1234567890/";

		var i = 0;
		for (i = 0; i <= string.length - 1; i++) {
			if (GoodChars.indexOf(string.charAt(i)) == -1) {
				valid = false;
			}
		}
		return valid;
	}

	function checkZip(string) {
		var valid = true;
		var GoodChars = "0123456789-";
		var hyphencount = 0;

		if (string.length !=5 && string.length !=10 && string.length !=9) {
			valid = false;
		}
		
		for (var i=0; i < string.length; i++) {
			temp = "" + string.substring(i, i+1);
			if (temp == "-") hyphencount++;
			if (GoodChars.indexOf(temp) == "-1") {
				valid = false;
			}

			if ((hyphencount > 1) || ((string.length == 10) && ""+string.charAt(5) != "-")) {
				valid = false;
			}
		}

		return valid;
	}

// Check that an email address is valid based on RFC 821 (?)
function isValidEmail(address) {
   if (address != '' && address.search) {
      if (address.search(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/) != -1) return true;
      else return false;
   }
   
   // allow empty strings to return true - screen these with either a 'required' test or a 'length' test
   else return true;
}

// Check that an email address has the form something@something.something
// This is a stricter standard than RFC 821 (?) which allows addresses like postmaster@localhost
function checkEmail(address) {
   if (isValidEmail(address) == false) return false;
   var domain = address.substring(address.indexOf('@') + 1);
   if (domain.indexOf('.') == -1) return false;
   if (domain.indexOf('.') == 0 || domain.indexOf('.') == domain.length - 1) return false;
   return true;
}

	function checkPhone(string) {
		var valid = true;
		var GoodChars = "0123456789";

		for (var i=0; i < string.length; i++) {
			if (GoodChars.indexOf(string.charAt(i)) == "-1") {
				valid = false;
			}
		}
		return valid;
	}

	function checkCVV2(string) {
		var valid = true;
		var GoodChars = "0123456789";

		if (string.length !=3 && string.length != 4) {
			valid = false;
		}
		
		for (var i=0; i < string.length; i++) {
			if (GoodChars.indexOf(string.charAt(i)) == "-1") {
				valid = false;
			}
		}
		return valid;
	}




var Cards = new makeArray(8);
Cards[0] = new CardType("MasterCard", "51,52,53,54,55", "16");
var MasterCard = Cards[0];
Cards[1] = new CardType("VisaCard", "4", "13,16");
var VisaCard = Cards[1];
Cards[2] = new CardType("AmExCard", "34,37", "15");
var AmExCard = Cards[2];
Cards[3] = new CardType("DinersClubCard", "30,36,38", "14");
var DinersClubCard = Cards[3];
Cards[4] = new CardType("DiscoverCard", "6011", "16");
var DiscoverCard = Cards[4];
Cards[5] = new CardType("enRouteCard", "2014,2149", "15");
var enRouteCard = Cards[5];
Cards[6] = new CardType("JCBCard", "3088,3096,3112,3158,3337,3528", "16");
var JCBCard = Cards[6];
var LuhnCheckSum = Cards[7] = new CardType();

//*************************************************************************\
//CheckCardNumber(form)
//function called when users click the "check" button.
//*************************************************************************/
function CheckCardNumber(form) {
	var tmpyear;

	tmpyear = "20" + form.Year.options[form.Year.selectedIndex].value;
	tmpmonth = form.Month.options[form.Month.selectedIndex].value;

	if (!(new CardType()).isExpiryDate(tmpyear, tmpmonth)) {
		form.Month.className = "error";
		form.Year.className = "error";
		return "This card has already expired.";
	}

	card = form.Type.options[form.Type.selectedIndex].value;
	var retval = eval(card + ".checkCardNumber(\"" + form.Accountnumber.value +
		"\", " + tmpyear + ", " + tmpmonth + ");");
	cardname = "";

	if (retval)
		return "";
	else {
		// The cardnumber has the valid luhn checksum, but we want to know which
		// cardtype it belongs to.
		for (var n = 0; n < Cards.size; n++) {
			if (Cards[n].checkCardNumber(form.Accountnumber.value, tmpyear, tmpmonth)) {
				cardname = Cards[n].getCardType();
				break;
			}
		}

		if (cardname.length > 0) {
			form.Type.className = "error";
			return "This appears to be a " + cardname + " number, not a " + card + " number.";
		} else {
			form.Accountnumber.className = "error";
			return "This card number is not valid.";
		}
	}
}
//*************************************************************************\
//Object CardType([String cardtype, String rules, String len, int year, 
//                                        int month])
//cardtype    : type of card, eg: MasterCard, Visa, etc.
//rules       : rules of the cardnumber, eg: "4", "6011", "34,37".
//len         : valid length of cardnumber, eg: "16,19", "13,16".
//year        : year of expiry date.
//month       : month of expiry date.
//eg:
//var VisaCard = new CardType("Visa", "4", "16");
//var AmExCard = new CardType("AmEx", "34,37", "15");
//*************************************************************************/
function CardType() {
var n;
var argv = CardType.arguments;
var argc = CardType.arguments.length;

this.objname = "object CardType";

var tmpcardtype = (argc > 0) ? argv[0] : "CardObject";
var tmprules = (argc > 1) ? argv[1] : "0,1,2,3,4,5,6,7,8,9";
var tmplen = (argc > 2) ? argv[2] : "13,14,15,16,19";

this.setCardNumber = setCardNumber;  // set CardNumber method.
this.setCardType = setCardType;  // setCardType method.
this.setLen = setLen;  // setLen method.
this.setRules = setRules;  // setRules method.
this.setExpiryDate = setExpiryDate;  // setExpiryDate method.

this.setCardType(tmpcardtype);
this.setLen(tmplen);
this.setRules(tmprules);
if (argc > 4)
this.setExpiryDate(argv[3], argv[4]);

this.checkCardNumber = checkCardNumber;  // checkCardNumber method.
this.getExpiryDate = getExpiryDate;  // getExpiryDate method.
this.getCardType = getCardType;  // getCardType method.
this.isCardNumber = isCardNumber;  // isCardNumber method.
this.isExpiryDate = isExpiryDate;  // isExpiryDate method.
this.luhnCheck = luhnCheck;// luhnCheck method.
return this;
}

//*************************************************************************\
//boolean checkCardNumber([String cardnumber, int year, int month])
//return true if cardnumber pass the luhncheck and the expiry date is
//valid, else return false.
//*************************************************************************/
function checkCardNumber() {
var argv = checkCardNumber.arguments;
var argc = checkCardNumber.arguments.length;
var cardnumber = (argc > 0) ? argv[0] : this.cardnumber;
var year = (argc > 1) ? argv[1] : this.year;
var month = (argc > 2) ? argv[2] : this.month;

this.setCardNumber(cardnumber);
this.setExpiryDate(year, month);

if (!this.isCardNumber())
return false;
if (!this.isExpiryDate())
return false;

return true;
}
//*************************************************************************\
//String getCardType()
//return the cardtype.
//*************************************************************************/
function getCardType() {
return this.cardtype;
}
//*************************************************************************\
//String getExpiryDate()
//return the expiry date.
//*************************************************************************/
function getExpiryDate() {
return this.month + "/" + this.year;
}
//*************************************************************************\
//boolean isCardNumber([String cardnumber])
//return true if cardnumber pass the luhncheck and the rules, else return
//false.
//*************************************************************************/
function isCardNumber() {
var argv = isCardNumber.arguments;
var argc = isCardNumber.arguments.length;
var cardnumber = (argc > 0) ? argv[0] : this.cardnumber;
if (!this.luhnCheck())
return false;

for (var n = 0; n < this.len.size; n++)
if (cardnumber.toString().length == this.len[n]) {
for (var m = 0; m < this.rules.size; m++) {
var headdigit = cardnumber.substring(0, this.rules[m].toString().length);
if (headdigit == this.rules[m])
return true;
}
return false;
}
return false;
}

//*************************************************************************\
//boolean isExpiryDate([int year, int month])
//return true if the date is a valid expiry date,
//else return false.
//*************************************************************************/
function isExpiryDate() {
var argv = isExpiryDate.arguments;
var argc = isExpiryDate.arguments.length;

year = argc > 0 ? argv[0] : this.year;
month = argc > 1 ? argv[1] : this.month;

if (!isNum(year+""))
return false;
if (!isNum(month+""))
return false;
today = new Date();
expiry = new Date(year, month);
if (today.getTime() > expiry.getTime())
return false;
else
return true;
}

//*************************************************************************\
//boolean isNum(String argvalue)
//return true if argvalue contains only numeric characters,
//else return false.
//*************************************************************************/
function isNum(argvalue) {
argvalue = argvalue.toString();

if (argvalue.length == 0)
return false;

for (var n = 0; n < argvalue.length; n++)
if (argvalue.substring(n, n+1) < "0" || argvalue.substring(n, n+1) > "9")
return false;

return true;
}

//*************************************************************************\
//boolean luhnCheck([String CardNumber])
//return true if CardNumber pass the luhn check else return false.
//Reference: http://www.ling.nwu.edu/~sburke/pub/luhn_lib.pl
//*************************************************************************/
function luhnCheck() {
var argv = luhnCheck.arguments;
var argc = luhnCheck.arguments.length;

var CardNumber = argc > 0 ? argv[0] : this.cardnumber;

if (! isNum(CardNumber)) {
return false;
  }

var no_digit = CardNumber.length;
var oddoeven = no_digit & 1;
var sum = 0;

for (var count = 0; count < no_digit; count++) {
var digit = parseInt(CardNumber.charAt(count));
if (!((count & 1) ^ oddoeven)) {
digit *= 2;
if (digit > 9)
digit -= 9;
}
sum += digit;
}
if (sum % 10 == 0)
return true;
else
return false;
}

//*************************************************************************\
//ArrayObject makeArray(int size)
//return the array object in the size specified.
//*************************************************************************/
function makeArray(size) {
this.size = size;
return this;
}

//*************************************************************************\
//CardType setCardNumber(cardnumber)
//return the CardType object.
//*************************************************************************/
function setCardNumber(cardnumber) {
this.cardnumber = cardnumber;
return this;
}

//*************************************************************************\
//CardType setCardType(cardtype)
//return the CardType object.
//*************************************************************************/
function setCardType(cardtype) {
this.cardtype = cardtype;
return this;
}

//*************************************************************************\
//CardType setExpiryDate(year, month)
//return the CardType object.
//*************************************************************************/
function setExpiryDate(year, month) {
this.year = year;
this.month = month;
return this;
}

//*************************************************************************\
//CardType setLen(len)
//return the CardType object.
//*************************************************************************/
function setLen(len) {
// Create the len array.
if (len.length == 0 || len == null)
len = "13,14,15,16,19";

var tmplen = len;
n = 1;
while (tmplen.indexOf(",") != -1) {
tmplen = tmplen.substring(tmplen.indexOf(",") + 1, tmplen.length);
n++;
}
this.len = new makeArray(n);
n = 0;
while (len.indexOf(",") != -1) {
var tmpstr = len.substring(0, len.indexOf(","));
this.len[n] = tmpstr;
len = len.substring(len.indexOf(",") + 1, len.length);
n++;
}
this.len[n] = len;
return this;
}

//*************************************************************************\
//CardType setRules()
//return the CardType object.
//*************************************************************************/
function setRules(rules) {
// Create the rules array.
if (rules.length == 0 || rules == null)
rules = "0,1,2,3,4,5,6,7,8,9";
  
var tmprules = rules;
n = 1;
while (tmprules.indexOf(",") != -1) {
tmprules = tmprules.substring(tmprules.indexOf(",") + 1, tmprules.length);
n++;
}
this.rules = new makeArray(n);
n = 0;
while (rules.indexOf(",") != -1) {
var tmpstr = rules.substring(0, rules.indexOf(","));
this.rules[n] = tmpstr;
rules = rules.substring(rules.indexOf(",") + 1, rules.length);
n++;
}
this.rules[n] = rules;
return this;
}



function validPhone(phNumber) {
	// Pass in form and field name
	// Makes the function reusable
	// format = "0" | "none" | "";
	//          "1" | "()";
	//          "-" | (user selected separator);
	// Note: all above must be passed as quoted strings
	// Otherwise generates an error in comparison
	
	// required = 0 | false | null for optional;
	//            1 | true for required;
	// Note: the above are boolean|numeric, no quotes
	
	// VARIABLES USED:
	//   killForm = a numeric flag used to stop or post the form
	//    special = a string of special characters to strip out for validation
	//     phOrig = the stored original user input
	//      phNum = the same, but will be manipulated/changed by the script
	//    phTrunc = the phone number truncated, minus any Ext.; will be changed by the script
	//  phLowCase = the phone number in lowercase; to find "Ext.", "ext", etc.
	// phLowCaseX = phTrunc in lowercase; to find "x000", "X 000", etc.
	//    phNoExt = the changed number without any extension; sent to the format function
	//        ext = holds the value "Ext" || "ext" || "ex." etc. (if found in the phone number)
	//          x = holds the value "x" || "X" (if found in the phone number)
	//     before = a section of the phone number before an illegal character
	//      after = a section of the phone number after an illegal character
	// formattedPhone = the final output of both functions

	var i = 0;
	var j = 0;
	var killForm = 0;
	var useForm = false;

		useForm = false;
		var phOrig = validPhone.arguments[0];
		var phNum = validPhone.arguments[0];
		var phTrunc = validPhone.arguments[0];
		var phNoExt = validPhone.arguments[0];

	var formattedPhone = "";
	var phLowCase = "";
	var phLowCaseX = "";
	var before = "";
	var after = "";
	var x = "";
	var ext = "";
	var extension = "";
	// Add any special characters to strip out here:
	var special = "()-. ,:;/\|*+#@&=";

	// If it's blank but not required, let it through
	if (phNum == "") {
		return true;
	}
	else {
		// Find "Ext. 000" extensions and separate them out
		phLowCase = phNum.toLowerCase();
		ext = phLowCase.lastIndexOf("ext");
		if (ext != -1) {
			extension = phOrig.substring(ext,phOrig.length);
			phTrunc = phNum.substring(0,ext);
			phNoExt = phNum.substring(0,ext);
		}
		else {
			extension = "";
		}
		// This strips out formatting characters for length comparison
		for (i=0;i<phTrunc.length;i++) {
			var phChar = phTrunc.charAt(i);
			for (j=0;j<special.length;j++) {
				if (phChar == special.charAt(j)) {
					before = phTrunc.substring(0,i);
					after = phTrunc.substring(i+1);
					phTrunc = before + after;
					// If 2 illegal characters are consecutive, we have to test again using
					// the current value of phNum.charAt(i) before incrementing i, otherwise we will miss it.
					i--;
				}
			}
		}
// Obsoleted.  Could not filter out X contained in the number if no extension present
// ==================================================================================
//		// Find x000 extensions.  Must be done after stripping extra characters
//		// because then x denoting an extension would only appear at character 7, 10 or 11
//		if (extension == "") {
//			phLowCaseX = phTrunc.toLowerCase();
//			// Don't want to mistakenly match an X that is part of the phone number itself
//			x = phLowCaseX.lastIndexOf("x");
//			if (phTrunc.length > 7 && x != -1 && (x == 7 || x == 10 || x == 11)) {
//				extension = phTrunc.substring(x,phTrunc.length);
//				phTrunc = phTrunc.substring(0,x);
//				phNoExt = phNum.substring(0,phNum.indexOf(extension));
//			}
//		}
// ==================================================================================
		if (phTrunc.length == 11) {
			// Make sure the first 4 characters (1-800) are numeric
			for (i=0;i<4;i++) {
				if (parseInt(phTrunc.charAt(i)) + "" == Number.NaN + "") {
					killForm = 1;
				}
			}
			// There is no such thing as 2-800 or 1-000
			if (phTrunc.charAt(0) != "1" && phTrunc.charAt(0) != "0" || phTrunc.charAt(1) == "0") {
				killForm = 1;
			}
		}
		else if (phTrunc.length == 10) {
			// Make sure the area code is numeric
			for (i=0;i<3;i++) {
				if (parseInt(phTrunc.charAt(i)) + "" == Number.NaN + "") {
					killForm = 1;
				}
			}
			// No area codes begin with 0 or 1
			if (phTrunc.charAt(0) == "0" || phTrunc.charAt(0) == "1") {
				killForm = 1;		
			}
		}
		else if (phTrunc.length == 7) {
			// No phone numbers begin with 0 or 1
			if (phTrunc.charAt(0) == "0" || phTrunc.charAt(0) == "1") {
				killForm = 1;		
			}
	        }
		// A proper US ph# should be 7, 10 or 11 characters now
		else if (phTrunc.length != 7 && phTrunc.length != 10 && phTrunc.length != 11) {
			killForm = 1;
		}
	}
	
	if (killForm == 1) {
		return false;
	} else {
		return true;
	}
}
