|
var Card = function (value) { |
|
value = String(value).replace(/[^0-9]/g, ''); |
|
|
|
var system = { |
|
type: function () { |
|
var data = false; |
|
|
|
for (var company in Card.iin) { |
|
if (value.match(Card.iin[company].identifier)) { |
|
data = { |
|
company: company, |
|
mii: Card.mii[+value.charAt(0)], |
|
iin: Card.iin[company] |
|
}; |
|
|
|
break; |
|
} |
|
} |
|
|
|
return data; |
|
}, |
|
|
|
valid: function () { |
|
var type = system.type(); |
|
|
|
return ( |
|
type && type.inactive != true && |
|
type.iin.lengths.indexOf(value.length) != -1 && |
|
Card.luhn(value) |
|
); |
|
}, |
|
|
|
validate_security_number: function (csc, type) { |
|
var type = system.type(); |
|
csc = String(csc).replace(/[^0-9]/g, ''); |
|
|
|
return ( |
|
type && |
|
(type.iin.csc || 3) == csc.length |
|
); |
|
}, |
|
|
|
validate_expiration: function (month, year) { |
|
month = +month; year = +year; |
|
|
|
if (!(month || year)) |
|
return false; |
|
|
|
var date = new Date() |
|
, now = +date; |
|
|
|
date.setFullYear(year); |
|
date.setMonth(--month); |
|
|
|
return +date >= now; |
|
}, |
|
|
|
format: function () { |
|
var index = 0 |
|
, pattern = Card.iin["American Express"].prefix.test(value) |
|
? 'XXXX XXXXXX XXXXX' |
|
: 'XXXX XXXX XXXX XXXX'; |
|
|
|
return pattern.replace(/X/g, function (char) { |
|
return value.charAt(index++) || ''; |
|
}).trim(); |
|
}, |
|
|
|
truncate: function () { |
|
var length = value.length - 4 |
|
, pattern = system.format(value); |
|
|
|
return pattern.replace(/\d/g, function (char) { |
|
return length-- > 0 |
|
? 'X' |
|
: char; |
|
}); |
|
}, |
|
|
|
clean: function () { |
|
return value; |
|
}, |
|
|
|
length: function () { |
|
return value.length; |
|
} |
|
}; |
|
|
|
return system; |
|
}; |
|
|
|
Card.luhn = function (value) { |
|
var ca, sum = 0, mul = 0; |
|
var len = value.length; |
|
|
|
while (len--) { |
|
ca = parseInt(value.charAt(len),10) << mul; |
|
sum += ca - (ca>9)*9; |
|
mul ^= 1; |
|
}; |
|
|
|
return (sum%10 === 0) && (sum > 0); |
|
}; |
|
|
|
/** |
|
* Per-Company IIN Details ([Wikipedia](https://en.wikipedia.org/wiki/Bank_card_number#Issuer_identification_number_.28IIN.29)) |
|
* @Type {Object} |
|
*/ |
|
Card.iin = { |
|
// Inactive Companies |
|
"Australian Bank Card": { inactive: true, identifier: /^(5610|560221|560222|560223|560224|560225)/, lengths: [16] }, |
|
"Diner's Club enRoute": { inactive: true, identifier: /^(2014|2149)/, lengths: [15] }, |
|
"Solo": { inactive: true, identifier: /^(6334|6767)/, lengths: [16] }, |
|
"Switch": { inactive: true, identifier: /^(4903|4905|4911|4936|564182|633110|6333|6759)/, lengths: [16,18,19] }, |
|
|
|
// Active Companies |
|
"American Express": { identifier: /^(34|37)/, lengths: [13, 15], csc: 4 }, |
|
"MasterCard": { identifier: /^5[0-5]/, lengths: [16] }, |
|
"Visa Electron": { identifier: /^(4026|417500|4508|4844|491(3|7))/, lengths: [16] }, |
|
"Visa": { identifier: /^4/, lengths: [13, 16] }, |
|
"Laser": { identifier: /^(6334|6767)/, lengths: [16,17,18,19] }, |
|
"Discover": { identifier: /^6(?:011|22[1-9]|4[4-9]|5)/, lengths: [16] }, |
|
"Diner's Club": { identifier: /^(36|30[0-5]|54|55|62|88)/, lengths: [14, 16] }, |
|
"JCB": { identifier: /^35(2[89]|[3-8][0-9])/, lengths: [16] }, |
|
"Maestro": { identifier: /^(5018|5020|5038|6304|6759|676[1-3])/, lengths: [12, 13, 14, 15, 16, 18, 19] } |
|
}; |
|
|
|
/** |
|
* Major Industry Identifier ([Wikipedia](https://en.wikipedia.org/wiki/Bank_card_number#Major_Industry_Identifier_.28MII.29)) |
|
* @type {Array} |
|
*/ |
|
Card.mii = [ |
|
"ISO/TC 68 and other future industry assignments", |
|
"Airlines", |
|
"Airlines and other future industry assignments", |
|
"Travel and entertainment and banking/financial", |
|
"Banking and financial", |
|
"Banking and financial", |
|
"Merchandising and banking/financial", |
|
"Petroleum and other future industry assignments", |
|
"Healthcare, telecommunications and other future industry assignments", |
|
"National assignment" |
|
]; |