Skip to content

Instantly share code, notes, and snippets.

@kevinblake
Last active December 28, 2015 18:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinblake/7543858 to your computer and use it in GitHub Desktop.
Save kevinblake/7543858 to your computer and use it in GitHub Desktop.
; (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: "#CardNumber",
securityCodeClass: "coSecCode",
cardTypes: [
{
name: 'american',
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: 'mastercard',
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]
},
{
name: 'solo',
pattern: /^(6334|6767|4903|4905|4911|4936|564182|633110|6333|6759)/,
valid_length: [16, 18, 19]
}
]
};
// 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.securityCodePanel = $('.' + this.options.securityCodeClass);
input.bind('change keyup', function () {
my.validate();
});
if (input.length !== 0) {
my.validate();
} else {
this.clearIcons();
}
},
clearIcons: function () {
$('ul.credit_cards li.chosen').removeClass('chosen');
},
value: function (val) {
var cardNumberInput = $(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.credit_cards li.' + result.card_type.name).addClass('chosen');
if (this.securityCodePanel && this.securityCodePanel != undefined) {
this.securityCodePanel.attr('class', this.options.securityCodeClass + ' ' + result.card_type.name);
}
} else {
if (this.securityCodePanel && this.securityCodePanel != undefined) {
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