
////////////////////////////////////////////////////////
/// Functions used to validate Credit Card Numbers
////////////////////////////////////////////////////////


var ccnumchk1 = function validateCCNum(cardType, cardNum)
{
	var name = "validateCCNum";

	var result = false;
	cardType = cardType.toUpperCase();
	
	var cardLen = cardNum.length;
	var firstdig = cardNum.substring(0,1);
	var seconddig = cardNum.substring(1,2);
	var first4digs = cardNum.substring(0,4);

	switch (cardType)
	{
		case "VISA":
			result = ((cardLen == 16) || (cardLen == 13)) && (firstdig == "4");
			break;
		case "AMEX":
			var validNums = "47";
			result = (cardLen == 15) && (firstdig == "3") && (validNums.indexOf(seconddig)>=0);
			break;
		case "MASTERCARD":
			var validNums = "12345";
			result = (cardLen == 16) && (firstdig == "5") && (validNums.indexOf(seconddig)>=0);
			break;
		case "DISCOVER":
			result = (cardLen == 16) && (first4digs == "6011");
			break;
		case "DINERS":
			var validNums = "068";
			result = (cardLen == 14) && (firstdig == "3") && (validNums.indexOf(seconddig)>=0);
			break;
	}
	return result;
}

var ccnumchk2 = function validateCCNum2(cardType, cardNumber)
{	
	var name = "validateCCNum2";

	var cardNumberLength = cardNumber.length;
	cardType = cardType.toUpperCase();
	var lengthIsValid = false;
	var prefixIsValid = false;
	var prefixRegExp = null;
	
	switch(cardType)
	{
		case "MASTERCARD":
			lengthIsValid = (cardNumberLength == 16);
			prefixRegExp = /^5[1-5]/;
			break;

		case "VISA":
			lengthIsValid = (cardNumberLength == 16 || cardNumberLength == 13);
			prefixRegExp = /^4/;
			break;

		case "AMEX":
			lengthIsValid = (cardNumberLength == 15);
			prefixRegExp = /^3(4|7)/;
			break;
			
		case "DISCOVER":
			lengthIsValid = (cardNumberLength == 16);
			prefixRegExp = /^6011/;
			break;
			
		case "DINERS":
			lengthIsValid = (cardNumberLength == 14);
			prefixRegExp = /^3(0|6|8)/;			
		break;

		default:
			lengthIsValid = false;
			prefixRegExp = /^$/;
	}

	prefixIsValid = prefixRegExp.test(cardNumber);
	
  return (prefixIsValid && lengthIsValid);
  
}

// Luhn Formula 1
var luhnchk1 = function LuhnCheck(str) 
{
	this.name = "LuhnCheck1";
	
  var result = true;

  var sum = 0; 
  var mul = 1; 
  var strLen = str.length;
  
  for (i = 0; i < strLen; i++) 
  {
    var digit = str.substring(strLen-i-1,strLen-i);
    var 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)
    result = false;
    
  return result;
}

// Luhn Formula 2 - simple
var luhnchk2 = function LuhnCheck2( cardNumber )  // LUHN Formula for validation of credit card numbers.
{
	this.name = "LuhnCheck2";

	var ar = new Array( cardNumber.length );
	var i = 0, sum = 0;

	// Loads up the array with each digit of the credit card number
	for( i = 0; i < cardNumber.length; ++i ) 
	{
		ar[i] = parseInt(cardNumber.charAt(i));
	}
	
	// Double the even-numbered digits
	for( i = ar.length - 2; i >= 0; i-=2 ) // you have to start from the right, and work back.
	{
		ar[i] *= 2;							 						 // every second digit starting with the right most (check digit)
		if( ar[i] > 9 ) ar[i]-=9;						 // will be doubled, and summed with the skipped digits.
	}										 									 // if the double digit is > 9, ADD those individual digits together 
		
	// Sum up odd-numbered digits with the even-numbered doubled digits
	for( i = 0; i < ar.length; ++i ) 
	{
		sum += ar[i];						 						// if the sum is divisible by 10 mod10 succeeds
	}
	
	// Test for Mod 10 result
	return (((sum%10)==0)?true:false);	 	
}

// Luhn Formula 3 - very efficient
var luhnchk3 = function LuhnCheck3(strNum) 
{  
	 this.name = "LuhnCheck3";
	 
   var nCheck = 0;
   var nDigit = 0;
   var bEven  = false;
   
   for (n = strNum.length - 1; n >= 0; n--) 
   {
      var cDigit = strNum.charAt (n);
      if (isDigit (cDigit))
      {
         var nDigit = parseInt(cDigit, 10);
         if (bEven)
         {
            if ((nDigit *= 2) > 9)
               nDigit -= 9;
         }
         nCheck += nDigit;
         bEven = ! bEven;
      }
      else if (cDigit != ' ' && cDigit != '.' && cDigit != '-')
      {
         return false;
      }
   }
   return (nCheck % 10) == 0;
}


//*****************************************************
/// HERE WE DEFINE WHICH FUNCTIONS WE WILL TO CALL
var useLuhnCheck = luhnchk3;
useLuhnCheck.prototype.name = "luhnchk3";

var useCCNumCheck = ccnumchk2;
useCCNumCheck.prototype.name = "ccnumchk2";
//*****************************************************


// Entry Point Function
function isValidCreditCardNumber(ccNumField, ccType)
{
	if ( !isNonNull(ccNumField) )
		return false;
		
	var result = true;
	
 	var ccNum = ccNumField.value;
 	
 	// Need to massage this number, just in case the user
 	// placed spaces or dashes between the numbers.
 	
 	ccNum = ccNum.replace(/\-|\ /g,'');
   
	if (!allDigits(ccNum))
	{
		focusField = ccNumField;
		result = false;
	}	

	if (result)
	{ 			
		// Currently using: LuhnCheck3 AND validateCCNum2
		if (!useLuhnCheck(ccNum) || !useCCNumCheck(ccType, ccNum))
		{
			focusField = ccNumField;
			result = false;
		}	
	} 
	
	return result;
}

// Entry Point Function
function isValidExpDate(ccMonthField, ccYearField)
{	
	var result = true;

	var inputMonth = ccMonthField.options[ccMonthField.selectedIndex];
	var inputYear = ccYearField.options[ccYearField.selectedIndex];	
	
	if ( !isNonNull(inputMonth) || !isNonNull(inputYear) ) 
		return false;
		
		
	var expired = false;
	
	var monthVal = inputMonth.value;
	var yearVal = inputYear.value;
	var month = parseInt(monthVal, 10);
	var year = parseInt(yearVal, 10);

	var now = new Date(); 			
	var nowMonth = now.getMonth() + 1;
	var nowYear = now.getFullYear();

	// Test: Month is digit-only and exists within valid range.
	result = allDigits(monthVal) && (month > 0) && (month < 13);			
	if (!result)
	{
		focusField = ccMonthField;				
	}
	
	if (result)
	{
		// Test: Year is digit-only and has 4 digit form (eg. 2004).
		result = allDigits(yearVal) && (yearVal.length == 4);
		if (!result)
		{
			focusField = ccYearField;				
		} 
		
		if (result)
		{
			// Test: Expiration of CC date against current date.
			expired = (nowYear > year) || ((nowYear == year ) && (nowMonth > month));
			if (expired)
			{
				result = false;
				focusField = ccMonthField;
			}
		}
	}

	return result;
}



///////////////////////////////////////////////////////////////////////
// Perform URL check of the Luhn algorithm (mod 10) and number check
function urlCheckCC(cardType, cardNum)
{		
	cardType = cardType.replace(/\-|\ /g,'');
	cardNum = cardNum.replace(/\-|\ /g,'');
	
	// Our current implementation is LuhnCheck3 AND validateCCNum2
	var luhnCheck = useLuhnCheck(cardNum);
	var valCCNum = useCCNumCheck(cardType, cardNum);

	try {

	alert("Luhn Check: " + luhnCheck + "  Valid CC Number: " + valCCNum
			+ "\n[using: " + useLuhnCheck.prototype.name + " and " + useCCNumCheck.prototype.name + "]");
			
	}catch(err) 
	{
		alert("An error occurred: " + err.message);
	}
	
}



//////////////////////////////////////////////////////////
/// Functions used to validate ABA Check Routing Numbers
/////////////////////////////////////////////////////////


var routeNum1 = function validRoutingNum(abaNum)
{
	// Now run through each digit and calculate the total.
	
	var sum = 0;
	for (var i = 0; i < abaNum.length; i += 3) 
	{
		sum += parseInt(abaNum.charAt(i),     10) * 3
				+  parseInt(abaNum.charAt(i + 1), 10) * 7
				+  parseInt(abaNum.charAt(i + 2), 10) * 1;
	}

	// If the resulting sum is an even multiple of ten (but not zero),
	// the aba routing number is good.
	
  return ( (sum != 0 && sum % 10 == 0) ? true : false );
}


//*****************************************************
/// HERE WE DEFINE WHICH FUNCTIONS WE WILL TO CALL
var useRoutNum = routeNum1;
useRoutNum.prototype.name = "routeNum1";
//*****************************************************


// Entry Point Function
function isValidABARouting(abaRouteField) 
{
	var result = true;

	if ( !isNonNull(abaRouteField) )
	{	
		return false;
	}

  var abaNum = abaRouteField.value;
  
  // Need to massage this number, just in case the user
 	// placed spaces or dashes between the numbers.

  abaNum = abaNum.replace(/\-|\ /g,'');
	
  // Check the length, it should be nine digits.

  if (abaNum.length != 9)
  {
  	focusField = abaRouteField;
    result = false;
  }

	if (result)
	{	
		result = useRoutNum(abaNum);
		if (!result) focusField = abaRouteField;    
  }
  
  return result;
}


//////////////////////////////////////////////////
// Perform URL check on Routing Number
function urlCheckACH(abaNum)
{	
	abaNum = abaNum.replace(/\-|\ /g,'');
	var result = useRoutNum(abaNum);
		
	try {
	
		alert("Routing No. is " + (result?"valid":"invalid")
				+ "\n[using: " + useRoutNum.prototype.name + "]");
				
		}catch(err) 
		{
			alert("An error occurred: " + err.message);
	}
}


///////////////////////////////////////////////////////
// Misc Functions called by both CC and ACH Validation
///////////////////////////////////////////////////////

function isNonNull(formField)
{
	var result = true;
	
	if (formField.value == "")
	{
		focusField = formField;
		result = false;
	}
	
	return result;
}

function allDigits(str)
{
	return inValidCharSet(str, "0123456789");
}

function inValidCharSet(str, charset)
{
	var result = true;
	
	for (var i = 0; i < str.length; i++)
		if (charset.indexOf(str.substr(i,1)) < 0)
		{
			result = false;
			break;
		}
	
	return result;
}

function isDigit(c)
{
   var strAllowed = "1234567890";
   return (strAllowed.indexOf(c) != -1);
}



