@idonatedev/idonate-sdk 1.2.0-dev17 → 1.2.0-dev19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,11 +16,12 @@ const styles_1 = require("./styles");
16
16
  const tokenizer_constants_1 = require("./tokenizer-constants");
17
17
  const tokenizer_utils_1 = require("./tokenizer-utils");
18
18
  class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
19
- constructor(iframe, iframeUrl, containerId, mode = 'credit_card', enableTestMode = false, layout = 'single-line') {
19
+ constructor(iframe, iframeUrl, containerId, mode = 'credit_card', enableTestMode = false, layout = 'single-line', customPlaceholders) {
20
20
  super();
21
21
  this.iframeUrl = iframeUrl;
22
22
  this.containerId = containerId;
23
23
  this.layout = layout;
24
+ this.customPlaceholders = customPlaceholders;
24
25
  this.enableTestMode = false;
25
26
  this.acceptAutoToken = true;
26
27
  this.mode = mode;
@@ -57,7 +58,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
57
58
  const mergedStyles = (0, styles_1.mergeStyles)(styles_1.DEFAULT_UNIFIED_STYLES, container.styling);
58
59
  const iframeUrl = CardConnectTokenizer.generateIframeUrl(baseUrl, resolvedContainer);
59
60
  const iframe = CardConnectTokenizer.createIframe(iframeUrl, mergedStyles);
60
- const tokenizer = new CardConnectTokenizer(iframe, iframeUrl, container.containerId, container.mode || 'credit_card', container.enableTestMode || false, effectiveLayout);
61
+ const tokenizer = new CardConnectTokenizer(iframe, iframeUrl, container.containerId, container.mode || 'credit_card', container.enableTestMode || false, effectiveLayout, container.placeholders);
61
62
  tokenizer.createInternalElements(resolvedContainer);
62
63
  yield tokenizer.init();
63
64
  return tokenizer;
@@ -80,19 +81,32 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
80
81
  }
81
82
  }
82
83
  createCreditCardFields(containerEl, mergedStyles) {
84
+ var _a;
83
85
  this.iframe.style.width = '100%';
86
+ const hasLabels = ((_a = mergedStyles.label) === null || _a === void 0 ? void 0 : _a.show) &&
87
+ (this.layout === 'two-line' || this.layout === 'responsive');
88
+ const inputHeight = mergedStyles.input.height || '40px';
84
89
  if (this.layout === 'two-line' || this.layout === 'responsive') {
85
- const inputHeight = mergedStyles.input.height || '40px';
86
- this.iframe.style.height = `calc((${inputHeight}) * 2 + 10px)`;
90
+ if (hasLabels) {
91
+ const labelFontPx = (0, tokenizer_utils_1.cssLengthToPixels)(mergedStyles.label.fontSize);
92
+ const labelMarginPx = parseFloat(mergedStyles.label.marginBottom) || 0;
93
+ const labelRowHeight = Math.ceil(labelFontPx * 1.2) + labelMarginPx;
94
+ const extraHeight = Math.ceil(labelRowHeight * 2) + 34;
95
+ this.iframe.style.height = `calc(${inputHeight} * 2 + ${extraHeight}px)`;
96
+ }
97
+ else {
98
+ this.iframe.style.height = `calc((${inputHeight}) * 2 + 10px)`;
99
+ }
87
100
  }
88
101
  else {
89
- this.iframe.style.height = mergedStyles.input.height || '40px';
102
+ this.iframe.style.height = inputHeight;
90
103
  }
91
104
  this.iframe.style.border = 'none';
92
105
  this.iframe.style.display = 'block';
93
106
  containerEl.appendChild(this.iframe);
94
107
  }
95
108
  createBankAccountFields(containerEl, mergedStyles) {
109
+ var _a, _b, _c, _d;
96
110
  this.iframe.style.display = 'none';
97
111
  if (this.layout === 'two-line') {
98
112
  this.accountTypeEl = (0, tokenizer_utils_1.createAccountTypeSelect)(`${this.containerId}-account-type`);
@@ -101,45 +115,45 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
101
115
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.accountType.minWidth,
102
116
  });
103
117
  this.applyInputStyles(this.accountTypeEl, mergedStyles, 'left');
104
- containerEl.appendChild(this.accountTypeEl);
105
- this.routingNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-routing`, 'text', 'Routing *', 9);
118
+ containerEl.appendChild((0, tokenizer_utils_1.wrapFieldWithLabel)(this.accountTypeEl, 'Account Type', mergedStyles));
119
+ this.routingNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-routing`, 'text', ((_a = this.customPlaceholders) === null || _a === void 0 ? void 0 : _a.routingNumber) || 'Routing *', 9);
106
120
  Object.assign(this.routingNumberEl.style, {
107
121
  flex: '1',
108
122
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.routingNumber.minWidth,
109
123
  });
110
124
  this.applyInputStyles(this.routingNumberEl, mergedStyles, 'right');
111
- containerEl.appendChild(this.routingNumberEl);
112
- this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', 'Account Number *', 17);
125
+ containerEl.appendChild((0, tokenizer_utils_1.wrapFieldWithLabel)(this.routingNumberEl, 'Routing Number', mergedStyles));
126
+ this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', ((_b = this.customPlaceholders) === null || _b === void 0 ? void 0 : _b.accountNumber) || 'Account Number *', 17);
113
127
  Object.assign(this.accountNumberEl.style, {
114
128
  flex: '1',
115
129
  flexBasis: '100%',
116
130
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.accountNumber.minWidth,
117
131
  });
118
132
  this.applyInputStyles(this.accountNumberEl, mergedStyles);
119
- containerEl.appendChild(this.accountNumberEl);
133
+ containerEl.appendChild((0, tokenizer_utils_1.wrapFieldWithLabel)(this.accountNumberEl, 'Account Number', mergedStyles));
120
134
  }
121
135
  else {
122
- this.routingNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-routing`, 'text', 'Routing *', 9);
136
+ this.routingNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-routing`, 'text', ((_c = this.customPlaceholders) === null || _c === void 0 ? void 0 : _c.routingNumber) || 'Routing *', 9);
123
137
  Object.assign(this.routingNumberEl.style, {
124
138
  flex: tokenizer_constants_1.BANK_FIELD_FLEX.routingNumber.flex,
125
139
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.routingNumber.minWidth,
126
140
  });
127
141
  this.applyInputStyles(this.routingNumberEl, mergedStyles, 'left');
128
- containerEl.appendChild(this.routingNumberEl);
129
- this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', 'Account Number *', 17);
142
+ containerEl.appendChild((0, tokenizer_utils_1.wrapFieldWithLabel)(this.routingNumberEl, 'Routing Number', mergedStyles));
143
+ this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', ((_d = this.customPlaceholders) === null || _d === void 0 ? void 0 : _d.accountNumber) || 'Account Number *', 17);
130
144
  Object.assign(this.accountNumberEl.style, {
131
145
  flex: tokenizer_constants_1.BANK_FIELD_FLEX.accountNumber.flex,
132
146
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.accountNumber.minWidth,
133
147
  });
134
148
  this.applyInputStyles(this.accountNumberEl, mergedStyles, 'middle');
135
- containerEl.appendChild(this.accountNumberEl);
149
+ containerEl.appendChild((0, tokenizer_utils_1.wrapFieldWithLabel)(this.accountNumberEl, 'Account Number', mergedStyles));
136
150
  this.accountTypeEl = (0, tokenizer_utils_1.createAccountTypeSelect)(`${this.containerId}-account-type`);
137
151
  Object.assign(this.accountTypeEl.style, {
138
152
  flex: tokenizer_constants_1.BANK_FIELD_FLEX.accountType.flex,
139
153
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.accountType.minWidth,
140
154
  });
141
155
  this.applyInputStyles(this.accountTypeEl, mergedStyles, 'right');
142
- containerEl.appendChild(this.accountTypeEl);
156
+ containerEl.appendChild((0, tokenizer_utils_1.wrapFieldWithLabel)(this.accountTypeEl, 'Account Type', mergedStyles));
143
157
  }
144
158
  this.mergedStyles = mergedStyles;
145
159
  this.currentValidationState = {
@@ -237,8 +251,6 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
237
251
  window.addEventListener('message', this.messageHandler);
238
252
  this.iframe.onload = () => {
239
253
  clearTimeout(timeout);
240
- if (this.enableTestMode && this.mode === 'credit_card') {
241
- }
242
254
  this.emit('ready');
243
255
  resolve();
244
256
  };
@@ -684,7 +696,34 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
684
696
  this.emit('validation', this.currentValidationState);
685
697
  }
686
698
  }
699
+ static measureCvvLabelMarginLeft(monthWidth, yearWidth, cvvMarginLeft) {
700
+ var _a;
701
+ const targetCvvX = monthWidth + 4 + 4 + yearWidth + 12 + cvvMarginLeft;
702
+ const host = document.createElement('div');
703
+ host.style.cssText =
704
+ 'position:absolute;visibility:hidden;left:0;top:0;width:9999px;height:0;overflow:hidden;';
705
+ document.body.appendChild(host);
706
+ const shadow = host.attachShadow({ mode: 'open' });
707
+ shadow.innerHTML = [
708
+ '<input type="text" size="2" style="margin-right:4px">',
709
+ '<label>/</label>',
710
+ '<input type="text" size="4" style="margin-left:4px;margin-right:12px">',
711
+ '<input type="tel" size="5">',
712
+ ].join('');
713
+ const inputs = shadow.querySelectorAll('input');
714
+ const hostX = host.getBoundingClientRect().x;
715
+ const cvvRect = (_a = inputs[2]) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
716
+ const defaultCvvX = cvvRect ? cvvRect.x - hostX : -1;
717
+ document.body.removeChild(host);
718
+ if (defaultCvvX <= 0 || defaultCvvX > 500) {
719
+ console.warn('[CardConnect] CVV measurement out of range, using fallback margin-left:100px');
720
+ return 100;
721
+ }
722
+ const predictedLabelLeft = defaultCvvX + 8;
723
+ return Math.round(targetCvvX - predictedLabelLeft);
724
+ }
687
725
  static generateIframeUrl(baseUrl, container) {
726
+ var _a, _b, _c, _d, _e, _f, _g, _h;
688
727
  const params = new URLSearchParams({
689
728
  invalidinputevent: 'true',
690
729
  enhancedresponse: 'true',
@@ -693,30 +732,46 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
693
732
  formatinput: 'true',
694
733
  unique: 'true',
695
734
  norsa: 'true',
696
- placeholder: 'Card Number *',
697
- placeholdercvv: 'CVV',
735
+ placeholder: ((_a = container.placeholders) === null || _a === void 0 ? void 0 : _a.cardNumber) || 'Card Number *',
736
+ placeholdercvv: ((_b = container.placeholders) === null || _b === void 0 ? void 0 : _b.cvv) || 'CVV',
698
737
  invalidcreditcardevent: 'true',
699
738
  invalidexpiry: 'true',
700
739
  invalidcvv: 'true',
701
740
  });
741
+ const mergedStyles = (0, styles_1.mergeStyles)(styles_1.DEFAULT_UNIFIED_STYLES, container.styling);
742
+ const showLabelsInUrl = ((_c = mergedStyles.label) === null || _c === void 0 ? void 0 : _c.show) && container.layout === 'two-line';
743
+ if (showLabelsInUrl) {
744
+ params.set('cardlabel', 'Card Number');
745
+ params.set('expirylabel', 'Expiration');
746
+ params.set('cvvlabel', 'CVV');
747
+ }
748
+ else {
749
+ params.set('cardlabel', '');
750
+ params.set('expirylabel', '');
751
+ params.set('cvvlabel', '');
752
+ }
702
753
  if (container.layout === 'two-line') {
703
754
  params.set('useexpiryfield', 'true');
704
755
  params.set('orientation', 'horizontal');
705
- params.set('placeholdermonth', 'MM');
706
- params.set('placeholderyear', 'YYYY');
756
+ params.set('placeholdermonth', ((_d = container.placeholders) === null || _d === void 0 ? void 0 : _d.expiryMonth) || 'MM');
757
+ params.set('placeholderyear', ((_e = container.placeholders) === null || _e === void 0 ? void 0 : _e.expiryYear) || 'YYYY');
707
758
  }
708
759
  else {
709
760
  params.set('orientation', 'horizontal');
710
- params.set('placeholdermonth', 'MM');
711
- params.set('placeholderyear', 'YYYY');
761
+ params.set('placeholdermonth', ((_f = container.placeholders) === null || _f === void 0 ? void 0 : _f.expiryMonth) || 'MM');
762
+ params.set('placeholderyear', ((_g = container.placeholders) === null || _g === void 0 ? void 0 : _g.expiryYear) || 'YYYY');
712
763
  }
713
- const mergedStyles = (0, styles_1.mergeStyles)(styles_1.DEFAULT_UNIFIED_STYLES, container.styling);
714
764
  const isDesktopSafari = typeof navigator !== 'undefined' &&
715
765
  /Safari/.test(navigator.userAgent) &&
716
766
  /Apple Computer/.test(navigator.vendor) &&
717
767
  !/Chrome/.test(navigator.userAgent) &&
718
768
  !/Mobile/.test(navigator.userAgent);
719
- const cssString = CardConnectTokenizer.generateCardConnectCss(mergedStyles, (container.layout || 'single-line'), isDesktopSafari);
769
+ const showLabels = ((_h = mergedStyles.label) === null || _h === void 0 ? void 0 : _h.show) && container.layout === 'two-line';
770
+ let cvvLabelMarginLeft;
771
+ if (showLabels && typeof document !== 'undefined') {
772
+ cvvLabelMarginLeft = CardConnectTokenizer.measureCvvLabelMarginLeft(60, 80, 40);
773
+ }
774
+ const cssString = CardConnectTokenizer.generateCardConnectCss(mergedStyles, (container.layout || 'single-line'), isDesktopSafari, cvvLabelMarginLeft);
720
775
  const queryPairs = [];
721
776
  params.forEach((value, key) => {
722
777
  queryPairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
@@ -731,7 +786,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
731
786
  iframe.style.width = '100%';
732
787
  iframe.style.height = styles.input.height;
733
788
  iframe.style.border = 'none';
734
- iframe.style.overflow = 'hidden';
789
+ iframe.style.overflow = '';
735
790
  iframe.style.display = 'block';
736
791
  iframe.style.minWidth = '0';
737
792
  if (styles.container) {
@@ -749,18 +804,30 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
749
804
  iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
750
805
  return iframe;
751
806
  }
752
- static generateCardConnectCss(styles, layout = 'single-line', isDesktopSafari = false) {
753
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
807
+ static generateCardConnectCss(styles, layout = 'single-line', isDesktopSafari = false, cvvLabelMarginLeft) {
808
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
754
809
  const css = [];
755
- if (layout === 'two-line') {
756
- css.push('html,form,body{margin:0;padding:0;}');
810
+ const showLabelsInIframe = ((_a = styles.label) === null || _a === void 0 ? void 0 : _a.show) && layout === 'two-line';
811
+ if (showLabelsInIframe) {
812
+ const labelCss = `font-size:${styles.label.fontSize};font-weight:${styles.label.fontWeight};font-family:${styles.label.fontFamily};color:${styles.label.color};margin-bottom:${styles.label.marginBottom}`;
813
+ css.push(`label{${labelCss};}`);
814
+ css.push(`label[for="ccexpiryfieldyear"]{display:none;}`);
815
+ css.push(`br{line-height:0;font-size:0;margin:0;}`);
816
+ css.push(`input,select{margin-top:0;margin-bottom:0;}`);
817
+ }
818
+ else {
757
819
  css.push('label{display:none;}');
758
- css.push('br{display:none;}');
820
+ }
821
+ if (layout === 'two-line') {
822
+ css.push('html,body{margin:0;padding:0;overflow:hidden;}');
823
+ css.push('form{margin:0;padding:0;width:100%;box-sizing:border-box;}');
824
+ if (!showLabelsInIframe) {
825
+ css.push('br{display:none;}');
826
+ }
759
827
  }
760
828
  else {
761
829
  css.push('body{margin:0;padding:0;display:flex;align-items:center;}');
762
830
  css.push('form{margin:0;padding:0;display:flex;width:100%;align-items:center;}');
763
- css.push('label{display:none;}');
764
831
  css.push('br{display:none;}');
765
832
  }
766
833
  const isConnected = styles.container.gap === '0';
@@ -793,13 +860,22 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
793
860
  }
794
861
  if (layout === 'two-line') {
795
862
  css.push('input#ccnumfield{width:100%;display:block;margin-bottom:8px;}');
796
- const twoLinePadding = ((_a = styles.twoLine) === null || _a === void 0 ? void 0 : _a.padding) || ((_b = styles.input) === null || _b === void 0 ? void 0 : _b.padding) || '10px';
797
- const twoLineFontSize = ((_c = styles.twoLine) === null || _c === void 0 ? void 0 : _c.fontSize) || ((_d = styles.input) === null || _d === void 0 ? void 0 : _d.fontSize) || '14px';
798
- const twoLineTextAlign = ((_e = styles.twoLine) === null || _e === void 0 ? void 0 : _e.textAlign) || 'left';
799
- css.push(`input#ccexpiryfieldmonth{width:25%;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
800
- css.push(`input#ccexpiryfieldyear{width:35%;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
801
- css.push(`input#cccvvfield{width:calc(40% - 16px);padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};margin-left:-4px;}`);
802
- if ((_f = styles.input) === null || _f === void 0 ? void 0 : _f.borderRadius) {
863
+ const twoLinePadding = ((_b = styles.twoLine) === null || _b === void 0 ? void 0 : _b.padding) || ((_c = styles.input) === null || _c === void 0 ? void 0 : _c.padding) || '10px';
864
+ const twoLineFontSize = ((_d = styles.twoLine) === null || _d === void 0 ? void 0 : _d.fontSize) || ((_e = styles.input) === null || _e === void 0 ? void 0 : _e.fontSize) || '14px';
865
+ const twoLineTextAlign = ((_f = styles.twoLine) === null || _f === void 0 ? void 0 : _f.textAlign) || 'left';
866
+ if (showLabelsInIframe) {
867
+ css.push(`input#ccexpiryfieldmonth{width:60px;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
868
+ css.push(`input#ccexpiryfieldyear{width:80px;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
869
+ css.push(`input#cccvvfield{width:80px;margin-left:40px;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
870
+ const labelMargin = cvvLabelMarginLeft !== null && cvvLabelMarginLeft !== void 0 ? cvvLabelMarginLeft : 100;
871
+ css.push(`label#cccvvlabel{margin-left:${labelMargin}px;}`);
872
+ }
873
+ else {
874
+ css.push(`input#ccexpiryfieldmonth{min-width:4em;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
875
+ css.push(`input#ccexpiryfieldyear{min-width:4em;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
876
+ css.push(`input#cccvvfield{min-width:4em;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
877
+ }
878
+ if ((_g = styles.input) === null || _g === void 0 ? void 0 : _g.borderRadius) {
803
879
  css.push(`input{border-radius:${styles.input.borderRadius};}`);
804
880
  }
805
881
  }
@@ -808,7 +884,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
808
884
  css.push('select#ccexpirymonth{width:16%;margin:0;margin-right:-12px;}');
809
885
  css.push('select#ccexpiryyear{width:20%;}');
810
886
  css.push('input#cccvvfield{width:20%;margin:0;margin-left:-12px;}');
811
- if ((_g = styles.input) === null || _g === void 0 ? void 0 : _g.borderRadius) {
887
+ if ((_h = styles.input) === null || _h === void 0 ? void 0 : _h.borderRadius) {
812
888
  css.push(`input#ccnumfield{border-radius:${styles.input.borderRadius} 0 0 ${styles.input.borderRadius};border-right:none;}`);
813
889
  css.push('select#ccexpirymonth{border-radius:0;border-right:none;}');
814
890
  css.push('select#ccexpiryyear{border-radius:0;border-right:none;}');
@@ -826,7 +902,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
826
902
  css.push(`select#ccexpirymonth{width:15%;margin-right:${marginRight};}`);
827
903
  css.push(`select#ccexpiryyear{width:20%;margin-right:${marginRight};}`);
828
904
  css.push('input#cccvvfield{width:15%;}');
829
- if ((_h = styles.input) === null || _h === void 0 ? void 0 : _h.borderRadius) {
905
+ if ((_j = styles.input) === null || _j === void 0 ? void 0 : _j.borderRadius) {
830
906
  css.push(`input,select{border-radius:${styles.input.borderRadius};}`);
831
907
  }
832
908
  }
@@ -842,7 +918,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
842
918
  if (focusStyles.length > 0) {
843
919
  css.push(`input:focus{${focusStyles.join(';')};}`);
844
920
  css.push(`select:focus{${focusStyles.join(';')};}`);
845
- if (isConnected && ((_j = styles.input) === null || _j === void 0 ? void 0 : _j.border)) {
921
+ if (isConnected && ((_k = styles.input) === null || _k === void 0 ? void 0 : _k.border)) {
846
922
  css.push(`input#ccnumfield:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
847
923
  css.push(`select#ccexpirymonth:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
848
924
  css.push(`select#ccexpiryyear:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
@@ -6,6 +6,7 @@ export declare class SpreedlyTokenizer extends Tokenizer {
6
6
  private environmentKey;
7
7
  private containerId;
8
8
  private layout;
9
+ private customPlaceholders?;
9
10
  private spreedly;
10
11
  private currentValidationState;
11
12
  private mergedStyles;
@@ -63,6 +64,7 @@ export declare class SpreedlyTokenizer extends Tokenizer {
63
64
  private updateOverallValidation;
64
65
  private handleFieldEvent;
65
66
  private createInternalElements;
67
+ private appendField;
66
68
  private createCreditCardFields;
67
69
  private createBankAccountFields;
68
70
  private createUSBankAccountFields;
@@ -21,11 +21,12 @@ const tokenizer_utils_1 = require("./tokenizer-utils");
21
21
  const SPREEDLY_SCRIPT_URL = 'https://core.spreedly.com/iframe/iframe-v1.min.js';
22
22
  const SPREEDLY_SCRIPT_ID = 'spreedly-iframe-script';
23
23
  class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
24
- constructor(environmentKey, containerId, styles, mode = 'credit_card', bankCountry = 'US', enableTestMode = false, layout = 'single-line', responsiveBreakpoint = tokenizer_constants_1.RESPONSIVE_BREAKPOINT) {
24
+ constructor(environmentKey, containerId, styles, mode = 'credit_card', bankCountry = 'US', enableTestMode = false, layout = 'single-line', responsiveBreakpoint = tokenizer_constants_1.RESPONSIVE_BREAKPOINT, customPlaceholders) {
25
25
  super();
26
26
  this.environmentKey = environmentKey;
27
27
  this.containerId = containerId;
28
28
  this.layout = layout;
29
+ this.customPlaceholders = customPlaceholders;
29
30
  this.bankCountry = 'US';
30
31
  this.enableTestMode = false;
31
32
  this.effectiveLayout = 'single-line';
@@ -80,7 +81,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
80
81
  if (!environmentKey) {
81
82
  throw new Error('Spreedly environment key is required. Provide it via gateway.config.environmentKey or client config.');
82
83
  }
83
- const tokenizer = new SpreedlyTokenizer(environmentKey, container.containerId, container.styling, container.mode || 'credit_card', container.bankCountry || 'US', container.enableTestMode || false, container.layout || 'single-line', (_b = container.responsiveBreakpoint) !== null && _b !== void 0 ? _b : tokenizer_constants_1.RESPONSIVE_BREAKPOINT);
84
+ const tokenizer = new SpreedlyTokenizer(environmentKey, container.containerId, container.styling, container.mode || 'credit_card', container.bankCountry || 'US', container.enableTestMode || false, container.layout || 'single-line', (_b = container.responsiveBreakpoint) !== null && _b !== void 0 ? _b : tokenizer_constants_1.RESPONSIVE_BREAKPOINT, container.placeholders);
84
85
  tokenizer.organizationId = config.organizationId;
85
86
  tokenizer.embedId = config.embedId;
86
87
  tokenizer.clientConfig = config.clientConfig;
@@ -139,11 +140,12 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
139
140
  reject(new Error('Spreedly initialization timeout'));
140
141
  }, tokenizer_constants_1.INIT_TIMEOUT);
141
142
  this.spreedly.on('ready', () => {
143
+ var _a, _b;
142
144
  clearTimeout(timeout);
143
- this.spreedly.setPlaceholder('number', tokenizer_constants_1.PLACEHOLDERS.cardNumber);
145
+ this.spreedly.setPlaceholder('number', ((_a = this.customPlaceholders) === null || _a === void 0 ? void 0 : _a.cardNumber) || tokenizer_constants_1.PLACEHOLDERS.cardNumber);
144
146
  this.spreedly.setFieldType('number', 'text');
145
147
  this.spreedly.setNumberFormat('prettyFormat');
146
- this.spreedly.setPlaceholder('cvv', tokenizer_constants_1.PLACEHOLDERS.cvv);
148
+ this.spreedly.setPlaceholder('cvv', ((_b = this.customPlaceholders) === null || _b === void 0 ? void 0 : _b.cvv) || tokenizer_constants_1.PLACEHOLDERS.cvv);
147
149
  this.spreedly.setFieldType('cvv', 'text');
148
150
  this.applyUnifiedStyles();
149
151
  if (this.enableTestMode &&
@@ -259,7 +261,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
259
261
  minWidth: tokenizer_constants_1.CANADIAN_BANK_FIELD_FLEX.accountType.minWidth,
260
262
  });
261
263
  this.applyInputStyles(this.accountTypeEl, this.mergedStyles, 'left');
262
- container.appendChild(this.accountTypeEl);
264
+ this.appendField(container, this.accountTypeEl, 'Account Type');
263
265
  this.institutionNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-institution`, 'text', 'Inst *', 3);
264
266
  Object.assign(this.institutionNumberEl.style, {
265
267
  flex: '1',
@@ -276,7 +278,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
276
278
  }
277
279
  this.updateBankAccountValidation();
278
280
  });
279
- container.appendChild(this.institutionNumberEl);
281
+ this.appendField(container, this.institutionNumberEl, 'Institution');
280
282
  this.transitNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-transit`, 'text', 'Transit *', 5);
281
283
  Object.assign(this.transitNumberEl.style, {
282
284
  flex: '2',
@@ -293,7 +295,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
293
295
  }
294
296
  this.updateBankAccountValidation();
295
297
  });
296
- container.appendChild(this.transitNumberEl);
298
+ this.appendField(container, this.transitNumberEl, 'Transit');
297
299
  this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', 'Account Number *');
298
300
  Object.assign(this.accountNumberEl.style, {
299
301
  flex: '1',
@@ -311,7 +313,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
311
313
  }
312
314
  this.updateBankAccountValidation();
313
315
  });
314
- container.appendChild(this.accountNumberEl);
316
+ this.appendField(container, this.accountNumberEl, 'Account Number');
315
317
  }
316
318
  else {
317
319
  this.institutionNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-institution`, 'text', 'Inst *', 3);
@@ -330,7 +332,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
330
332
  }
331
333
  this.updateBankAccountValidation();
332
334
  });
333
- container.appendChild(this.institutionNumberEl);
335
+ this.appendField(container, this.institutionNumberEl, 'Institution');
334
336
  this.transitNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-transit`, 'text', 'Transit *', 5);
335
337
  Object.assign(this.transitNumberEl.style, {
336
338
  flex: tokenizer_constants_1.CANADIAN_BANK_FIELD_FLEX.transitNumber.flex,
@@ -347,7 +349,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
347
349
  }
348
350
  this.updateBankAccountValidation();
349
351
  });
350
- container.appendChild(this.transitNumberEl);
352
+ this.appendField(container, this.transitNumberEl, 'Transit');
351
353
  this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', 'Account Number *');
352
354
  Object.assign(this.accountNumberEl.style, {
353
355
  flex: tokenizer_constants_1.CANADIAN_BANK_FIELD_FLEX.accountNumber.flex,
@@ -364,14 +366,14 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
364
366
  }
365
367
  this.updateBankAccountValidation();
366
368
  });
367
- container.appendChild(this.accountNumberEl);
369
+ this.appendField(container, this.accountNumberEl, 'Account Number');
368
370
  this.accountTypeEl = (0, tokenizer_utils_1.createAccountTypeSelect)(`${this.containerId}-account-type`);
369
371
  Object.assign(this.accountTypeEl.style, {
370
372
  flex: tokenizer_constants_1.CANADIAN_BANK_FIELD_FLEX.accountType.flex,
371
373
  minWidth: tokenizer_constants_1.CANADIAN_BANK_FIELD_FLEX.accountType.minWidth,
372
374
  });
373
375
  this.applyInputStyles(this.accountTypeEl, this.mergedStyles, 'right');
374
- container.appendChild(this.accountTypeEl);
376
+ this.appendField(container, this.accountTypeEl, 'Account Type');
375
377
  }
376
378
  }
377
379
  tokenizeBankAccountInternal(bankData) {
@@ -844,7 +846,11 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
844
846
  this.createCreditCardFields(container);
845
847
  }
846
848
  }
849
+ appendField(container, field, labelText) {
850
+ container.appendChild((0, tokenizer_utils_1.wrapFieldWithLabel)(field, labelText, this.mergedStyles));
851
+ }
847
852
  createCreditCardFields(container) {
853
+ var _a, _b;
848
854
  if (this.layout === 'responsive') {
849
855
  this.effectiveLayout = this.determineLayout(container.offsetWidth);
850
856
  }
@@ -857,8 +863,8 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
857
863
  cardNumberDiv.style.height = this.mergedStyles.input.height;
858
864
  cardNumberDiv.style.flexBasis = '100%';
859
865
  this.cardNumberDiv = cardNumberDiv;
860
- container.appendChild(cardNumberDiv);
861
- this.expiryEl = (0, tokenizer_utils_1.createInputElement)(this.expiryId, 'text', tokenizer_constants_1.PLACEHOLDERS.expiry, tokenizer_constants_1.FIELD_CONSTRAINTS.expiry.maxLength);
866
+ this.appendField(container, cardNumberDiv, 'Card Number');
867
+ this.expiryEl = (0, tokenizer_utils_1.createInputElement)(this.expiryId, 'text', ((_a = this.customPlaceholders) === null || _a === void 0 ? void 0 : _a.expiry) || tokenizer_constants_1.PLACEHOLDERS.expiry, tokenizer_constants_1.FIELD_CONSTRAINTS.expiry.maxLength);
862
868
  Object.assign(this.expiryEl.style, {
863
869
  flex: '1',
864
870
  minWidth: tokenizer_constants_1.FIELD_FLEX.expiry.minWidth,
@@ -878,18 +884,18 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
878
884
  this.validateExpiry();
879
885
  this.updateOverallValidation();
880
886
  });
881
- container.appendChild(this.expiryEl);
887
+ this.appendField(container, this.expiryEl, 'Expiration');
882
888
  const cvvDiv = (0, tokenizer_utils_1.createFieldContainer)(this.cvvEl, '1', tokenizer_constants_1.FIELD_FLEX.cvv.minWidth);
883
889
  cvvDiv.style.height = this.mergedStyles.input.height;
884
890
  this.cvvDiv = cvvDiv;
885
- container.appendChild(cvvDiv);
891
+ this.appendField(container, cvvDiv, 'CVV');
886
892
  }
887
893
  else {
888
894
  const cardNumberDiv = (0, tokenizer_utils_1.createFieldContainer)(this.numberEl, tokenizer_constants_1.FIELD_FLEX.cardNumber.flex, tokenizer_constants_1.FIELD_FLEX.cardNumber.minWidth);
889
895
  cardNumberDiv.style.height = this.mergedStyles.input.height;
890
896
  this.cardNumberDiv = cardNumberDiv;
891
- container.appendChild(cardNumberDiv);
892
- this.expiryEl = (0, tokenizer_utils_1.createInputElement)(this.expiryId, 'text', tokenizer_constants_1.PLACEHOLDERS.expiry, tokenizer_constants_1.FIELD_CONSTRAINTS.expiry.maxLength);
897
+ this.appendField(container, cardNumberDiv, 'Card Number');
898
+ this.expiryEl = (0, tokenizer_utils_1.createInputElement)(this.expiryId, 'text', ((_b = this.customPlaceholders) === null || _b === void 0 ? void 0 : _b.expiry) || tokenizer_constants_1.PLACEHOLDERS.expiry, tokenizer_constants_1.FIELD_CONSTRAINTS.expiry.maxLength);
893
899
  Object.assign(this.expiryEl.style, {
894
900
  flex: tokenizer_constants_1.FIELD_FLEX.expiry.flex,
895
901
  minWidth: tokenizer_constants_1.FIELD_FLEX.expiry.minWidth,
@@ -909,11 +915,11 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
909
915
  this.validateExpiry();
910
916
  this.updateOverallValidation();
911
917
  });
912
- container.appendChild(this.expiryEl);
918
+ this.appendField(container, this.expiryEl, 'Expiration');
913
919
  const cvvDiv = (0, tokenizer_utils_1.createFieldContainer)(this.cvvEl, tokenizer_constants_1.FIELD_FLEX.cvv.flex, tokenizer_constants_1.FIELD_FLEX.cvv.minWidth);
914
920
  cvvDiv.style.height = this.mergedStyles.input.height;
915
921
  this.cvvDiv = cvvDiv;
916
- container.appendChild(cvvDiv);
922
+ this.appendField(container, cvvDiv, 'CVV');
917
923
  if (this.layout === 'responsive') {
918
924
  this.applyLayoutStyles(this.effectiveLayout);
919
925
  this.setupResizeObserver();
@@ -929,6 +935,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
929
935
  }
930
936
  }
931
937
  createUSBankAccountFields(container) {
938
+ var _a, _b, _c, _d;
932
939
  if (this.layout === 'two-line') {
933
940
  this.accountTypeEl = (0, tokenizer_utils_1.createAccountTypeSelect)(`${this.containerId}-account-type`);
934
941
  Object.assign(this.accountTypeEl.style, {
@@ -936,8 +943,8 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
936
943
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.accountType.minWidth,
937
944
  });
938
945
  this.applyInputStyles(this.accountTypeEl, this.mergedStyles, 'left');
939
- container.appendChild(this.accountTypeEl);
940
- this.routingNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-routing`, 'text', 'Routing *', 9);
946
+ this.appendField(container, this.accountTypeEl, 'Account Type');
947
+ this.routingNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-routing`, 'text', ((_a = this.customPlaceholders) === null || _a === void 0 ? void 0 : _a.routingNumber) || 'Routing *', 9);
941
948
  Object.assign(this.routingNumberEl.style, {
942
949
  flex: '1',
943
950
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.routingNumber.minWidth,
@@ -953,8 +960,8 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
953
960
  }
954
961
  this.updateBankAccountValidation();
955
962
  });
956
- container.appendChild(this.routingNumberEl);
957
- this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', 'Account Number *', 17);
963
+ this.appendField(container, this.routingNumberEl, 'Routing Number');
964
+ this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', ((_b = this.customPlaceholders) === null || _b === void 0 ? void 0 : _b.accountNumber) || 'Account Number *', 17);
958
965
  Object.assign(this.accountNumberEl.style, {
959
966
  flex: '1',
960
967
  flexBasis: '100%',
@@ -971,10 +978,10 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
971
978
  }
972
979
  this.updateBankAccountValidation();
973
980
  });
974
- container.appendChild(this.accountNumberEl);
981
+ this.appendField(container, this.accountNumberEl, 'Account Number');
975
982
  }
976
983
  else {
977
- this.routingNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-routing`, 'text', 'Routing *', 9);
984
+ this.routingNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-routing`, 'text', ((_c = this.customPlaceholders) === null || _c === void 0 ? void 0 : _c.routingNumber) || 'Routing *', 9);
978
985
  Object.assign(this.routingNumberEl.style, {
979
986
  flex: tokenizer_constants_1.BANK_FIELD_FLEX.routingNumber.flex,
980
987
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.routingNumber.minWidth,
@@ -990,8 +997,8 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
990
997
  }
991
998
  this.updateBankAccountValidation();
992
999
  });
993
- container.appendChild(this.routingNumberEl);
994
- this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', 'Account Number *', 17);
1000
+ this.appendField(container, this.routingNumberEl, 'Routing Number');
1001
+ this.accountNumberEl = (0, tokenizer_utils_1.createInputElement)(`${this.containerId}-account`, 'text', ((_d = this.customPlaceholders) === null || _d === void 0 ? void 0 : _d.accountNumber) || 'Account Number *', 17);
995
1002
  Object.assign(this.accountNumberEl.style, {
996
1003
  flex: tokenizer_constants_1.BANK_FIELD_FLEX.accountNumber.flex,
997
1004
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.accountNumber.minWidth,
@@ -1007,14 +1014,14 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
1007
1014
  }
1008
1015
  this.updateBankAccountValidation();
1009
1016
  });
1010
- container.appendChild(this.accountNumberEl);
1017
+ this.appendField(container, this.accountNumberEl, 'Account Number');
1011
1018
  this.accountTypeEl = (0, tokenizer_utils_1.createAccountTypeSelect)(`${this.containerId}-account-type`);
1012
1019
  Object.assign(this.accountTypeEl.style, {
1013
1020
  flex: tokenizer_constants_1.BANK_FIELD_FLEX.accountType.flex,
1014
1021
  minWidth: tokenizer_constants_1.BANK_FIELD_FLEX.accountType.minWidth,
1015
1022
  });
1016
1023
  this.applyInputStyles(this.accountTypeEl, this.mergedStyles, 'right');
1017
- container.appendChild(this.accountTypeEl);
1024
+ this.appendField(container, this.accountTypeEl, 'Account Type');
1018
1025
  }
1019
1026
  }
1020
1027
  updateBankAccountValidation() {
@@ -37,17 +37,30 @@ exports.DEFAULT_UNIFIED_STYLES = {
37
37
  fontSize: '',
38
38
  textAlign: '',
39
39
  },
40
+ label: {
41
+ show: false,
42
+ fontSize: '0.875rem',
43
+ fontWeight: 'bold',
44
+ fontFamily: '',
45
+ color: '#333',
46
+ marginBottom: '0.25rem',
47
+ },
40
48
  };
41
49
  function mergeStyles(defaults, userStyles) {
42
50
  if (!userStyles)
43
51
  return defaults;
44
- return {
52
+ const merged = {
45
53
  input: Object.assign(Object.assign({}, defaults.input), userStyles.input),
46
54
  focus: Object.assign(Object.assign({}, defaults.focus), userStyles.focus),
47
55
  error: Object.assign(Object.assign({}, defaults.error), userStyles.error),
48
56
  container: Object.assign(Object.assign({}, defaults.container), userStyles.container),
49
57
  twoLine: Object.assign(Object.assign({}, defaults.twoLine), userStyles.twoLine),
58
+ label: Object.assign(Object.assign({}, defaults.label), userStyles.label),
50
59
  };
60
+ if (!merged.label.fontFamily) {
61
+ merged.label.fontFamily = merged.input.fontFamily;
62
+ }
63
+ return merged;
51
64
  }
52
65
  function getContainerStylesForLayout(baseStyles, layout = 'single-line') {
53
66
  if (layout === 'two-line') {
@@ -7,6 +7,8 @@ export declare function formatExpiryInput(value: string): string;
7
7
  export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage: string): Promise<T>;
8
8
  export declare function getConnectedBorderRadius(baseRadius: string, position: 'left' | 'middle' | 'right', isConnected: boolean): string;
9
9
  export declare function addFocusHandlers(element: HTMLElement, styles: TokenizerStylingComplete, position?: 'left' | 'middle' | 'right', onFocus?: () => void, onBlur?: () => void): void;
10
+ export declare function createFieldLabel(text: string, forId: string, styles: TokenizerStylingComplete): HTMLLabelElement;
11
+ export declare function wrapFieldWithLabel(field: HTMLElement, labelText: string, styles: TokenizerStylingComplete): HTMLElement;
10
12
  export declare function createFieldContainer(id: string, flex: string, minWidth?: string, maxWidth?: string): HTMLDivElement;
11
13
  export declare function createInputElement(id: string, type: string, placeholder: string, maxLength?: number): HTMLInputElement;
12
14
  export declare function validateRoutingNumber(routingNumber: string): boolean;
@@ -17,3 +19,4 @@ export declare function validateInstitutionNumber(institutionNumber: string): bo
17
19
  export declare function validateTransitNumber(transitNumber: string): boolean;
18
20
  export declare function validateCanadianAccountNumber(accountNumber: string): boolean;
19
21
  export declare function formatCanadianRoutingNumber(institutionNumber: string, transitNumber: string): string;
22
+ export declare function cssLengthToPixels(value: string): number;