Created
January 16, 2014 10:51
-
-
Save kevinblake/8452978 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; (function ($, window, document, undefined) { | |
// Create the defaults once | |
var __indexOf = | |
[].indexOf || function(item) { | |
for (var i = 0, l = this.length; i < l; i++) { | |
if (i in this && this[i] === item) return i; | |
} | |
return -1; | |
}; | |
var pluginName = "creditCard", | |
dataPlugin = "plugin_" + pluginName, | |
defaults = { | |
inputSelector: "input.cardNumber[type=text]", | |
securityCodeClass: "coSecCode", | |
cardTypes: [ | |
{ | |
name: 'amex', | |
pattern: /^3[47]/, | |
valid_length: [15] | |
}, { | |
name: 'diners_club_carte_blanche', | |
pattern: /^30[0-5]/, | |
valid_length: [14] | |
}, { | |
name: 'diners_club_international', | |
pattern: /^36/, | |
valid_length: [14] | |
}, { | |
name: 'jcb', | |
pattern: /^35(2[89]|[3-8][0-9])/, | |
valid_length: [16] | |
}, { | |
name: 'laser', | |
pattern: /^(6304|670[69]|6771)/, | |
valid_length: [16, 17, 18, 19] | |
}, { | |
name: 'visa_electron', | |
pattern: /^(4026|417500|4508|4844|491(3|7))/, | |
valid_length: [16] | |
}, { | |
name: 'visa', | |
pattern: /^4/, | |
valid_length: [16, 13] | |
}, { | |
name: 'master', | |
pattern: /^5[1-5]/, | |
valid_length: [16] | |
}, { | |
name: 'maestro', | |
pattern: /^(5018|5020|5038|6304|6759|676[1-3])/, | |
valid_length: [12, 13, 14, 15, 16, 17, 18, 19] | |
}, { | |
name: 'discover', | |
pattern: /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/, | |
valid_length: [16] | |
} | |
] | |
}; | |
// The actual plugin constructor | |
function Plugin(element, options) { | |
this.element = element; | |
this.options = $.extend({}, defaults, options); | |
this._defaults = defaults; | |
this._name = pluginName; | |
this.securityCodePanel = null; | |
} | |
Plugin.prototype = { | |
init: function () { | |
var my = this; | |
var input = $(this.options.inputSelector, this.element); | |
this.securityCodePanel = $('.' + this.options.securityCodeClass, this.element); | |
input.bind('change keyup', function () { | |
my.validate(); | |
}); | |
if (input.length !== 0) { | |
my.validate(); | |
} else { | |
this.clearIcons(); | |
} | |
}, | |
clearIcons: function () { | |
$(this.element).find('ul.cardIcons li.active').removeClass('active'); | |
}, | |
value: function (val) { | |
var cardNumberInput = $(this.element).find(this.options.inputSelector); | |
if (typeof val == 'undefined') { | |
return cardNumberInput.val(); | |
} else { | |
cardNumberInput.val(val); | |
this.validate(); | |
} | |
return null; | |
}, | |
getCardType: function (number) { | |
var i, _len1, options = this.options; | |
for (i = 0, _len1 = options.cardTypes.length; i < _len1; i++) { | |
var cardType = options.cardTypes[i]; | |
if (number.match(cardType.pattern)) { | |
return cardType; | |
} | |
} | |
return null; | |
}, | |
isValidLuhn: function (number) { | |
var digit, n, sum, _j, _len1, _ref2; | |
sum = 0; | |
_ref2 = number.split('').reverse(); | |
for (n = _j = 0, _len1 = _ref2.length; _j < _len1; n = ++_j) { | |
digit = _ref2[n]; | |
digit = +digit; | |
if (n % 2) { | |
digit *= 2; | |
if (digit < 10) { | |
sum += digit; | |
} else { | |
sum += digit - 9; | |
} | |
} else { | |
sum += digit; | |
} | |
} | |
return sum % 10 === 0; | |
}, | |
isValidLength: function (number, cardType) { | |
var _ref2; | |
return _ref2 = number.length, __indexOf.call(cardType.valid_length, _ref2) >= 0; | |
}, | |
validateNumber: function (number) { | |
var length_valid, luhn_valid; | |
var cardType = this.getCardType(number); | |
luhn_valid = false; | |
length_valid = false; | |
if (cardType != null) { | |
luhn_valid = this.isValidLuhn(number); | |
length_valid = this.isValidLength(number, cardType); | |
} | |
return { | |
card_type: cardType, | |
luhn_valid: luhn_valid, | |
length_valid: length_valid | |
}; | |
}, | |
validate: function () { | |
this.clearIcons(); | |
var number; | |
number = this.normalize(this.value()); | |
var result = this.validateNumber(number); | |
if (result.card_type && result.card_type) { | |
$('ul.cardIcons li.' + result.card_type.name, this.element).addClass('active'); | |
this.securityCodePanel.attr('class', this.options.securityCodeClass + ' ' + result.card_type.name); | |
} else { | |
this.securityCodePanel.attr('class', this.options.securityCodeClass); | |
} | |
return result; | |
}, | |
normalize: function (number) { | |
return number.replace(/[ -]/g, ''); | |
} | |
}; | |
// A really lightweight plugin wrapper around the constructor, | |
// preventing against multiple instantiations | |
$.fn[pluginName] = function (arg) { | |
var args, instance; | |
// only allow the plugin to be instantiated once | |
if (!(this.data(dataPlugin) instanceof Plugin)) { | |
var plugin = new Plugin(this); | |
this.data(dataPlugin, plugin); | |
} | |
instance = this.data(dataPlugin); | |
instance.element = this; | |
// Is the first parameter an object (arg), or was omitted, | |
// call Plugin.init( arg ) | |
if (typeof arg === 'undefined' || typeof arg === 'object') { | |
if (typeof instance['init'] === 'function') { | |
instance.init(arg); | |
} | |
return instance; | |
// checks that the requested public method exists | |
} else if (typeof arg === 'string' && typeof instance[arg] === 'function') { | |
// copy arguments & remove function name | |
args = Array.prototype.slice.call(arguments, 1); | |
return instance[arg].apply(instance, args); | |
} else { | |
$.error('Method ' + arg + ' does not exist on jQuery.' + pluginName); | |
} | |
return null; | |
}; | |
})(jQuery, window, document); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment