jquery.payment.js 17 KB

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