//=====================================================================||
//                  CreditCard Validation Object                       
//                                                                     
//                                                                     
//                                                                     
// Javascript CreditCard Validation, 1.21v
//   * Changed the default form field binds to:
//        cc_number
//        cc_type
//        cc_exp_month
//        cc_exp_year
//        cc_name <- New Addition
//   * Added support for the credit card name
//
// JavaScript CreditCard Validation, 1.11v
//   * Fixed bug where if the month was a "08" or "09" it would return
//     a null month.  This was the result a strange parseInt bug.
//     parseInt will return the correct number for "01" -> "07" but it
//     will return 0 for numbers "08" and "09".  Go figure
//
// JavaScript CreditCard Validation, 1.1v                              
//   * Extended set methods to set fields regardless of input type
//
// JavaScript CreditCard Validation, 1.0v                              
// --------------------------------------------------------------------||
//  Constructors
//  ============
//          The credit card object is either bound or virtual.  Bound is
//     the term used to describe the object attaching itself to the form 
//     fields required to perform validation.  Vitual is when the values
//     of the fields are set directly and are not gathered from the form
//     directly.  There are two constructors. Calling the constructor 
//     with no parameters results in a virtual object while calling it
//     with a single parameter will result in a bound object.  The single
//     paramater is the reference to the form object.  By default it will
//     attempt to bind itself to fields in the form with id's:
//           card_number
//           card_type
//           card_exp_month
//           card_exp_year
//     If these are not availble by the form you will have to set the
//     form field references by calling the bind methods with the form
//     field object refrences.  You may bind an object at any time by
//     calling any bind method.  It is sink or swim so either all fields
//     are virtual or all fields are bound. 
//
//        new CreditCard()  // Virtual Object
//        new CreditCard(bindingFormReference)  // Bound Object
//
//  Methods
//  =======
//        Binding Methods will bind an reference to a form field to the object.
//     By binding one field all fields will then need to be bound.  If the form
//     has not been bound to the object then the full reference must be givin to 
//     the bind methods.  If the form has been bound to the object then only
//     the form field id name is needed to bind but the full reference may be givin.
//
//        bindForm(form_object_reference)
//        bindCardNumber(form_field_object_reference)
//        bindCardType(form_field_object_reference)
//        bindCardExpYear(form_field_object_reference)
//        bindCardExpMonth(form_field_object_reference)
//
//        ex 1:  Form will be bound to the object
//               cc.bindForm(document.form[0])
//               cc.bindCardNumber("cardNumber")
//                         - OR -
//               cc.bindCardNumber(document.form[0].cardNumber)
//
//        ex 2: Form will not be bound to the object
//               cc.bindCardNumber(document.form[0].cardNumber)
//
//        setCardNumber(value)
//        setCardType(value)
//        setCardExpYear(value)
//        setCardExpMonth(value)
//        getCardNumber()
//        getCardType()
//        getCardExpYear()
//        getCardExpMonth()
//   
//        getFormattedCardExpYear()
//        getCardExpMonthWord()
//        getCardExpMonthNumber()
//
//        isCardExpYearValid()
//        isCardExpMonthValid()
//        isCardDateValid()
//        isCardTypeValid()
//        isCardNumberValid()
//        isCardValid()
//=====================================================================||

function CreditCard(name){
	this.alerts = false
	this.form = CreditCard.arguments > 0 ? CreditCard.arguments[0] : null
	this.isBound = CreditCard.arguments > 0 ? true : false
	this.name = name ? name : "credit card"
	this.require = new Object()
	this.require.cardName = false
	this.require.cardNumber = true
	this.require.cardType = true
	this.require.cardExpMonth = true
	this.require.cardExpYear = true	
	
	this.fields = new Object()
	this.fields.cardType = new Object()
	this.fields.cardNumber = new Object()
	this.fields.cardExpYear = new Object()
	this.fields.cardExpMonth = new Object()
	this.fields.cardName = new Object()

	function bindForm2(form){
	   if(form == null) return
	   this.form = form
	   this.isBound = true
	   if(this.form.card_number)
	      this.bindCardNumber("card_number")
	   if(this.form.card_exp_year)
	      this.bindCardExpYear("card_exp_year")
	   if(this.form.card_exp_month)
	      this.bindCardExpMonth("card_exp_month")
	   if(this.form.card_type)
	      this.bindCardType("card_type")
	}
	function bindForm(form){
	   if(form == null) return
	   this.form = form
	   this.isBound = true
	   if(this.form.cc_number)
	      this.bindCardNumber("cc_number")
	   else
	   	  return false
	   if(this.form.cc_exp_year)
	      this.bindCardExpYear("cc_exp_year")
	   else
	   	  return false
	   if(this.form.cc_exp_month)
	      this.bindCardExpMonth("cc_exp_month")
	   else
	   	  return false
	   if(this.form.cc_type)
	      this.bindCardType("cc_type")
	   else
	   	 return false
	   if(this.form.cc_name)
	     this.bindCardName("cc_name")
	   else
	   	  return false
		return true
	}	
	function getBoundObject(objRef){
       return this.form == null
	       ? objRef
		   : eval("objRef.type") 
		      ? objRef
			  : eval("this.form." + objRef)	
	}
	function bindCardName(objRef){
		this.isBound = true
		this.fields.cardName = this.getBoundObject(objRef)
	}
	function bindCardNumber(objRef){
	   this.isBound = true
	   this.fields.cardNumber = this.getBoundObject(objRef)
	}
	function bindCardType(objRef){
	   this.isBound = true
	   this.fields.cardType = this.getBoundObject(objRef)	
	}
	function bindCardExpMonth(objRef){
	   this.isBound = true
	   this.fields.cardExpMonth = this.getBoundObject(objRef)	
	}
	function bindCardExpYear(objRef){
	   this.isBound = true
	   this.fields.cardExpYear = this.getBoundObject(objRef)	
	}
    function setAlerts(bool){
	   this.alerts = bool
	}
    function error(message, obj){
	   if(this.alerts){
	      alert(message)
		  if(obj != null)
		     obj.focus()
	   }
	}
//---------------------------------------------------------------------||
// FUNCTIONS:  Utility function to set and get form values             ||
// DESC: These function are for internal use ONLY and are used to      ||
//       retrieve and set form field values.  These functions are      ||
//       cross browser enabled.                                        ||
//---------------------------------------------------------------------||
	function getSelectionFieldValue(obj, bound){
	    return bound == 0 
		       ? obj.value
			   : obj[obj.selectedIndex].value
	}
	function getFormFieldValue(obj){
	    return obj.type == "select-one"
            ? getSelectionFieldValue(obj)
		    : obj.value
	}
	function setSelectionFieldValue(obj, value, bound){
	    if(bound == 0)
		    obj.value = value
		else 
		    obj[obj.selectedIndex].value = value
	}
	function setFormFieldValue(field, value, bound){
	    if(field.type == "select-one"){
		   setSelectionFieldValue(field, bound)
		} else {
		   field.value = value
		}
	}
//---------------------------------------------------------------------||
// FUNCTION:  getCardName                                              ||
// PARAM:                                                              ||
// RETURN: The value in binded cardName field                          ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
   function setCardName(value){
      return setFormFieldValue(this.fields.cardName, value, this.isBound)
   }
//---------------------------------------------------------------------||
// FUNCTION:  getCardType                                              ||
// PARAM:                                                              ||
// RETURN: The value in binded cardType field                          ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
   function setCardType(value){
      return setFormFieldValue(this.fields.cardType, value, this.isBound)
   }
//---------------------------------------------------------------------||
// FUNCTION:  getCardNumber                                            ||
// PARAM:                                                              ||
// RETURN: The value in binded cardNumberr field                       ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
   function setCardNumber(value){
      return setFormFieldValue(this.fields.cardNumber, value,  this.isBound)
   }
//---------------------------------------------------------------------||
// FUNCTION:  getCardExpYear                                           ||
// PARAM:                                                              ||
// RETURN: The value in binded cardExpYear field                       ||
// DESC:                                                               ||
//---------------------------------------------------------------------||		
	function setCardExpYear(value){
	    return setFormFieldValue(this.fields.cardExpYear, value, this.isBound)
	}
//---------------------------------------------------------------------||
// FUNCTION:  getCardExpMonth                                          ||
// PARAM:                                                              ||
// RETURN: The value in binded cardExpMonth field                      ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
	function setCardExpMonth(value){
	    return setFormFieldValue(this.fields.cardExpMonth, value, this.isBound)
	}
//---------------------------------------------------------------------||
// FUNCTION:  getCardName                                              ||
// PARAM:                                                              ||
// RETURN: The value in binded cardType field                          ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
   function getCardName(){
      return getFormFieldValue(this.fields.cardName)
   }
//---------------------------------------------------------------------||
// FUNCTION:  getCardType                                              ||
// PARAM:                                                              ||
// RETURN: The value in binded cardType field                          ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
   function getCardType(){
      return getFormFieldValue(this.fields.cardType)
   }
//---------------------------------------------------------------------||
// FUNCTION:  getCardNumber                                            ||
// PARAM:                                                              ||
// RETURN: The value in binded cardNumberr field                       ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
   function getCardNumber(){
      return getFormFieldValue(this.fields.cardNumber)
   }
//---------------------------------------------------------------------||
// FUNCTION:  getCardExpYear                                           ||
// PARAM:                                                              ||
// RETURN: The value in binded cardExpYear field                       ||
// DESC:                                                               ||
//---------------------------------------------------------------------||		
	function getCardExpYear(){
	    return getFormFieldValue(this.fields.cardExpYear)
	}
//---------------------------------------------------------------------||
// FUNCTION:  getFormattedCardExpYear                                  ||
// PARAM:                                                              ||
// RETURN: The numeric value of the binded cardExpYear in yyyy format  ||
// DESC:  Returns the yyyy format of the year value.  If the value is  ||
//        is not either of length 2 or 4, "" is returned               ||
//---------------------------------------------------------------------||
	function getFormattedCardExpYear(){
		if(this.getCardExpYear().length == 2){
		   return parseInt("20" + this.getCardExpYear())
		} else if(this.getCardExpYear().length == 4){
		   return parseInt(this.getCardExpYear())
		} else {
		   return ""
		}	
	}
//---------------------------------------------------------------------||
// FUNCTION:  getCardExpMonth                                          ||
// PARAM:                                                              ||
// RETURN: The value in binded cardExpMonth field                      ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
	function getCardExpMonth(){
	    return getFormFieldValue(this.fields.cardExpMonth)
	}
//---------------------------------------------------------------------||
// FUNCTION:  getCardExpMonthNumber                                    ||
// PARAM:                                                              ||
// RETURN: The numeric value of the binded cardExpMonth                ||
//         1 (january) .. 12 (december)                                ||
// DESC:  Returns the numberic month value.  If the value is null or   ||
//        or "" it will return the value.  If the value is a month     ||
//        in word format it will convert it to a numeric represtation  ||
//        if the value numeric value is not found a 0 will be returned ||
//---------------------------------------------------------------------||
	function getCardExpMonthNumber(){
	    var month = this.getCardExpMonthWord()
		if(month == null || month == "")
		   return month

		if(parseInt(month) >= 0)
		   return parseInt(month)
     
		month = month != null ? month.toLowerCase() : ""

		for(i in CreditCard.months){
		   if(CreditCard.months[i] == month
		     || CreditCard.abrMonths[i] == month)
		      return parseInt(i) + 1
		}
		return 0
	}
//---------------------------------------------------------------------||
// FUNCTION:  getCardExpMonthWord                                      ||
// PARAM:                                                              ||
// RETURN: The word representation of the binded cardExpMonth value    ||
//         january (1) ..  december (12)                               ||
// DESC:  Returns the word representation of the month value.  If the  ||
//        value is null or "" returns the value.  If the value is      ||
//        either a non represenitive numberic month or incorrect word  ||
//        month then "" is returned                                    ||
//---------------------------------------------------------------------||
	function getCardExpMonthWord(){
	    var month = this.getCardExpMonth()
		if(month == null || month == "")
		   return month
		 
		if(month.length > 1 && parseInt(month.substring(0, 1)) == 0) 
			month = month.substring(1)

		if(parseInt(month) > 0)
		   return CreditCard.months[month-1]

        month = month.toLowerCase()
		for(i in CreditCard.months){
		   if(CreditCard.months[i] == month 
		      || CreditCard.abrMonths[i] == month)
		      return CreditCard.months[i].substring(0, 1).toUpperCase() +
			         CreditCard.months[i].substring(1)
		}
		return ""		
	}

    function isAttempted(){
		if(this.getCardExpMonth()  
		  || this.getCardNumber()
		  || this.getCardType()
		){
			return true
		}
		return false
	}
//---------------------------------------------------------------------||
// FUNCTION:  isCardExpYearValid                                       ||
// PARAM:                                                              ||
// RETURN: Bool - true if the experation year is valid                 ||
//              - false if the experation is expired or not valid      ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
	function isCardExpYearValid(){
	    var expYear = this.getFormattedCardExpYear()
		// Check is its null
		if((expYear == null) || (expYear == "")) {
		   this.error("Please enter the Expiration Year", this.fields.cardExpYear)
		   return false
		}
		
        // Check to see if its only numbers
		if(!(expYear >= 0)){
		   this.error("The credit card expiration year is not valid", this.fields.cardExpYear)
		   return false
		}		

		var today = new Date()
		var currentYear = today.getYear() < 200 
		                  ? today.getYear() + 1900 
						  : today.getYear()
						  
		if(expYear < currentYear){
		   this.error(this.name + " has expired",  this.fields.cardExpYear)
		   return false
		}

		return true
	}
	
//---------------------------------------------------------------------||
// FUNCTION:  isCardExpMonthValid                                      ||
// PARAM:                                                              ||
// RETURN: Bool - true if the experation month is valid                ||
//              - false if the experation is not valid                 ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
	function isCardExpMonthValid(){
		// Check is its null
		var expMonth = this.getCardExpMonthNumber()
	
		if((expMonth == null) || (expMonth == "")) {
				msg = "Please enter " + this.name + "'s expiration month"
		   this.error(msg, this.fields.cardExpMonth)
		   return false
		}
		if(expMonth >= 0){	
			if((parseInt(expMonth) > 12) ||(parseInt(expMonth) < 1) ){
			   this.error(this.name + "'s expiration month is not a valid month.", this.fields.cardExpMonth)
			   return false
			}
		}
		return true	
	}
//---------------------------------------------------------------------||
// FUNCTION:  isCardExpDateValid                                       ||
// PARAM:                                                              ||
// RETURN: Bool - true if the experation date is valid                 ||
//              - false if the experation date is expired or not valid ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
	function isCardExpDateValid(){			
		if(!this.isCardExpMonthValid())  return false
		if(!this.isCardExpYearValid())   return false
		
		var today = new Date()
		var ccExpDate = new Date(this.getFormattedCardExpYear(), this.getCardExpMonthNumber()-1, 1)

		if(today.getTime() > ccExpDate.getTime()){
		   this.error(this.name + " has expired", this.fields.cardExpMonth)  
		   return false
		}
		return true
	}
//---------------------------------------------------------------------||
// FUNCTION:  isCardTypeValid                                          ||
// PARAM:                                                              ||
// RETURN: Bool - true if the card type is valid                       ||
//              - false otherwise                                      ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
    function isCardNameValid(){
		if ( this.require.cardName == false )
			return true
	   var cardName = this.getCardName()
	   if(cardName == null || cardName == ""){
	      this.error("Please enter a credit card name", this.fields.cardName)
		  return false
	   }	  
	   
	   return true
	}
//---------------------------------------------------------------------||
// FUNCTION:  isCardTypeValid                                          ||
// PARAM:                                                              ||
// RETURN: Bool - true if the card type is valid                       ||
//              - false otherwise                                      ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
    function isCardTypeValid(){
	   var cardType = this.getCardType()
	   if(cardType == null || cardType == "" || cardType == 0){
	      this.error("Please enter " + this.name + "'s card type", this.fields.cardType)
		  return false
	   }	  
	   cardType = CreditCard.getCardType(cardType)

	   if(cardType == null){
	      this.error("The credit card type is not an accepted card type")
	      return false
	   }
	   
	   return true
	}
//---------------------------------------------------------------------||
// FUNCTION:  isCardNumberValid                                        ||
// PARAM:                                                              ||
// RETURN: Bool - true if the card number passes the luhn algo. and    ||
//                the card number is a valid to its card type          ||
//              - false othwise                                        ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
	function isCardNumberValid(){		
        var cardNum = this.getCardNumber()
		var cardType
	
		if(!this.isCardTypeValid()){
		   return false
		}
		if(cardNum == "" || cardNum == null){
		   this.error("Please enter " + this.name + "'s card number", this.fields.cardNumber)
		   return false
		}
		if (!this.isLuhnable()){
		   this.error("Card number is not a valid " + this.getCardType() + " format", this.fields.cardNumber)
		   return false;
		}
        cardType = CreditCard.getCardType(this.getCardType())
		if(!cardType.isCardNumberValid(cardNum)){
		   this.error(this.name + " is not a valid " + cardType.cardType + " format", this.fields.cardNumber)
		   return false
		}
		return true;		
	}
//---------------------------------------------------------------------||
// FUNCTION:  isCardValid                                              ||
// PARAM:                                                              ||
// RETURN: Bool - true if the card number passes the luhn algo. and    ||
//                the card number is a valid to its card type          ||
//              - false othwise                                        ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
    function isCardValid(){
	  if(this.isCardNameValid())
	     if(this.isCardNumberValid())
		   if(this.isCardExpDateValid())
		       return true
	   return false
	}
//---------------------------------------------------------------------||
// FUNCTION:  isLuhnable                                               ||
// PARAM:                                                              ||
// RETURN: Bool - true if the card number passes the luhn algo.        ||
//              - false otherwise                                      ||
// DESC:                                                               ||
//---------------------------------------------------------------------||
	function isLuhnable() {
		var argv = isLuhnable.arguments
		var argc = isLuhnable.arguments.length
		
		var CardNumber = argc > 0 ? argv[0] : this.getCardNumber()

		if (!(parseInt(CardNumber) > 0)) {
		   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
		}
		
		return sum % 10 == 0 
		      ? true
		      : false
	}
	
		
	this.bindForm = bindForm
	this.bindCardName = bindCardName
	this.bindCardType = bindCardType
	this.bindCardNumber = bindCardNumber
	this.bindCardExpYear = bindCardExpYear
	this.bindCardExpMonth = bindCardExpMonth
	this.setAlerts = setAlerts
	
	this.isCardNameValid = isCardNameValid
	this.isCardNumberValid = isCardNumberValid
	this.isCardExpYearValid = isCardExpYearValid
	this.isCardExpMonthValid = isCardExpMonthValid
	this.isCardExpDateValid = isCardExpDateValid
	this.isCardTypeValid = isCardTypeValid
	this.isCardValid = isCardValid
	this.isLuhnable = isLuhnable
	this.isAttempted = isAttempted
		
	this.getCardName = getCardName
	this.getCardNumber = getCardNumber
	this.getCardType = getCardType
	this.getCardExpYear = getCardExpYear
	this.getCardExpMonth = getCardExpMonth
	this.getCardExpMonthNumber = getCardExpMonthNumber
	this.getCardExpMonthWord = getCardExpMonthWord
	this.getFormattedCardExpYear = getFormattedCardExpYear
	this.getBoundObject = getBoundObject
	this.error = error
}

CreditCard.months = [ "january",  "febuary",  "march",  
                      "april", "may", "june",
			          "july", "august", "september", 
					  "october", "november", "december"
				    ]
CreditCard.abrMonths = [ "jan", "feb", "mar", 
					     "apr", "may", "jun",
			             "jul", "aug", "sept", 
					     "oct", "nov", "dec"
					   ]

CreditCard.cards = new Array()
    CreditCard.cards.push( new CardType("MasterCard", "51,52,53,54,55", "16") )
    CreditCard.cards.push( new CardType("VISA", "4", "13,16,19") )
	CreditCard.cards.push( new CardType("American Express", "34,37", "15") )
	CreditCard.cards.push( new CardType("Diners Club", "30,36,38", "14") )
	CreditCard.cards.push( new CardType("Discover", "6011", "16") )
	CreditCard.cards.push( new CardType("enRoute", "2014,2149", "15") )
	CreditCard.cards.push( new CardType("JCB", "3088,3096,3112,3158,3337,3528", "16") )

CreditCard.getCardType = function(cardType){
	cardType = new String(cardType)
	cardType = cardType.toLowerCase()
   for(i in CreditCard.cards){
      if(cardType == CreditCard.cards[i].cardType.toLowerCase())
	     return CreditCard.cards[i]
      }
   return null
} 


function CardType(){
	var n
	var argv = CardType.arguments
	var argc = CardType.arguments.length

	this.objname = "object CardType"
	this.cardType = (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.setLen = setLen
	this.setRules = setRules
    this.isCardNumberValid = isCardNumberValid

	this.setLen(tmplen)
	this.setRules(tmprules)

	return this
   
	function setLen(len) {
		if (len.length == 0 || len == null)
			len = "13,14,15,16,19"

		this.len = len.split(",")
	}
	function setRules(rules) {
		if (rules.length == 0 || rules == null)
			rules = "0,1,2,3,4,5,6,7,8,9"
		
		this.rules = rules.split(",")
	}
	function isCardNumberValid(cardNum){
		for (var n = 0; n < this.len.length; n++){
		   if (cardNum.toString().length == this.len[n]) {
		      for (var m = 0; m < this.rules.length; m++) {
		         var headdigit = cardNum.substring(0, this.rules[m].toString().length);
		         if (headdigit == this.rules[m])
		            return true;
		      }
		      return false;
		   }
	   }
	}
}