/*
	File: validateCC.js
	Author: John Gardner
	Date Created: 11/1/2003, updated: 2/26/2005
	Modified By: George Gutin
	Date Modified: 05/31/2005
*/

var cards = new Array();
cards [0] = {name: "Visa", 
			lengths: "13,16", 
			prefixes: "4",
			tests: "4222222222222,4111111111111111,4012888888881881",
			checkdigit: true};
cards [1] = {name: "MasterCard", 
			lengths: "16", 
			prefixes: "51,52,53,54,55",
			tests: "5105105105105100,5555555555554444",
			checkdigit: true};
cards [2] = {name: "AmEx", 
			lengths: "15", 
			prefixes: "34,37",
			tests: "378282246310005,371449635398431,378734493671000",
			checkdigit: true};
cards [3] = {name: "Discover", 
			lengths: "16", 
			prefixes: "6011",
			tests: "6011111111111117,6011000990139424",
			checkdigit: true};
cards [4] = {name: "DinersClub", 
			lengths: "14,", 
			prefixes: "300,301,302,303,304,305,36,38",
			tests: "38520000023237,30569309025904",
			checkdigit: true};
cards [5] = {name: "JCB", 
			lengths: "15,16", 
			prefixes: "3,1800,2131",
			tests: "3530111333300000,3566002020360505",
			checkdigit: true};
cards [6] = {name: "Enroute", 
			lengths: "15", 
			prefixes: "2014,2149",
			checkdigit: true};
cards [7] = {name: "CarteBlanche", 
			lengths: "14", 
			prefixes: "300,301,302,303,304,305,36,38",
			checkdigit: true};

var ccErrors = new Array ();
ccErrors [0] = "Unknown credit card type. Please try again.";
ccErrors [1] = "No credit card number provided. Please enter credit card number.";
ccErrors [2] = "Credit card number is in invalid format. Please try again.";
ccErrors [3] = "Credit card number is invalid. Please try again.";
ccErrors [4] = "Credit card number has an inappropriate number of digits.\nPlease try again.";

var expErrors = new Array ();
expErrors [0] = "Please select/enter expiration month/year.";
expErrors [1] = "Expiration date is not valid. Please try again.";

var checkCard = false;
var checkDate = false;

// Complex check (4 arguments: cardName, cardNumber, expMonth, expYear)
function validateCC (cardName, cardNumber, expMonth, expYear) {
	if ( typeof ( cardName ) == "object" && typeof ( cardNumber ) == "object" && 
		typeof ( expMonth ) == "object" && typeof ( expYear ) == "object") { 
		
		var checkCard = checkCreditCard (cardName, cardNumber);
		var checkDate = checkExpDate (expMonth, expYear);

		return checkCard && checkDate;
		
	} else return false;
}

// Check for expiration date
function checkExpDate (myexpMonth, myexpYear) {

	var expMonth = "";
	var expYear  = "";

	if ( typeof ( myexpMonth ) == "object" && typeof ( myexpYear ) == "object") { 
		typeMonth = myexpMonth.type.toUpperCase();
		typeYear  = myexpYear.type.toUpperCase();
	
		if (typeMonth == "TEXT" && typeYear == "TEXT") {	
			expMonth = myexpMonth.value; 
			expYear  = myexpYear.value;
		} else if (typeMonth == "SELECT-ONE" && typeYear == "SELECT-ONE") {
			expMonth = myexpMonth.options[myexpMonth.options.selectedIndex].value;
			expYear  = myexpYear.options[myexpYear.options.selectedIndex].value;
		}
 
	} else return false;

	// Check if expiration month is not empty
	if (expMonth.length == 0)  {
		alert (expErrors[0]);
		return false; 
	}

	// Check if expiration year is not empty
	if (expYear.length == 0)  {
		alert (expErrors[0]);
		return false; 
	}

	if (expYear.length == 1) expYear = "200" + expYear;
	else expYear = "20" + expYear;
	
	var today = new Date();
	var expiry = new Date(expYear, expMonth);

	if (today.getTime() > expiry.getTime()) {
		alert (expErrors[1]);
		return false;
	} 

	return true;
}

// Check for cc type and number
function checkCreditCard (mycardName, mycardNumber) {

	if ( typeof ( mycardName ) == "object" && typeof ( mycardNumber ) == "object" ) { 
		typeCardField = mycardName.type.toUpperCase();
	
		if (typeCardField == "SELECT-ONE") {
			cardName = mycardName.options[mycardName.options.selectedIndex].value;
		} else if (typeCardField == "TEXT") {
			cardName = mycardName.value;
		}
		
		cardNumber = mycardNumber.value; 
	} else return false;

	// Check if it is valid cc type
	var cardType = -1;
	for (var i=0; i<cards.length; i++) {
		if (cardName.toLowerCase() == cards[i].name.toLowerCase()) {
			cardType = i;
		}
	}
	if (cardType == -1) {
		alert (ccErrors[0]);
		return false; 
	}

	// Check if cc number is not empty string
	if (cardNumber.length == 0)  {
		alert (ccErrors[1]);
		return false; 
	}
  
	// Check if cc number is numeric
	var regCardExp = /^([0-9]{4})\s?([0-9]{4})\s?([0-9]{4})\s?([0-9]{1,4})$/;
	if (!(regCardExp.test(cardNumber)))  {
		alert (ccErrors[2]);
		return false; 
	}
    
	// Remove spaces from the credit card number (between 4-digit groups)
	regCardExp.exec(cardNumber);
	cardNumber = RegExp.$1 + RegExp.$2 + RegExp.$3 + RegExp.$4;
	
	// Check the modulus 10 - START
	//**************************************************************************
	var checksum = 0;                                  // running checksum total
	var mychar = "";                                   // next char to process
	var j = 1;                                         // takes value of 1 or 2
	
	// Process each digit one by one starting at the right
	var calc;
	for (i = cardNumber.length - 1; i >= 0; i--) {
		// Extract the next digit and multiply by 1 or 2 on alternative digits.
		calc = Number(cardNumber.charAt(i)) * j;
		// If the result is in two digits add 1 to the checksum total
		if (calc > 9) {
			checksum = checksum + 1;
			calc = calc - 10;
		}
		// Add the units element to the checksum total
		checksum = checksum + calc;
		// Switch the value of j
		if (j ==1) {j = 2} else {j = 1};
	} 
	// If checksum is divisible by 10, it is a valid modulus 10.
	if (checksum % 10 != 0)  {
		alert (ccErrors[3]);
		return false; 
	}
	//**************************************************************************
	// Check the modulus 10 - END
 
	var prefixValid = false;
	// Create array of valid prefixes for this cc type
	var arrayPrefix = new Array ();
	arrayPrefix = cards[cardType].prefixes.split(",");
	
	// Check if cc number prefix is valid
	for (i=0; i<arrayPrefix.length; i++) {
		var tempExp = new RegExp ("^" + arrayPrefix[i]);
		if (tempExp.test(cardNumber)) {
			prefixValid = true;
			break;
		}
	}
	if (!prefixValid) {
		alert (ccErrors[3]);
		return false; 
	}
	
	var lengthValid = false;
	// Create array of valid lengths for this cc type
	var arrayLength = new Array ();
	arrayLength = cards[cardType].lengths.split(",");
	
	// Check if cc number length is valid
	for (i=0; i<arrayLength.length; i++) {
		if (cardNumber.length == arrayLength[i]) { 
			lengthValid = true; 
			break;
		}
	}
	if (!lengthValid) {
		alert (ccErrors[4]);
		return false; 
	}
  
	// Check if cc number is test for this cc type
	if (cardType != 6 && cardType != 6) {
		// Create array of test cc numbers for this cc type
		var arrayTest = new Array ();
		arrayTest = cards[cardType].tests.split(",");
		
		// Check if cc number length is valid
		for (i=0; i<arrayTest.length; i++) {
			if (cardNumber == arrayTest[i]) { 
				if (confirm('You provided a test credit card number. Would you like to proceed?')) break;
				else return false;
			}
		}
	}
	
	return true;
}

// Check for ONLY cc number
function checkOnlyNumber (mycardNumber) {

	if ( typeof ( mycardNumber ) == "object" ) { cardNumber = mycardNumber.value; } 
	else return false;

	var cardName = "";
	var term     = 0;
	
	for (i=1; i<5; i++) {
		myPrefix = cardNumber.substr(0,i);
		for (var j=0; j<cards.length; j++) {
			// Create array of valid prefixes for "j" cc type
			var arrayPrefix = new Array ();
			arrayPrefix = cards[j].prefixes.split(",");
			// Check for cc number prefix for "j" cc type
			for (k=0; k<arrayPrefix.length; k++) {
				if (myPrefix == arrayPrefix[k]) {
					cardName = cards[j].name;
					term = 1;
				}
				if (term == 1) break;
			}
			if (term == 1) break;
		}
		if (term == 1) break;
	}
	
	if (cardName == "") {
		alert (ccErrors[3]);
		return false; 
	} else {
		var mycardName = new Object();
		mycardName.type = "TEXT";
		mycardName.value = cardName;
		return checkCreditCard (mycardName, mycardNumber);
	}

}