jquery.payment.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. // Generated by CoffeeScript 1.7.1
  2. (function() {
  3. var $, cardFromNumber, cardFromType, cards, defaultFormat, formatBackCardNumber, formatBackExpiry, formatCardNumber, formatExpiry, formatForwardExpiry, formatForwardSlashAndSpace, hasTextSelected, luhnCheck, reFormatCVC, reFormatCardNumber, reFormatExpiry, reFormatNumeric, replaceFullWidthChars, restrictCVC, restrictCardNumber, restrictExpiry, restrictNumeric, safeVal, setCardType,
  4. __slice = [].slice,
  5. __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; };
  6. $ = window.jQuery || window.Zepto || window.$;
  7. $.payment = {};
  8. $.payment.fn = {};
  9. $.fn.payment = function() {
  10. var args, method;
  11. method = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
  12. return $.payment.fn[method].apply(this, args);
  13. };
  14. defaultFormat = /(\d{1,4})/g;
  15. $.payment.cards = cards = [
  16. {
  17. type: 'maestro',
  18. patterns: [5018, 502, 503, 506, 56, 58, 639, 6220, 67],
  19. format: defaultFormat,
  20. length: [12, 13, 14, 15, 16, 17, 18, 19],
  21. cvcLength: [3],
  22. luhn: true
  23. }, {
  24. type: 'forbrugsforeningen',
  25. patterns: [600],
  26. format: defaultFormat,
  27. length: [16],
  28. cvcLength: [3],
  29. luhn: true
  30. }, {
  31. type: 'dankort',
  32. patterns: [5019],
  33. format: defaultFormat,
  34. length: [16],
  35. cvcLength: [3],
  36. luhn: true
  37. }, {
  38. type: 'visa',
  39. patterns: [4],
  40. format: defaultFormat,
  41. length: [13, 16],
  42. cvcLength: [3],
  43. luhn: true
  44. }, {
  45. type: 'mastercard',
  46. patterns: [51, 52, 53, 54, 55, 22, 23, 24, 25, 26, 27],
  47. format: defaultFormat,
  48. length: [16],
  49. cvcLength: [3],
  50. luhn: true
  51. }, {
  52. type: 'amex',
  53. patterns: [34, 37],
  54. format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/,
  55. length: [15],
  56. cvcLength: [3, 4],
  57. luhn: true
  58. }, {
  59. type: 'dinersclub',
  60. patterns: [30, 36, 38, 39],
  61. format: /(\d{1,4})(\d{1,6})?(\d{1,4})?/,
  62. length: [14],
  63. cvcLength: [3],
  64. luhn: true
  65. }, {
  66. type: 'discover',
  67. patterns: [60, 64, 65, 622],
  68. format: defaultFormat,
  69. length: [16],
  70. cvcLength: [3],
  71. luhn: true
  72. }, {
  73. type: 'unionpay',
  74. patterns: [62, 88],
  75. format: defaultFormat,
  76. length: [16, 17, 18, 19],
  77. cvcLength: [3],
  78. luhn: false
  79. }, {
  80. type: 'jcb',
  81. patterns: [35],
  82. format: defaultFormat,
  83. length: [16],
  84. cvcLength: [3],
  85. luhn: true
  86. }
  87. ];
  88. cardFromNumber = function(num) {
  89. var card, p, pattern, _i, _j, _len, _len1, _ref;
  90. num = (num + '').replace(/\D/g, '');
  91. for (_i = 0, _len = cards.length; _i < _len; _i++) {
  92. card = cards[_i];
  93. _ref = card.patterns;
  94. for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
  95. pattern = _ref[_j];
  96. p = pattern + '';
  97. if (num.substr(0, p.length) === p) {
  98. return card;
  99. }
  100. }
  101. }
  102. };
  103. cardFromType = function(type) {
  104. var card, _i, _len;
  105. for (_i = 0, _len = cards.length; _i < _len; _i++) {
  106. card = cards[_i];
  107. if (card.type === type) {
  108. return card;
  109. }
  110. }
  111. };
  112. luhnCheck = function(num) {
  113. var digit, digits, odd, sum, _i, _len;
  114. odd = true;
  115. sum = 0;
  116. digits = (num + '').split('').reverse();
  117. for (_i = 0, _len = digits.length; _i < _len; _i++) {
  118. digit = digits[_i];
  119. digit = parseInt(digit, 10);
  120. if ((odd = !odd)) {
  121. digit *= 2;
  122. }
  123. if (digit > 9) {
  124. digit -= 9;
  125. }
  126. sum += digit;
  127. }
  128. return sum % 10 === 0;
  129. };
  130. hasTextSelected = function($target) {
  131. var _ref;
  132. if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== $target.prop('selectionEnd')) {
  133. return true;
  134. }
  135. if ((typeof document !== "undefined" && document !== null ? (_ref = document.selection) != null ? _ref.createRange : void 0 : void 0) != null) {
  136. if (document.selection.createRange().text) {
  137. return true;
  138. }
  139. }
  140. return false;
  141. };
  142. safeVal = function(value, $target) {
  143. var currPair, cursor, digit, error, last, prevPair;
  144. try {
  145. cursor = $target.prop('selectionStart');
  146. } catch (_error) {
  147. error = _error;
  148. cursor = null;
  149. }
  150. last = $target.val();
  151. $target.val(value);
  152. if (cursor !== null && $target.is(":focus")) {
  153. if (cursor === last.length) {
  154. cursor = value.length;
  155. }
  156. if (last !== value) {
  157. prevPair = last.slice(cursor - 1, +cursor + 1 || 9e9);
  158. currPair = value.slice(cursor - 1, +cursor + 1 || 9e9);
  159. digit = value[cursor];
  160. if (/\d/.test(digit) && prevPair === ("" + digit + " ") && currPair === (" " + digit)) {
  161. cursor = cursor + 1;
  162. }
  163. }
  164. $target.prop('selectionStart', cursor);
  165. return $target.prop('selectionEnd', cursor);
  166. }
  167. };
  168. replaceFullWidthChars = function(str) {
  169. var chars, chr, fullWidth, halfWidth, idx, value, _i, _len;
  170. if (str == null) {
  171. str = '';
  172. }
  173. fullWidth = '\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19';
  174. halfWidth = '0123456789';
  175. value = '';
  176. chars = str.split('');
  177. for (_i = 0, _len = chars.length; _i < _len; _i++) {
  178. chr = chars[_i];
  179. idx = fullWidth.indexOf(chr);
  180. if (idx > -1) {
  181. chr = halfWidth[idx];
  182. }
  183. value += chr;
  184. }
  185. return value;
  186. };
  187. reFormatNumeric = function(e) {
  188. var $target;
  189. $target = $(e.currentTarget);
  190. return setTimeout(function() {
  191. var value;
  192. value = $target.val();
  193. value = replaceFullWidthChars(value);
  194. value = value.replace(/\D/g, '');
  195. return safeVal(value, $target);
  196. });
  197. };
  198. reFormatCardNumber = function(e) {
  199. var $target;
  200. $target = $(e.currentTarget);
  201. return setTimeout(function() {
  202. var value;
  203. value = $target.val();
  204. value = replaceFullWidthChars(value);
  205. value = $.payment.formatCardNumber(value);
  206. return safeVal(value, $target);
  207. });
  208. };
  209. formatCardNumber = function(e) {
  210. var $target, card, digit, length, re, upperLength, value;
  211. digit = String.fromCharCode(e.which);
  212. if (!/^\d+$/.test(digit)) {
  213. return;
  214. }
  215. $target = $(e.currentTarget);
  216. value = $target.val();
  217. card = cardFromNumber(value + digit);
  218. length = (value.replace(/\D/g, '') + digit).length;
  219. upperLength = 16;
  220. if (card) {
  221. upperLength = card.length[card.length.length - 1];
  222. }
  223. if (length >= upperLength) {
  224. return;
  225. }
  226. if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {
  227. return;
  228. }
  229. if (card && card.type === 'amex') {
  230. re = /^(\d{4}|\d{4}\s\d{6})$/;
  231. } else {
  232. re = /(?:^|\s)(\d{4})$/;
  233. }
  234. if (re.test(value)) {
  235. e.preventDefault();
  236. return setTimeout(function() {
  237. return $target.val(value + ' ' + digit);
  238. });
  239. } else if (re.test(value + digit)) {
  240. e.preventDefault();
  241. return setTimeout(function() {
  242. return $target.val(value + digit + ' ');
  243. });
  244. }
  245. };
  246. formatBackCardNumber = function(e) {
  247. var $target, value;
  248. $target = $(e.currentTarget);
  249. value = $target.val();
  250. if (e.which !== 8) {
  251. return;
  252. }
  253. if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {
  254. return;
  255. }
  256. if (/\d\s$/.test(value)) {
  257. e.preventDefault();
  258. return setTimeout(function() {
  259. return $target.val(value.replace(/\d\s$/, ''));
  260. });
  261. } else if (/\s\d?$/.test(value)) {
  262. e.preventDefault();
  263. return setTimeout(function() {
  264. return $target.val(value.replace(/\d$/, ''));
  265. });
  266. }
  267. };
  268. reFormatExpiry = function(e) {
  269. var $target;
  270. $target = $(e.currentTarget);
  271. return setTimeout(function() {
  272. var value;
  273. value = $target.val();
  274. value = replaceFullWidthChars(value);
  275. value = $.payment.formatExpiry(value);
  276. return safeVal(value, $target);
  277. });
  278. };
  279. formatExpiry = function(e) {
  280. var $target, digit, val;
  281. digit = String.fromCharCode(e.which);
  282. if (!/^\d+$/.test(digit)) {
  283. return;
  284. }
  285. $target = $(e.currentTarget);
  286. val = $target.val() + digit;
  287. if (/^\d$/.test(val) && (val !== '0' && val !== '1')) {
  288. e.preventDefault();
  289. return setTimeout(function() {
  290. return $target.val("0" + val + " / ");
  291. });
  292. } else if (/^\d\d$/.test(val)) {
  293. e.preventDefault();
  294. return setTimeout(function() {
  295. var m1, m2;
  296. m1 = parseInt(val[0], 10);
  297. m2 = parseInt(val[1], 10);
  298. if (m2 > 2 && m1 !== 0) {
  299. return $target.val("0" + m1 + " / " + m2);
  300. } else {
  301. return $target.val("" + val + " / ");
  302. }
  303. });
  304. }
  305. };
  306. formatForwardExpiry = function(e) {
  307. var $target, digit, val;
  308. digit = String.fromCharCode(e.which);
  309. if (!/^\d+$/.test(digit)) {
  310. return;
  311. }
  312. $target = $(e.currentTarget);
  313. val = $target.val();
  314. if (/^\d\d$/.test(val)) {
  315. return $target.val("" + val + " / ");
  316. }
  317. };
  318. formatForwardSlashAndSpace = function(e) {
  319. var $target, val, which;
  320. which = String.fromCharCode(e.which);
  321. if (!(which === '/' || which === ' ')) {
  322. return;
  323. }
  324. $target = $(e.currentTarget);
  325. val = $target.val();
  326. if (/^\d$/.test(val) && val !== '0') {
  327. return $target.val("0" + val + " / ");
  328. }
  329. };
  330. formatBackExpiry = function(e) {
  331. var $target, value;
  332. $target = $(e.currentTarget);
  333. value = $target.val();
  334. if (e.which !== 8) {
  335. return;
  336. }
  337. if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {
  338. return;
  339. }
  340. if (/\d\s\/\s$/.test(value)) {
  341. e.preventDefault();
  342. return setTimeout(function() {
  343. return $target.val(value.replace(/\d\s\/\s$/, ''));
  344. });
  345. }
  346. };
  347. reFormatCVC = function(e) {
  348. var $target;
  349. $target = $(e.currentTarget);
  350. return setTimeout(function() {
  351. var value;
  352. value = $target.val();
  353. value = replaceFullWidthChars(value);
  354. value = value.replace(/\D/g, '').slice(0, 4);
  355. return safeVal(value, $target);
  356. });
  357. };
  358. restrictNumeric = function(e) {
  359. var input;
  360. if (e.metaKey || e.ctrlKey) {
  361. return true;
  362. }
  363. if (e.which === 32) {
  364. return false;
  365. }
  366. if (e.which === 0) {
  367. return true;
  368. }
  369. if (e.which < 33) {
  370. return true;
  371. }
  372. input = String.fromCharCode(e.which);
  373. return !!/[\d\s]/.test(input);
  374. };
  375. restrictCardNumber = function(e) {
  376. var $target, card, digit, value;
  377. $target = $(e.currentTarget);
  378. digit = String.fromCharCode(e.which);
  379. if (!/^\d+$/.test(digit)) {
  380. return;
  381. }
  382. if (hasTextSelected($target)) {
  383. return;
  384. }
  385. value = ($target.val() + digit).replace(/\D/g, '');
  386. card = cardFromNumber(value);
  387. if (card) {
  388. return value.length <= card.length[card.length.length - 1];
  389. } else {
  390. return value.length <= 16;
  391. }
  392. };
  393. restrictExpiry = function(e) {
  394. var $target, digit, value;
  395. $target = $(e.currentTarget);
  396. digit = String.fromCharCode(e.which);
  397. if (!/^\d+$/.test(digit)) {
  398. return;
  399. }
  400. if (hasTextSelected($target)) {
  401. return;
  402. }
  403. value = $target.val() + digit;
  404. value = value.replace(/\D/g, '');
  405. if (value.length > 6) {
  406. return false;
  407. }
  408. };
  409. restrictCVC = function(e) {
  410. var $target, digit, val;
  411. $target = $(e.currentTarget);
  412. digit = String.fromCharCode(e.which);
  413. if (!/^\d+$/.test(digit)) {
  414. return;
  415. }
  416. if (hasTextSelected($target)) {
  417. return;
  418. }
  419. val = $target.val() + digit;
  420. return val.length <= 4;
  421. };
  422. setCardType = function(e) {
  423. var $target, allTypes, card, cardType, val;
  424. $target = $(e.currentTarget);
  425. val = $target.val();
  426. cardType = $.payment.cardType(val) || 'unknown';
  427. if (!$target.hasClass(cardType)) {
  428. allTypes = (function() {
  429. var _i, _len, _results;
  430. _results = [];
  431. for (_i = 0, _len = cards.length; _i < _len; _i++) {
  432. card = cards[_i];
  433. _results.push(card.type);
  434. }
  435. return _results;
  436. })();
  437. $target.removeClass('unknown');
  438. $target.removeClass(allTypes.join(' '));
  439. $target.addClass(cardType);
  440. $target.toggleClass('identified', cardType !== 'unknown');
  441. return $target.trigger('payment.cardType', cardType);
  442. }
  443. };
  444. $.payment.fn.formatCardCVC = function() {
  445. this.on('keypress', restrictNumeric);
  446. this.on('keypress', restrictCVC);
  447. this.on('paste', reFormatCVC);
  448. this.on('change', reFormatCVC);
  449. this.on('input', reFormatCVC);
  450. return this;
  451. };
  452. $.payment.fn.formatCardExpiry = function() {
  453. this.on('keypress', restrictNumeric);
  454. this.on('keypress', restrictExpiry);
  455. this.on('keypress', formatExpiry);
  456. this.on('keypress', formatForwardSlashAndSpace);
  457. this.on('keypress', formatForwardExpiry);
  458. this.on('keydown', formatBackExpiry);
  459. this.on('change', reFormatExpiry);
  460. this.on('input', reFormatExpiry);
  461. return this;
  462. };
  463. $.payment.fn.formatCardNumber = function() {
  464. this.on('keypress', restrictNumeric);
  465. this.on('keypress', restrictCardNumber);
  466. this.on('keypress', formatCardNumber);
  467. this.on('keydown', formatBackCardNumber);
  468. this.on('keyup', setCardType);
  469. this.on('paste', reFormatCardNumber);
  470. this.on('change', reFormatCardNumber);
  471. this.on('input', reFormatCardNumber);
  472. this.on('input', setCardType);
  473. return this;
  474. };
  475. $.payment.fn.restrictNumeric = function() {
  476. this.on('keypress', restrictNumeric);
  477. this.on('paste', reFormatNumeric);
  478. this.on('change', reFormatNumeric);
  479. this.on('input', reFormatNumeric);
  480. return this;
  481. };
  482. $.payment.fn.cardExpiryVal = function() {
  483. return $.payment.cardExpiryVal($(this).val());
  484. };
  485. $.payment.cardExpiryVal = function(value) {
  486. var month, prefix, year, _ref;
  487. _ref = value.split(/[\s\/]+/, 2), month = _ref[0], year = _ref[1];
  488. if ((year != null ? year.length : void 0) === 2 && /^\d+$/.test(year)) {
  489. prefix = (new Date).getFullYear();
  490. prefix = prefix.toString().slice(0, 2);
  491. year = prefix + year;
  492. }
  493. month = parseInt(month, 10);
  494. year = parseInt(year, 10);
  495. return {
  496. month: month,
  497. year: year
  498. };
  499. };
  500. $.payment.validateCardNumber = function(num) {
  501. var card, _ref;
  502. num = (num + '').replace(/\s+|-/g, '');
  503. if (!/^\d+$/.test(num)) {
  504. return false;
  505. }
  506. card = cardFromNumber(num);
  507. if (!card) {
  508. return false;
  509. }
  510. return (_ref = num.length, __indexOf.call(card.length, _ref) >= 0) && (card.luhn === false || luhnCheck(num));
  511. };
  512. $.payment.validateCardExpiry = function(month, year) {
  513. var currentTime, expiry, _ref;
  514. if (typeof month === 'object' && 'month' in month) {
  515. _ref = month, month = _ref.month, year = _ref.year;
  516. }
  517. if (!(month && year)) {
  518. return false;
  519. }
  520. month = $.trim(month);
  521. year = $.trim(year);
  522. if (!/^\d+$/.test(month)) {
  523. return false;
  524. }
  525. if (!/^\d+$/.test(year)) {
  526. return false;
  527. }
  528. if (!((1 <= month && month <= 12))) {
  529. return false;
  530. }
  531. if (year.length === 2) {
  532. if (year < 70) {
  533. year = "20" + year;
  534. } else {
  535. year = "19" + year;
  536. }
  537. }
  538. if (year.length !== 4) {
  539. return false;
  540. }
  541. expiry = new Date(year, month);
  542. currentTime = new Date;
  543. expiry.setMonth(expiry.getMonth() - 1);
  544. expiry.setMonth(expiry.getMonth() + 1, 1);
  545. return expiry > currentTime;
  546. };
  547. $.payment.validateCardCVC = function(cvc, type) {
  548. var card, _ref;
  549. cvc = $.trim(cvc);
  550. if (!/^\d+$/.test(cvc)) {
  551. return false;
  552. }
  553. card = cardFromType(type);
  554. if (card != null) {
  555. return _ref = cvc.length, __indexOf.call(card.cvcLength, _ref) >= 0;
  556. } else {
  557. return cvc.length >= 3 && cvc.length <= 4;
  558. }
  559. };
  560. $.payment.cardType = function(num) {
  561. var _ref;
  562. if (!num) {
  563. return null;
  564. }
  565. return ((_ref = cardFromNumber(num)) != null ? _ref.type : void 0) || null;
  566. };
  567. $.payment.formatCardNumber = function(num) {
  568. var card, groups, upperLength, _ref;
  569. num = num.replace(/\D/g, '');
  570. card = cardFromNumber(num);
  571. if (!card) {
  572. return num;
  573. }
  574. upperLength = card.length[card.length.length - 1];
  575. num = num.slice(0, upperLength);
  576. if (card.format.global) {
  577. return (_ref = num.match(card.format)) != null ? _ref.join(' ') : void 0;
  578. } else {
  579. groups = card.format.exec(num);
  580. if (groups == null) {
  581. return;
  582. }
  583. groups.shift();
  584. groups = $.grep(groups, function(n) {
  585. return n;
  586. });
  587. return groups.join(' ');
  588. }
  589. };
  590. $.payment.formatExpiry = function(expiry) {
  591. var mon, parts, sep, year;
  592. parts = expiry.match(/^\D*(\d{1,2})(\D+)?(\d{1,4})?/);
  593. if (!parts) {
  594. return '';
  595. }
  596. mon = parts[1] || '';
  597. sep = parts[2] || '';
  598. year = parts[3] || '';
  599. if (year.length > 0) {
  600. sep = ' / ';
  601. } else if (sep === ' /') {
  602. mon = mon.substring(0, 1);
  603. sep = '';
  604. } else if (mon.length === 2 || sep.length > 0) {
  605. sep = ' / ';
  606. } else if (mon.length === 1 && (mon !== '0' && mon !== '1')) {
  607. mon = "0" + mon;
  608. sep = ' / ';
  609. }
  610. return mon + sep + year;
  611. };
  612. }).call(this);