// JavaScript Document

function validate_form(n) {
	var form = document.getElementById(n);
	
	var fields = new Array();
	
	for (i=0; i<form.elements.length; i++) {
		var element = form.elements[i]
		
		/////     if element is a checkbox     /////
		if (element.type == "checkbox") {
			
			// check if required //
			if (required(element) && element.checked != true) {
				fields.push([element, "This check box is required. \r\n"]);
			}
		}
		
		/////     if element is text field     /////
		if (element.type == "text") {
			
			// check if required //
			if (required(element) && element.value == "") {
				fields.push([element, "Please fill out this field. \r\n"]);
			}
			
			// check if textfield is email //
			if (required(element) && email(element)) {
				if (!valid_email(element)) {
					fields.push([element, "Please use a valid email address. \r\n"]);
				}
			}
			
			// check if textfield is credit card
			if (required(element) && credit_card(element)) {
				if(!valid_credit_card(element)) {
					fields.push([element, "Please use a valid credit card number. \r\n"]);
				}
			}
			// check if textfield is credit card
			if (required(element) && date(element)) {
				if(!valid_date(element)) {
					fields.push([element, "Please use a valid numerical date format. \r\n"]);
				}
			}
		}
	}
	if (fields.length > 0) {
		var error = "Please Fix the following error: \r\n";
		error += fields[0][1];
		fields[0][0].focus();
		alert(error);
		return false;
	} else {
		return true;
	}
}

function required(n) {
	if (n.getAttribute('class') == null || n.getAttribute('class') == '') {
		return false;
	} else {
		var str = n.getAttribute('class');
		if (str.search('required')>-1) {
			return true;
		}
	}
}

function email(n) {
	var str = n.getAttribute('class');
	if (str.search('email')>-1) {
		return true;
	}
}

function valid_email(n) {
	if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(n.value)){
		return true;
	} else {
		return false;
	}
}

function credit_card(n) {
	var str = n.getAttribute('class');
	if (str.search('credit')>-1) {
		return true;
	} else {
		return false;
	}
}

var ccErrorNo = 0;
var ccErrors = new Array ()

ccErrors [0] = "Unknown card type";
ccErrors [1] = "No card number provided";
ccErrors [2] = "Credit card number is in invalid format";
ccErrors [3] = "Credit card number is invalid";
ccErrors [4] = "Credit card number has an inappropriate number of digits";

function valid_credit_card (n, cardname) {

	n = n.value;
	
	cardnumber = n
	
	var selObj = document.getElementById('cc_type');
	var selIndex = selObj.selectedIndex;
	cardname = selObj.options[selIndex].value;
	
	var cards = new Array();
	cards [0] = {name: "Visa", length: "13,16", prefixes: "4",checkdigit: true};
	cards [1] = {name: "MasterCard", length: "16", prefixes: "51,52,53,54,55", checkdigit: true};
	cards [2] = {name: "DinersClub", length: "14,16", prefixes: "305, 36, 38, 54,55", checkdigit: true};
	cards [3] = {name: "CarteBlanche", length: "14", prefixes: "300,301,302,303,304,305", checkdigit: true};
	cards [4] = {name: "AmEx", length: "15", prefixes: "34,37", checkdigit: true};
	cards [5] = {name: "Discover", length: "16", prefixes: "6011,622,64,65", checkdigit: true};
	cards [6] = {name: "JCB", length: "16", prefixes: "35", checkdigit: true};
	cards [7] = {name: "enRoute", length: "15", prefixes: "2014,2149", checkdigit: true};
	cards [8] = {name: "Solo", length: "16,18,19", prefixes: "6334, 6767", checkdigit: true};
	cards [9] = {name: "Switch", length: "16,18,19", prefixes: "4903,4905,4911,4936,564182,633110,6333,6759", checkdigit: true};
	cards [10] = {name: "Maestro", length: "12,13,14,15,16,18,19", prefixes: "5018,5020,5038,6304,6759,6761", checkdigit: true};
	cards [11] = {name: "VisaElectron", length: "16", prefixes: "417500,4917,4913,4508,4844", checkdigit: true};
	cards [12] = {name: "LaserCard", length: "16,17,18,19", prefixes: "6304,6706,6771,6709", checkdigit: true};
	
	// Establish card type
	var cardType = -1;
	for (var i=0; i<cards.length; i++) {
		// See if it is this card (ignoring the case of the string)
		if (cardname.toLowerCase () == cards[i].name.toLowerCase()) {
			cardType = i;
			break;
		}
	}
	
	// If card type not found, report an error
	if (cardType == -1) {
		ccErrorNo = 0;
		return false; 
	}
	
	// Ensure that the user has provided a credit card number
	if (cardnumber.length == 0)  {
		ccErrorNo = 1;
		return false; 
	}
	
	// Now remove any spaces from the credit card number
	cardnumber = cardnumber.replace (/\s/g, "");
	
	// Check that the number is numeric
	var cardNo = cardnumber
	var cardexp = /^[0-9]{13,19}$/;
	if (!cardexp.exec(cardNo))  {
		ccErrorNo = 2;
		return false; 
	}
	
	// Now check the modulus 10 check digit - if required
	if (cards[cardType].checkdigit) {
		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 = cardNo.length - 1; i >= 0; i--) {
	
		// Extract the next digit and multiply by 1 or 2 on alternative digits.
		calc = Number(cardNo.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};
	} 
	
	// All done - if checksum is divisible by 10, it is a valid modulus 10.
	// If not, report an error.
	if (checksum % 10 != 0)  {
		ccErrorNo = 3;
		return false; 
	}
	}  
	
	// The following are the card-specific checks we undertake.
	var LengthValid = false;
	var PrefixValid = false; 
	var undefined; 
	
	// We use these for holding the valid lengths and prefixes of a card type
	var prefix = new Array ();
	var lengths = new Array ();
	
	// Load an array with the valid prefixes for this card
	prefix = cards[cardType].prefixes.split(",");
	
	// Now see if any of them match what we have in the card number
	for (i=0; i<prefix.length; i++) {
	var exp = new RegExp ("^" + prefix[i]);
	if (exp.test (cardNo)) PrefixValid = true;
	}
	
	// If it isn't a valid prefix there's no point at looking at the length
	if (!PrefixValid) {
	ccErrorNo = 3;
	return false; 
	}
	
	// See if the length is valid for this card
	lengths = cards[cardType].length.split(",");
	for (j=0; j<lengths.length; j++) {
	if (cardNo.length == lengths[j]) LengthValid = true;
	}
	
	// See if all is OK by seeing if the length was valid. We only check the 
	// length if all else was hunky dory.
	if (!LengthValid) {
	ccErrorNo = 4;
	return false; 
	};   
	
	// The credit card is in the required format.
	return true;
}

function date(n) {
	var str = n.getAttribute('class');
	if (str.search('date')>-1) {
		return true;
	}
}

function valid_date(n) {
	txtDate = n.value;
	
	var objDate;  // date object initialized from the txtDate string  
	var mSeconds; // milliseconds from txtDate  
	
	// date length should be 10 characters - no more, no less  
	if (txtDate.length != 10) return false;  
	
	// extract day, month and year from the txtDate string  
	// expected format is mm/dd/yyyy  
	// subtraction will cast variables to integer implicitly  
	var day   = txtDate.substring(3,5)  - 0;  
	var month = txtDate.substring(0,2)  - 1; // because months in JS start with 0  
	var year  = txtDate.substring(6,10) - 0;  
	
	// third and sixth character should be /  
	if (txtDate.substring(2,3) != '/') return false;
	if (txtDate.substring(5,6) != '/') return false;
	
	// test year range  
	if (year < 999 || year > 3000) return false;  
	
	// convert txtDate to the milliseconds  
	mSeconds = (new Date(year, month, day)).getTime();  
	
	// initialize Date() object from calculated milliseconds  
	objDate = new Date();  
	objDate.setTime(mSeconds);  
	
	// compare input parameter date and created Date() object  
	// if difference exists then date isn't valid  
	if (objDate.getFullYear() != year)  return false;  
	if (objDate.getMonth()    != month) return false;  
	if (objDate.getDate()     != day)   return false;  
	
	// otherwise return true  
	return true;  
}
