1 2/* 3 Payform Javascript Library 4 5 URL: https://github.com/jondavidjohn/payform 6 Author: Jonathan D. Johnson <[email protected]> 7 License: MIT 8 Version: 1.4.0 9 */ 10 11(function() { 12 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; }; 13 14 (function(name, definition) { 15 if (typeof module !== "undefined" && module !== null) { 16 return module.exports = definition(); 17 } else if (typeof define === 'function' && typeof define.amd === 'object') { 18 return define(name, definition); 19 } else { 20 return this[name] = definition(); 21 } 22 })('payform', function() { 23 var _eventNormalize, _getCaretPos, _off, _on, attachEvents, cardFromNumber, cardFromType, defaultFormat, eventList, formatBackCardNumber, formatBackExpiry, formatCardExpiry, formatCardNumber, formatForwardExpiry, formatForwardSlashAndSpace, getDirectionality, hasTextSelected, keyCodes, luhnCheck, payform, reFormatCVC, reFormatCardNumber, reFormatExpiry, replaceFullWidthChars, restrictCVC, restrictCardNumber, restrictExpiry, restrictNumeric; 24 _getCaretPos = function(ele) { 25 var r, rc, re; 26 if (ele.selectionStart != null) { 27 return ele.selectionStart; 28 } else if (document.selection != null) { 29 ele.focus(); 30 r = document.selection.createRange(); 31 re = ele.createTextRange(); 32 rc = re.duplicate(); 33 re.moveToBookmark(r.getBookmark()); 34 rc.setEndPoint('EndToStart', re); 35 return rc.text.length; 36 } 37 }; 38 _eventNormalize = function(listener) { 39 return function(e) { 40 var newEvt; 41 if (e == null) { 42 e = window.event; 43 } 44 if (e.inputType === 'insertCompositionText' && !e.isComposing) { 45 return; 46 } 47 newEvt = { 48 target: e.target || e.srcElement, 49 which: e.which || e.keyCode, 50 type: e.type, 51 metaKey: e.metaKey, 52 ctrlKey: e.ctrlKey, 53 preventDefault: function() { 54 if (e.preventDefault) { 55 e.preventDefault(); 56 } else { 57 e.returnValue = false; 58 } 59 } 60 }; 61 return listener(newEvt); 62 }; 63 }; 64 _on = function(ele, event, listener) { 65 if (ele.addEventListener != null) { 66 return ele.addEventListener(event, listener, false); 67 } else { 68 return ele.attachEvent("on" + event, listener); 69 } 70 }; 71 _off = function(ele, event, listener) { 72 if (ele.removeEventListener != null) { 73 return ele.removeEventListener(event, listener, false); 74 } else { 75 return ele.detachEvent("on" + event, listener); 76 } 77 }; 78 payform = {}; 79 keyCodes = { 80 UNKNOWN: 0, 81 BACKSPACE: 8, 82 PAGE_UP: 33, 83 ARROW_LEFT: 37, 84 ARROW_RIGHT: 39 85 }; 86 defaultFormat = /(\d{1,4})/g; 87 payform.cards = [ 88 { 89 type: 'elo', 90 pattern: /^(4011(78|79)|43(1274|8935)|45(1416|7393|763(1|2))|50(4175|6699|67[0-7][0-9]|9000)|627780|63(6297|6368)|650(03([^4])|04([0-9])|05(0|1)|4(0[5-9]|3[0-9]|8[5-9]|9[0-9])|5([0-2][0-9]|3[0-8])|9([2-6][0-9]|7[0-8])|541|700|720|901)|651652|655000|655021)/, 91 format: defaultFormat, 92 length: [16], 93 cvcLength: [3], 94 luhn: true 95 }, { 96 type: 'visaelectron', 97 pattern: /^4(026|17500|405|508|844|91[37])/, 98 format: defaultFormat, 99 length: [16], 100 cvcLength: [3], 101 luhn: true 102 }, { 103 type: 'maestro', 104 pattern: /^(5018|5020|5038|6304|6390[0-9]{2}|67[0-9]{4})/, 105 format: defaultFormat, 106 length: [12, 13, 14, 15, 16, 17, 18, 19], 107 cvcLength: [3], 108 luhn: true 109 }, { 110 type: 'forbrugsforeningen', 111 pattern: /^600/, 112 format: defaultFormat, 113 length: [16], 114 cvcLength: [3], 115 luhn: true 116 }, { 117 type: 'dankort', 118 pattern: /^5019/, 119 format: defaultFormat, 120 length: [16], 121 cvcLength: [3], 122 luhn: true 123 }, { 124 type: 'visa', 125 pattern: /^4/, 126 format: defaultFormat, 127 length: [13, 16, 19], 128 cvcLength: [3], 129 luhn: true 130 }, { 131 type: 'mastercard', 132 pattern: /^(5[1-5][0-9]{4}|677189)|^(222[1-9]|2[3-6]\d{2}|27[0-1]\d|2720)([0-9]{2})/, 133 format: defaultFormat, 134 length: [16], 135 cvcLength: [3], 136 luhn: true 137 }, { 138 type: 'amex', 139 pattern: /^3[47]/, 140 format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/, 141 length: [15], 142 cvcLength: [4], 143 luhn: true 144 }, { 145 type: 'hipercard', 146 pattern: /^(384100|384140|384160|606282|637095|637568|60(?!11))/, 147 format: defaultFormat, 148 length: [14, 15, 16, 17, 18, 19], 149 cvcLength: [3], 150 luhn: true 151 }, { 152 type: 'dinersclub', 153 pattern: /^(36|38|30[0-5])/, 154 format: /(\d{1,4})(\d{1,6})?(\d{1,4})?/, 155 length: [14], 156 cvcLength: [3], 157 luhn: true 158 }, { 159 type: 'discover', 160 pattern: /^(6011|65|64[4-9]|622)/, 161 format: defaultFormat, 162 length: [16], 163 cvcLength: [3], 164 luhn: true 165 }, { 166 type: 'unionpay', 167 pattern: /^62/, 168 format: defaultFormat, 169 length: [16, 17, 18, 19], 170 cvcLength: [3],
171 luhn: false 172 }, { 173 type: 'jcb', 174 pattern: /^35/, 175 format: defaultFormat, 176 length: [16, 17, 18, 19], 177 cvcLength: [3], 178 luhn: true 179 }, { 180 type: 'laser', 181 pattern: /^(6706|6771|6709)/, 182 format: defaultFormat, 183 length: [16, 17, 18, 19], 184 cvcLength: [3], 185 luhn: true 186 } 187 ]; 188 cardFromNumber = function(num) { 189 var card, i, len, ref; 190 num = (num + '').replace(/\D/g, ''); 191 ref = payform.cards; 192 for (i = 0, len = ref.length; i < len; i++) { 193 card = ref[i]; 194 if (card.pattern.test(num)) { 195 return card; 196 } 197 } 198 }; 199 cardFromType = function(type) { 200 var card, i, len, ref; 201 ref = payform.cards; 202 for (i = 0, len = ref.length; i < len; i++) { 203 card = ref[i]; 204 if (card.type === type) { 205 return card; 206 } 207 } 208 }; 209 getDirectionality = function(target) { 210 var style; 211 style = getComputedStyle(target); 212 return style && style['direction'] || document.dir; 213 }; 214 luhnCheck = function(num) { 215 var digit, digits, i, len, odd, sum; 216 odd = true; 217 sum = 0; 218 digits = (num + '').split('').reverse(); 219 for (i = 0, len = digits.length; i < len; i++) { 220 digit = digits[i]; 221 digit = parseInt(digit, 10); 222 if ((odd = !odd)) { 223 digit *= 2; 224 } 225 if (digit > 9) { 226 digit -= 9; 227 } 228 sum += digit; 229 } 230 return sum % 10 === 0; 231 }; 232 hasTextSelected = function(target) { 233 var ref; 234 if ((typeof document !== "undefined" && document !== null ? (ref = document.selection) != null ? ref.createRange : void 0 : void 0) != null) { 235 if (document.selection.createRange().text) { 236 return true; 237 } 238 } 239 return (target.selectionStart != null) && target.selectionStart !== target.selectionEnd; 240 }; 241 replaceFullWidthChars = function(str) { 242 var char, chars, fullWidth, halfWidth, i, idx, len, value; 243 if (str == null) { 244 str = ''; 245 } 246 fullWidth = '\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19'; 247 halfWidth = '0123456789'; 248 value = ''; 249 chars = str.split(''); 250 for (i = 0, len = chars.length; i < len; i++) { 251 char = chars[i]; 252 idx = fullWidth.indexOf(char); 253 if (idx > -1) { 254 char = halfWidth[idx]; 255 } 256 value += char; 257 } 258 return value; 259 }; 260 reFormatCardNumber = function(e) { 261 var cursor; 262 cursor = _getCaretPos(e.target); 263 if (e.target.value === "") { 264 return; 265 } 266 if (getDirectionality(e.target) === 'ltr') { 267 cursor = _getCaretPos(e.target); 268 } 269 e.target.value = payform.formatCardNumber(e.target.value); 270 if (getDirectionality(e.target) === 'ltr' && cursor !== e.target.selectionStart) { 271 cursor = _getCaretPos(e.target); 272 } 273 if (getDirectionality(e.target) === 'rtl' && e.target.value.indexOf('â\u200e') === -1) { 274 e.target.value = 'â\u200e'.concat(e.target.value); 275 } 276 cursor = _getCaretPos(e.target); 277 if ((cursor != null) && cursor !== 0 && e.type !== 'change') { 278 return e.target.setSelectionRange(cursor, cursor); 279 } 280 }; 281 formatCardNumber = function(e) { 282 var card, cursor, digit, length, re, upperLength, value; 283 digit = String.fromCharCode(e.which); 284 if (!/^\d+$/.test(digit)) { 285 return; 286 } 287 value = e.target.value; 288 card = cardFromNumber(value + digit); 289 length = (value.replace(/\D/g, '') + digit).length; 290 upperLength = 16; 291 if (card) { 292 upperLength = card.length[card.length.length - 1]; 293 } 294 if (length >= upperLength) { 295 return; 296 } 297 cursor = _getCaretPos(e.target); 298 if (cursor && cursor !== value.length) { 299 return; 300 } 301 if (card && card.type === 'amex') { 302 re = /^(\d{4}|\d{4}\s\d{6})$/; 303 } else { 304 re = /(?:^|\s)(\d{4})$/; 305 } 306 if (re.test(value)) { 307 e.preventDefault(); 308 return setTimeout(function() { 309 return e.target.value = value + " " + digit; 310 }); 311 } else if (re.test(value + digit)) { 312 e.preventDefault(); 313 return setTimeout(function() { 314 return e.target.value = (value + digit) + " "; 315 }); 316 } 317 }; 318 formatBackCardNumber = function(e) { 319 var cursor, value; 320 value = e.target.value; 321 if (e.which !== keyCodes.BACKSPACE) { 322 return; 323 } 324 cursor = _getCaretPos(e.target); 325 if (cursor && cursor !== value.length) { 326 return; 327 } 328 if ((e.target.selectionEnd - e.target.selectionStart) > 1) { 329 return; 330 } 331 if (/\d\s$/.test(value)) { 332 e.preventDefault(); 333 return setTimeout(function() { 334 return e.target.value = value.replace(/\d\s$/, ''); 335 }); 336 } else if (/\s\d?$/.test(value)) { 337 e.preventDefault(); 338 return setTimeout(function() { 339 return e.target.value = value.replace(/\d$/, ''); 340 }); 341 } 342 }; 343 reFormatExpiry = function(e) { 344 var cursor; 345 if (e.target.value === "") { 346 return; 347 } 348 e.target.value = payform.formatCardExpiry(e.target.value); 349 if (getDirectionality(e.target) === 'rtl' && e.target.value.indexOf('â\u200e') === -1) { 350 e.target.value = 'â\u200e'.concat(e.target.value); 351 } 352 cursor = _getCaretPos(e.target); 353 if ((cursor != null) && e.type !== 'change') { 354 return e.target.setSelectionRange(cursor, cursor); 355 } 356 }; 357 formatCardExpiry = function(e) { 358 var digit, val; 359 digit = String.fromCharCode(e.which); 360 if (!/^\d+$/.test(digit)) { 361 return; 362 } 363 val = e.target.value + digit; 364 if (/^\d$/.test(val) && (val !== '0' && val !== '1')) { 365 e.preventDefault(); 366 return setTimeout(function() { 367 return e.target.value = "0" + val + " / "; 368 }); 369 } else if (/^\d\d$/.test(val)) { 370 e.preventDefault(); 371 return setTimeout(function() { 372 return e.target.value = val + " / "; 373 }); 374 } 375 }; 376 formatForwardExpiry = function(e) { 377 var digit, val; 378 digit = String.fromCharCode(e.which); 379 if (!/^\d+$/.test(digit)) { 380 return; 381 } 382 val = e.target.value; 383 if (/^\d\d$/.test(val)) { 384 return e.target.value = val + " / "; 385 } 386 }; 387 formatForwardSlashAndSpace = function(e) { 388 var val, which; 389 which = String.fromCharCode(e.which); 390 if (!(which === '/' || which === ' ')) { 391 return; 392 } 393 val = e.target.value; 394 if (/^\d$/.test(val) && val !== '0') { 395 return e.target.value = "0" + val + " / "; 396 } 397 }; 398 formatBackExpiry = function(e) { 399 var cursor, value; 400 value = e.target.value; 401 if (e.which !== keyCodes.BACKSPACE) { 402 return; 403 } 404 cursor = _getCaretPos(e.target); 405 if (cursor && cursor !== value.length) { 406 return; 407 } 408 if (/\d\s\/\s$/.test(value)) { 409 e.preventDefault(); 410 return setTimeout(function() { 411 return e.target.value = value.replace(/\d\s\/\s$/, ''); 412 }); 413 } 414 }; 415 reFormatCVC = function(e) { 416 var cursor; 417 if (e.target.value === "") { 418 return; 419 } 420 cursor = _getCaretPos(e.target); 421 e.target.value = replaceFullWidthChars(e.target.value).replace(/\D/g, '').slice(0, 4); 422 if ((cursor != null) && e.type !== 'change') { 423 return e.target.setSelectionRange(cursor, cursor); 424 } 425 }; 426 restrictNumeric = function(e) { 427 var input; 428 if (e.metaKey || e.ctrlKey) { 429 return; 430 } 431 if ([keyCodes.UNKNOWN, keyCodes.ARROW_LEFT, keyCodes.ARROW_RIGHT].indexOf(e.which) > -1) { 432 return; 433 } 434 if (e.which < keyCodes.PAGE_UP) { 435 return; 436 } 437 input = String.fromCharCode(e.which); 438 if (!/^\d+$/.test(input)) { 439 return e.preventDefault(); 440 } 441 }; 442 restrictCardNumber = function(e) { 443 var card, digit, maxLength, value; 444 digit = String.fromCharCode(e.which); 445 if (!/^\d+$/.test(digit)) { 446 return; 447 } 448 if (hasTextSelected(e.target)) { 449 return; 450 } 451 value = (e.target.value + digit).replace(/\D/g, ''); 452 card = cardFromNumber(value); 453 maxLength = card ? card.length[card.length.length - 1] : 16; 454 if (value.length > maxLength) { 455 return e.preventDefault(); 456 } 457 }; 458 restrictExpiry = function(e) { 459 var digit, value; 460 digit = String.fromCharCode(e.which); 461 if (!/^\d+$/.test(digit)) { 462 return; 463 } 464 if (hasTextSelected(e.target)) { 465 return; 466 } 467 value = e.target.value + digit; 468 value = value.replace(/\D/g, ''); 469 if (value.length > 4) { 470 return e.preventDefault(); 471 } 472 }; 473 restrictCVC = function(e) { 474 var digit, val; 475 digit = String.fromCharCode(e.which); 476 if (!/^\d+$/.test(digit)) { 477 return; 478 } 479 if (hasTextSelected(e.target)) { 480 return; 481 } 482 val = e.target.value + digit; 483 if (val.length > 4) { 484 return e.preventDefault(); 485 } 486 }; 487 eventList = { 488 cvcInput: [ 489 { 490 eventName: 'keypress', 491 eventHandler: _eventNormalize(restrictNumeric) 492 }, { 493 eventName: 'keypress', 494 eventHandler: _eventNormalize(restrictCVC) 495 }, { 496 eventName: 'paste', 497 eventHandler: _eventNormalize(reFormatCVC) 498 }, { 499 eventName: 'change', 500 eventHandler: _eventNormalize(reFormatCVC) 501 }, { 502 eventName: 'input', 503 eventHandler: _eventNormalize(reFormatCVC) 504 } 505 ], 506 expiryInput: [ 507 { 508 eventName: 'keypress', 509 eventHandler: _eventNormalize(restrictNumeric) 510 }, { 511 eventName: 'keypress', 512 eventHandler: _eventNormalize(restrictExpiry) 513 }, { 514 eventName: 'keypress', 515 eventHandler: _eventNormalize(formatCardExpiry) 516 }, { 517 eventName: 'keypress', 518 eventHandler: _eventNormalize(formatForwardSlashAndSpace) 519 }, { 520 eventName: 'keypress', 521 eventHandler: _eventNormalize(formatForwardExpiry) 522 }, { 523 eventName: 'keydown', 524 eventHandler: _eventNormalize(formatBackExpiry) 525 }, { 526 eventName: 'change', 527 eventHandler: _eventNormalize(reFormatExpiry) 528 }, { 529 eventName: 'input', 530 eventHandler: _eventNormalize(reFormatExpiry) 531 } 532 ], 533 cardNumberInput: [ 534 { 535 eventName: 'keypress', 536 eventHandler: _eventNormalize(restrictNumeric) 537 }, { 538 eventName: 'keypress', 539 eventHandler: _eventNormalize(restrictCardNumber) 540 }, { 541 eventName: 'keypress', 542 eventHandler: _eventNormalize(formatCardNumber) 543 }, { 544 eventName: 'keydown', 545 eventHandler: _eventNormalize(formatBackCardNumber) 546 }, { 547 eventName: 'paste', 548 eventHandler: _eventNormalize(reFormatCardNumber) 549 }, { 550 eventName: 'change', 551 eventHandler: _eventNormalize(reFormatCardNumber) 552 }, { 553 eventName: 'input', 554 eventHandler: _eventNormalize(reFormatCardNumber) 555 } 556 ], 557 numericInput: [ 558 { 559 eventName: 'keypress', 560 eventHandler: _eventNormalize(restrictNumeric) 561 }, { 562 eventName: 'paste', 563 eventHandler: _eventNormalize(restrictNumeric) 564 }, { 565 eventName: 'change', 566 eventHandler: _eventNormalize(restrictNumeric) 567 }, { 568 eventName: 'input', 569 eventHandler: _eventNormalize(restrictNumeric) 570 } 571 ] 572 }; 573 attachEvents = function(input, events, detach) { 574 var evt, i, len; 575 for (i = 0, len = events.length; i < len; i++) { 576 evt = events[i]; 577 if (detach) { 578 _off(input, evt.eventName, evt.eventHandler); 579 } else { 580 _on(input, evt.eventName, evt.eventHandler); 581 } 582 } 583 }; 584 payform.cvcInput = function(input) { 585 return attachEvents(input, eventList.cvcInput); 586 }; 587 payform.expiryInput = function(input) { 588 return attachEvents(input, eventList.expiryInput); 589 }; 590 payform.cardNumberInput = function(input) { 591 return attachEvents(input, eventList.cardNumberInput); 592 }; 593 payform.numericInput = function(input) { 594 return attachEvents(input, eventList.numericInput); 595 }; 596 payform.detachCvcInput = function(input) { 597 return attachEvents(input, eventList.cvcInput, true); 598 }; 599 payform.detachExpiryInput = function(input) { 600 return attachEvents(input, eventList.expiryInput, true); 601 }; 602 payform.detachCardNumberInput = function(input) { 603 return attachEvents(input, eventList.cardNumberInput, true); 604 }; 605 payform.detachNumericInput = function(input) { 606 return attachEvents(input, eventList.numericInput, true); 607 }; 608 payform.parseCardExpiry = function(value) { 609 var month, prefix, ref, year; 610 value = value.replace(/\s/g, '');
611 ref = value.split('/', 2), month = ref[0], year = ref[1]; 612 if ((year != null ? year.length : void 0) === 2 && /^\d+$/.test(year)) { 613 prefix = (new Date).getFullYear(); 614 prefix = prefix.toString().slice(0, 2); 615 year = prefix + year; 616 } 617 month = parseInt(month.replace(/[\u200e]/g, ""), 10); 618 year = parseInt(year, 10); 619 return { 620 month: month, 621 year: year 622 }; 623 }; 624 payform.validateCardNumber = function(num) { 625 var card, ref; 626 num = (num + '').replace(/\s+|-/g, ''); 627 if (!/^\d+$/.test(num)) { 628 return false; 629 } 630 card = cardFromNumber(num); 631 if (!card) { 632 return false; 633 } 634 return (ref = num.length, indexOf.call(card.length, ref) >= 0) && (card.luhn === false || luhnCheck(num)); 635 }; 636 payform.validateCardExpiry = function(month, year) { 637 var currentTime, expiry, ref; 638 if (typeof month === 'object' && 'month' in month) { 639 ref = month, month = ref.month, year = ref.year; 640 } 641 if (!(month && year)) { 642 return false; 643 } 644 month = String(month).trim(); 645 year = String(year).trim(); 646 if (!/^\d+$/.test(month)) { 647 return false; 648 } 649 if (!/^\d+$/.test(year)) { 650 return false; 651 } 652 if (!((1 <= month && month <= 12))) { 653 return false; 654 } 655 if (year.length === 2) { 656 if (year < 70) { 657 year = "20" + year; 658 } else { 659 year = "19" + year; 660 } 661 } 662 if (year.length !== 2) { 663 return false; 664 } 665 expiry = new Date(year, month); 666 currentTime = new Date; 667 expiry.setMonth(expiry.getMonth() - 1); 668 expiry.setMonth(expiry.getMonth() + 1, 1); 669 return expiry > currentTime; 670 }; 671 payform.validateCardCVC = function(cvc, type) { 672 var card, ref; 673 cvc = String(cvc).trim(); 674 if (!/^\d+$/.test(cvc)) { 675 return false; 676 } 677 card = cardFromType(type); 678 if (card != null) { 679 return ref = cvc.length, indexOf.call(card.cvcLength, ref) >= 0; 680 } else { 681 return cvc.length >= 3 && cvc.length <= 4; 682 } 683 }; 684 payform.parseCardType = function(num) { 685 var ref; 686 if (!num) { 687 return null; 688 } 689 return ((ref = cardFromNumber(num)) != null ? ref.type : void 0) || null; 690 }; 691 payform.formatCardNumber = function(num) { 692 var card, groups, ref, upperLength; 693 num = replaceFullWidthChars(num); 694 num = num.replace(/\D/g, ''); 695 card = cardFromNumber(num); 696 if (!card) { 697 return num; 698 } 699 upperLength = card.length[card.length.length - 1]; 700 num = num.slice(0, upperLength); 701 if (card.format.global) { 702 return (ref = num.match(card.format)) != null ? ref.join(' ') : void 0; 703 } else { 704 groups = card.format.exec(num); 705 if (groups == null) { 706 return; 707 } 708 groups.shift(); 709 groups = groups.filter(Boolean); 710 return groups.join(' '); 711 } 712 }; 713 payform.formatCardExpiry = function(expiry) { 714 var mon, parts, sep, year; 715 expiry = replaceFullWidthChars(expiry); 716 parts = expiry.match(/^\D*(\d{1,2})(\D+)?(\d{1,4})?/); 717 if (!parts) { 718 return ''; 719 } 720 mon = parts[1] || ''; 721 sep = parts[2] || ''; 722 year = parts[3] || ''; 723 if (year.length > 0) { 724 sep = ' / '; 725 } else if (sep === ' /') { 726 mon = mon.substring(0, 1); 727 sep = ''; 728 } else if (mon.length === 2 || sep.length > 0) { 729 sep = ' / '; 730 } else if (mon.length === 1 && (mon !== '0' && mon !== '1')) { 731 mon = "0" + mon; 732 sep = ' / '; 733 } 734 return mon + sep + year; 735 }; 736 return payform; 737 }); 738 739}).call(this);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.