
/*
  Payform Javascript Library

  URL: https://github.com/jondavidjohn/payform
  Author: Jonathan D. Johnson <me@jondavidjohn.com>
  License: MIT
  Version: 1.2.5
 */

(function() {
  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; };

  (function(name, definition) {
    if (typeof module !== "undefined" && module !== null) {
      return module.exports = definition();
    } else if (typeof define === 'function' && typeof define.amd === 'object') {
      return define(name, definition);
    } else {
      return this[name] = definition();
    }
  })('payform', function() {
    var _eventNormalize, _getCaretPos, _on, cardFromNumber, cardFromType, defaultFormat, formatBackCardNumber, formatBackExpiry, formatCardExpiry, formatCardNumber, formatForwardExpiry, formatForwardSlashAndSpace, hasTextSelected, luhnCheck, payform, reFormatCVC, reFormatCardNumber, reFormatExpiry, replaceFullWidthChars, restrictCVC, restrictCardNumber, restrictExpiry, restrictNumeric;
    _getCaretPos = function(ele) {
      var r, rc, re;
      if (ele.selectionStart != null) {
        return ele.selectionStart;
      } else if (document.selection != null) {
        ele.focus();
        r = document.selection.createRange();
        re = ele.createTextRange();
        rc = re.duplicate();
        re.moveToBookmark(r.getBookmark());
        rc.setEndPoint('EndToStart', re);
        return rc.text.length;
      }
    };
    _eventNormalize = function(listener) {
      return function(e) {
        var newEvt;
        if (e == null) {
          e = window.event;
        }
        if (e.inputType === 'insertCompositionText' && !e.isComposing) {
          return;
        }
        newEvt = {
          target: e.target || e.srcElement,
          which: e.which || e.keyCode,
          type: e.type,
          metaKey: e.metaKey,
          ctrlKey: e.ctrlKey,
          preventDefault: function() {
            if (e.preventDefault) {
              e.preventDefault();
            } else {
              e.returnValue = false;
            }
          }
        };
        return listener(newEvt);
      };
    };
    _on = function(ele, event, listener) {
      listener = _eventNormalize(listener);
      if (ele.addEventListener != null) {
        return ele.addEventListener(event, listener, false);
      } else {
        return ele.attachEvent("on" + event, listener);
      }
    };
    payform = {};
    defaultFormat = /(\d{1,4})/g;
    payform.cards = [
      {
        type: 'elo',
        pattern: /^((50670[7-8])|(506715)|(50671[7-9])|(50672[0-1])|(50672[4-9])|(50673[0-3])|(506739)|(50674[0-8])|(50675[0-3])|(50677[4-8])|(50900[0-9])|(50901[3-9])|(50902[0-9])|(50903[1-9])|(50904[0-9])|(50905[0-9])|(50906[0-4])|(50906[6-9])|(50907[0-2])|(50907[4-5])|(636368)|(636297)|(504175)|(438935)|(40117[8-9])|(45763[1-2])|(457393)|(431274)|(50907[6-9])|(50908[0-9])|(627780))/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'visaelectron',
        pattern: /^4(026|17500|405|508|844|91[37])/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'maestro',
        pattern: /^(5(018|0[23]|[68])|6(39|7))/,
        format: defaultFormat,
        length: [12, 13, 14, 15, 16, 17, 18, 19],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'forbrugsforeningen',
        pattern: /^600/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'dankort',
        pattern: /^5019/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'visa',
        pattern: /^4/,
        format: defaultFormat,
        length: [13, 16],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'mastercard',
        pattern: /^(5[1-5]|2[2-7])/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'amex',
        pattern: /^3[47]/,
        format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/,
        length: [15],
        cvcLength: [3, 4],
        luhn: true
      }, {
        type: 'dinersclub',
        pattern: /^3[0689]/,
        format: /(\d{1,4})(\d{1,4})?(\d{1,4})?(\d{1,2})?/,
        length: [14],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'discover',
        pattern: /^6([045]|22)/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
      }, {
        type: 'unionpay',
        pattern: /^(62|88)/,
        format: defaultFormat,
        length: [16, 17, 18, 19],
        cvcLength: [3],
        luhn: false
      }, {
        type: 'jcb',
        pattern: /^35/,
        format: defaultFormat,
        length: [16],
        cvcLength: [3],
        luhn: true
      }
    ];
    cardFromNumber = function(num) {
      var card, i, len, ref;
      num = (num + '').replace(/\D/g, '');
      ref = payform.cards;
      for (i = 0, len = ref.length; i < len; i++) {
        card = ref[i];
        if (card.pattern.test(num)) {
          return card;
        }
      }
    };
    cardFromType = function(type) {
      var card, i, len, ref;
      ref = payform.cards;
      for (i = 0, len = ref.length; i < len; i++) {
        card = ref[i];
        if (card.type === type) {
          return card;
        }
      }
    };
    luhnCheck = function(num) {
      var digit, digits, i, len, odd, sum;
      odd = true;
      sum = 0;
      digits = (num + '').split('').reverse();
      for (i = 0, len = digits.length; i < len; i++) {
        digit = digits[i];
        digit = parseInt(digit, 10);
        if ((odd = !odd)) {
          digit *= 2;
        }
        if (digit > 9) {
          digit -= 9;
        }
        sum += digit;
      }
      return sum % 10 === 0;
    };
    hasTextSelected = function(target) {
      var ref;
      if ((typeof document !== "undefined" && document !== null ? (ref = document.selection) != null ? ref.createRange : void 0 : void 0) != null) {
        if (document.selection.createRange().text) {
          return true;
        }
      }
      return (target.selectionStart != null) && target.selectionStart !== target.selectionEnd;
    };
    replaceFullWidthChars = function(str) {
      var char, chars, fullWidth, halfWidth, i, idx, len, value;
      if (str == null) {
        str = '';
      }
      fullWidth = '\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19';
      halfWidth = '0123456789';
      value = '';
      chars = str.split('');
      for (i = 0, len = chars.length; i < len; i++) {
        char = chars[i];
        idx = fullWidth.indexOf(char);
        if (idx > -1) {
          char = halfWidth[idx];
        }
        value += char;
      }
      return value;
    };
    reFormatCardNumber = function(e) {
      var cursor;
      if (e.target.value === "") {
        return;
      }
      e.target.value = payform.formatCardNumber(e.target.value);
      if (document.dir === 'rtl' && e.target.value.indexOf('‎\u200e') === -1) {
        e.target.value = '‎\u200e'.concat(e.target.value);
      }
      cursor = _getCaretPos(e.target);
      if ((cursor != null) && e.type !== 'change') {
        return e.target.setSelectionRange(cursor, cursor);
      }
    };
    formatCardNumber = function(e) {
      var card, cursor, digit, length, re, upperLength, value;
      digit = String.fromCharCode(e.which);
      if (!/^\d+$/.test(digit)) {
        return;
      }
      value = e.target.value;
      card = cardFromNumber(value + digit);
      length = (value.replace(/\D/g, '') + digit).length;
      upperLength = 16;
      if (card) {
        upperLength = card.length[card.length.length - 1];
      }
      if (length >= upperLength) {
        return;
      }
      cursor = _getCaretPos(e.target);
      if (cursor && cursor !== value.length) {
        return;
      }
      if (card && card.type === 'amex') {
        re = /^(\d{4}|\d{4}\s\d{6})$/;
      } else {
        re = /(?:^|\s)(\d{4})$/;
      }
      if (re.test(value)) {
        e.preventDefault();
        return setTimeout(function() {
          return e.target.value = value + " " + digit;
        });
      } else if (re.test(value + digit)) {
        e.preventDefault();
        return setTimeout(function() {
          return e.target.value = (value + digit) + " ";
        });
      }
    };
    formatBackCardNumber = function(e) {
      var cursor, value;
      value = e.target.value;
      if (e.which !== 8) {
        return;
      }
      cursor = _getCaretPos(e.target);
      if (cursor && cursor !== value.length) {
        return;
      }
      if (/\d\s$/.test(value)) {
        e.preventDefault();
        return setTimeout(function() {
          return e.target.value = value.replace(/\d\s$/, '');
        });
      } else if (/\s\d?$/.test(value)) {
        e.preventDefault();
        return setTimeout(function() {
          return e.target.value = value.replace(/\d$/, '');
        });
      }
    };
    reFormatExpiry = function(e) {
      var cursor;
      if (e.target.value === "") {
        return;
      }
      e.target.value = payform.formatCardExpiry(e.target.value);
      if (document.dir === 'rtl' && e.target.value.indexOf('‎\u200e') === -1) {
        e.target.value = '‎\u200e'.concat(e.target.value);
      }
      cursor = _getCaretPos(e.target);
      if ((cursor != null) && e.type !== 'change') {
        return e.target.setSelectionRange(cursor, cursor);
      }
    };
    formatCardExpiry = function(e) {
      var digit, val;
      digit = String.fromCharCode(e.which);
      if (!/^\d+$/.test(digit)) {
        return;
      }
      val = e.target.value + digit;
      if (/^\d$/.test(val) && (val !== '0' && val !== '1')) {
        e.preventDefault();
        return setTimeout(function() {
          return e.target.value = "0" + val + " / ";
        });
      } else if (/^\d\d$/.test(val)) {
        e.preventDefault();
        return setTimeout(function() {
          return e.target.value = val + " / ";
        });
      }
    };
    formatForwardExpiry = function(e) {
      var digit, val;
      digit = String.fromCharCode(e.which);
      if (!/^\d+$/.test(digit)) {
        return;
      }
      val = e.target.value;
      if (/^\d\d$/.test(val)) {
        return e.target.value = val + " / ";
      }
    };
    formatForwardSlashAndSpace = function(e) {
      var val, which;
      which = String.fromCharCode(e.which);
      if (!(which === '/' || which === ' ')) {
        return;
      }
      val = e.target.value;
      if (/^\d$/.test(val) && val !== '0') {
        return e.target.value = "0" + val + " / ";
      }
    };
    formatBackExpiry = function(e) {
      var cursor, value;
      value = e.target.value;
      if (e.which !== 8) {
        return;
      }
      cursor = _getCaretPos(e.target);
      if (cursor && cursor !== value.length) {
        return;
      }
      if (/\d\s\/\s$/.test(value)) {
        e.preventDefault();
        return setTimeout(function() {
          return e.target.value = value.replace(/\d\s\/\s$/, '');
        });
      }
    };
    reFormatCVC = function(e) {
      var cursor;
      if (e.target.value === "") {
        return;
      }
      cursor = _getCaretPos(e.target);
      e.target.value = replaceFullWidthChars(e.target.value).replace(/\D/g, '').slice(0, 4);
      if ((cursor != null) && e.type !== 'change') {
        return e.target.setSelectionRange(cursor, cursor);
      }
    };
    restrictNumeric = function(e) {
      var input;
      if (e.metaKey || e.ctrlKey) {
        return;
      }
      if (e.which === 0) {
        return;
      }
      if (e.which < 33) {
        return;
      }
      input = String.fromCharCode(e.which);
      if (!/^\d+$/.test(input)) {
        return e.preventDefault();
      }
    };
    restrictCardNumber = function(e) {
      var card, digit, maxLength, value;
      digit = String.fromCharCode(e.which);
      if (!/^\d+$/.test(digit)) {
        return;
      }
      if (hasTextSelected(e.target)) {
        return;
      }
      value = (e.target.value + digit).replace(/\D/g, '');
      card = cardFromNumber(value);
      maxLength = card ? card.length[card.length.length - 1] : 16;
      if (value.length > maxLength) {
        return e.preventDefault();
      }
    };
    restrictExpiry = function(e) {
      var digit, value;
      digit = String.fromCharCode(e.which);
      if (!/^\d+$/.test(digit)) {
        return;
      }
      if (hasTextSelected(e.target)) {
        return;
      }
      value = e.target.value + digit;
      value = value.replace(/\D/g, '');
      if (value.length > 6) {
        return e.preventDefault();
      }
    };
    restrictCVC = function(e) {
      var digit, val;
      digit = String.fromCharCode(e.which);
      if (!/^\d+$/.test(digit)) {
        return;
      }
      if (hasTextSelected(e.target)) {
        return;
      }
      val = e.target.value + digit;
      if (val.length > 4) {
        return e.preventDefault();
      }
    };
    payform.cvcInput = function(input) {
      _on(input, 'keypress', restrictNumeric);
      _on(input, 'keypress', restrictCVC);
      _on(input, 'paste', reFormatCVC);
      _on(input, 'change', reFormatCVC);
      return _on(input, 'input', reFormatCVC);
    };
    payform.expiryInput = function(input) {
      _on(input, 'keypress', restrictNumeric);
      _on(input, 'keypress', restrictExpiry);
      _on(input, 'keypress', formatCardExpiry);
      _on(input, 'keypress', formatForwardSlashAndSpace);
      _on(input, 'keypress', formatForwardExpiry);
      _on(input, 'keydown', formatBackExpiry);
      _on(input, 'change', reFormatExpiry);
      return _on(input, 'input', reFormatExpiry);
    };
    payform.cardNumberInput = function(input) {
      _on(input, 'keypress', restrictNumeric);
      _on(input, 'keypress', restrictCardNumber);
      _on(input, 'keypress', formatCardNumber);
      _on(input, 'keydown', formatBackCardNumber);
      _on(input, 'paste', reFormatCardNumber);
      _on(input, 'change', reFormatCardNumber);
      return _on(input, 'input', reFormatCardNumber);
    };
    payform.numericInput = function(input) {
      _on(input, 'keypress', restrictNumeric);
      _on(input, 'paste', restrictNumeric);
      _on(input, 'change', restrictNumeric);
      return _on(input, 'input', restrictNumeric);
    };
    payform.parseCardExpiry = function(value) {
      var month, prefix, ref, year;
      value = value.replace(/\s/g, '');
      ref = value.split('/', 2), month = ref[0], year = ref[1];
      if ((year != null ? year.length : void 0) === 2 && /^\d+$/.test(year)) {
        prefix = (new Date).getFullYear();
        prefix = prefix.toString().slice(0, 2);
        year = prefix + year;
      }
      month = parseInt(month, 10);
      year = parseInt(year, 10);
      return {
        month: month,
        year: year
      };
    };
    payform.validateCardNumber = function(num) {
      var card, ref;
      num = (num + '').replace(/\s+|-/g, '');
      if (!/^\d+$/.test(num)) {
        return false;
      }
      card = cardFromNumber(num);
      if (!card) {
        return false;
      }
      return (ref = num.length, indexOf.call(card.length, ref) >= 0) && (card.luhn === false || luhnCheck(num));
    };
    payform.validateCardExpiry = function(month, year) {
      var currentTime, expiry, ref;
      if (typeof month === 'object' && 'month' in month) {
        ref = month, month = ref.month, year = ref.year;
      }
      if (!(month && year)) {
        return false;
      }
      month = String(month).trim();
      year = String(year).trim();
      if (!/^\d+$/.test(month)) {
        return false;
      }
      if (!/^\d+$/.test(year)) {
        return false;
      }
      if (!((1 <= month && month <= 12))) {
        return false;
      }
      if (year.length === 2) {
        if (year < 70) {
          year = "20" + year;
        } else {
          year = "19" + year;
        }
      }
      if (year.length !== 4) {
        return false;
      }
      expiry = new Date(year, month);
      currentTime = new Date;
      expiry.setMonth(expiry.getMonth() - 1);
      expiry.setMonth(expiry.getMonth() + 1, 1);
      return expiry > currentTime;
    };
    payform.validateCardCVC = function(cvc, type) {
      var card, ref;
      cvc = String(cvc).trim();
      if (!/^\d+$/.test(cvc)) {
        return false;
      }
      card = cardFromType(type);
      if (card != null) {
        return ref = cvc.length, indexOf.call(card.cvcLength, ref) >= 0;
      } else {
        return cvc.length >= 3 && cvc.length <= 4;
      }
    };
    payform.parseCardType = function(num) {
      var ref;
      if (!num) {
        return null;
      }
      return ((ref = cardFromNumber(num)) != null ? ref.type : void 0) || null;
    };
    payform.formatCardNumber = function(num) {
      var card, groups, ref, upperLength;
      num = replaceFullWidthChars(num);
      num = num.replace(/\D/g, '');
      card = cardFromNumber(num);
      if (!card) {
        return num;
      }
      upperLength = card.length[card.length.length - 1];
      num = num.slice(0, upperLength);
      if (card.format.global) {
        return (ref = num.match(card.format)) != null ? ref.join(' ') : void 0;
      } else {
        groups = card.format.exec(num);
        if (groups == null) {
          return;
        }
        groups.shift();
        groups = groups.filter(Boolean);
        return groups.join(' ');
      }
    };
    payform.formatCardExpiry = function(expiry) {
      var mon, parts, sep, year;
      expiry = replaceFullWidthChars(expiry);
      parts = expiry.match(/^\D*(\d{1,2})(\D+)?(\d{1,4})?/);
      if (!parts) {
        return '';
      }
      mon = parts[1] || '';
      sep = parts[2] || '';
      year = parts[3] || '';
      if (year.length > 0) {
        sep = ' / ';
      } else if (sep === ' /') {
        mon = mon.substring(0, 1);
        sep = '';
      } else if (mon.length === 2 || sep.length > 0) {
        sep = ' / ';
      } else if (mon.length === 1 && (mon !== '0' && mon !== '1')) {
        mon = "0" + mon;
        sep = ' / ';
      }
      return mon + sep + year;
    };
    return payform;
  });

}).call(this);
