Index: theme/pay_cc.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/pay/theme/pay_cc.js,v
retrieving revision 1.1
diff -u -r1.1 pay_cc.js
--- theme/pay_cc.js	29 Apr 2009 02:35:32 -0000	1.1
+++ theme/pay_cc.js	27 Sep 2010 19:46:28 -0000
@@ -4,10 +4,106 @@
  * jQuery functions for the credit card form.
  */
 
-$(document).ready(function() {
-  $('.pay-cc-type input').hide();
-  $('.pay-cc-form').change(function() {
-    $('.pay-cc-type input').siblings('img').css('opacity', '.35');
-    $('.pay-cc-type input:checked').siblings('img').css('opacity','1');
-  }).trigger('change');
-});
+Drupal.behaviors.payCcType = function(context) {
+  $('.pay-cc-form:not(.pay-cc-processed)', context).each(function() {
+    var $payCcForm = $(this).addClass('pay-cc-processed');
+
+    // Hide the radio buttons themselves.
+    $payCcForm.find('.pay-cc-type input').hide();
+
+    // Wrap the labels in links so that they may be given keyboard focus.
+    $payCcForm.find('.pay-cc-type .form-radios img').each(function() {
+      var image = this;
+      $(image).wrap('<a href="#"></a>');
+      $(image).parent().click(function() {
+        $(image).parents('label:first').find('input').click().trigger('change');
+        return false;
+      });
+    });
+
+    // Highlight the selected radio button when clicked.
+    $payCcForm.find('.pay-cc-type input').change(function() {
+      $payCcForm.find('.pay-cc-type img').css('opacity', '.35');
+      $payCcForm.find('.pay-cc-type input:checked').parents('label:first').find('img').css('opacity','1');
+    }).trigger('change');
+
+    // Automatically set the credit card type based on the number.
+    $payCcForm.find('.pay-cc-number input').keyup(function() {
+      var type = Drupal.pay.ccTypeFromNumber(this.value);
+      $payCcForm.find('.pay-cc-type input[value=' + type + ']').click().trigger('change');
+    });
+  });
+}
+
+/**
+ * Make sure the pay object is instantiated.
+ */
+Drupal.pay = Drupal.pay || {};
+
+/**
+ * Utility function to get the credit card type from a number.
+ */
+Drupal.pay.ccTypeFromNumber = function(number) {
+  var type = false;
+
+  // Clean-up the number to be numeric only.
+  number = number.replace(/[^0-9]/, '');
+  if (number.length < 4) {
+    return false;
+  }
+
+  var prefix1 = parseInt(number.substr(0, 1));
+  var prefix2 = parseInt(number.substr(0, 2));
+  var prefix3 = parseInt(number.substr(0, 3));
+  var prefix4 = parseInt(number.substr(0, 4));
+
+ /**
+  * References: http://www.beachnet.com/~hstiles/cardtype.html
+  */
+  switch (prefix1) {
+    case 1:
+      // JCB: prefix 1800, length 15
+      if (prefix4 == 1800) type = 'jcb';
+      break;
+
+    case 2:
+      // JCB: prefix 2131, length 15
+      if (prefix4 == 2131) type = 'jcb';
+
+      // enRoute: prefix 2014, 2149 length 15
+      else if (prefix4 == 2014) type = 'enroute';
+      else if (prefix4 == 2149) type = 'enroute';
+      break;
+
+    case 3:
+      // Diners Club: prefix 300-305, length 14
+      if ($.inArray(prefix3, [300, 301, 302, 303, 304, 305]) != -1) type = 'diners';
+
+      // AmEX: prefix 34, 37, length 15
+      else if ($.inArray(prefix2, [34, 37]) != -1) type = 'amex';
+
+      // Diners Club: prefix 36, 38, length 14
+      else if ($.inArray(prefix2, [36, 38]) != -1) type = 'diners';
+
+      // JCB: prefix 3, length 16
+      else type = 'jcb';
+      break;
+
+    case 4:
+      // Visa: prefix 4, length 13, 16
+      type = 'visa';
+      break;
+
+    case 5:
+      // Mastercard: prefix 51-55, length 16
+      if ($.inArray(prefix2, [51, 52, 53, 54, 55]) != -1) type = 'mc';
+      break;
+
+    case 6:
+      // Discover: prefix 6011, length 16
+      if (prefix4 == 6011) type = 'discover';
+      break;
+  }
+
+  return type;
+}
