skeuocard-rails 0.1.0 → 1.0.0beta
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.
- checksums.yaml +4 -4
- data/vendor/assets/images/card-flip-arrow.png +0 -0
- data/vendor/assets/images/card-front-background.png +0 -0
- data/vendor/assets/images/card-invalid-indicator.png +0 -0
- data/vendor/assets/images/card-valid-indicator.png +0 -0
- data/vendor/assets/images/error-pointer.png +0 -0
- data/vendor/assets/images/issuers/visa-simple-front.png +0 -0
- data/vendor/assets/images/products/generic-front.png +0 -0
- data/vendor/assets/images/products/jcb-front.png +0 -0
- data/vendor/assets/images/products/maestro-front.png +0 -0
- data/vendor/assets/images/products/mastercard-front.png +0 -0
- data/vendor/assets/images/products/unionpay-front.png +0 -0
- data/vendor/assets/images/products/visa-front.png +0 -0
- data/vendor/assets/javascripts/skeuocard.js +890 -763
- data/vendor/assets/stylesheets/{src/_browser_hacks.scss → _browser_hacks.scss} +24 -4
- data/vendor/assets/stylesheets/{src/_cards.scss → _cards.scss} +203 -80
- data/vendor/assets/stylesheets/{src/_util.scss → _util.scss} +0 -0
- data/vendor/assets/stylesheets/{src/skeuocard.reset.scss → skeuocard.reset.scss} +4 -0
- data/vendor/assets/stylesheets/{src/skeuocard.scss → skeuocard.scss} +29 -18
- metadata +13 -9
@@ -1,5 +1,3 @@
|
|
1
|
-
// Generated by CoffeeScript 1.3.3
|
2
|
-
|
3
1
|
/*
|
4
2
|
"Skeuocard" -- A Skeuomorphic Credit-Card Input Enhancement
|
5
3
|
@description Skeuocard is a skeuomorphic credit card input plugin, supporting
|
@@ -13,13 +11,14 @@
|
|
13
11
|
|
14
12
|
|
15
13
|
(function() {
|
16
|
-
var
|
17
|
-
__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; },
|
14
|
+
var $, Skeuocard, visaProduct,
|
18
15
|
__slice = [].slice,
|
19
|
-
|
20
|
-
|
16
|
+
__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; };
|
17
|
+
|
18
|
+
$ = jQuery;
|
21
19
|
|
22
20
|
Skeuocard = (function() {
|
21
|
+
Skeuocard.currentDate = new Date();
|
23
22
|
|
24
23
|
function Skeuocard(el, opts) {
|
25
24
|
var optDefaults;
|
@@ -31,54 +30,69 @@
|
|
31
30
|
underlyingFields: {}
|
32
31
|
};
|
33
32
|
this._inputViews = {};
|
33
|
+
this._inputViewsByFace = {
|
34
|
+
front: [],
|
35
|
+
back: []
|
36
|
+
};
|
34
37
|
this._tabViews = {};
|
35
|
-
this.
|
36
|
-
this.
|
37
|
-
this.issuerShortname = void 0;
|
38
|
-
this._cardProductNeedsLayout = true;
|
39
|
-
this.acceptedCardProducts = {};
|
38
|
+
this._state = {};
|
39
|
+
this.product = null;
|
40
40
|
this.visibleFace = 'front';
|
41
|
-
this._initialValidationState = {};
|
42
|
-
this._validationState = {
|
43
|
-
number: false,
|
44
|
-
exp: false,
|
45
|
-
name: false,
|
46
|
-
cvc: false
|
47
|
-
};
|
48
|
-
this._faceFillState = {
|
49
|
-
front: false,
|
50
|
-
back: false
|
51
|
-
};
|
52
41
|
optDefaults = {
|
53
42
|
debug: false,
|
54
|
-
acceptedCardProducts:
|
43
|
+
acceptedCardProducts: null,
|
55
44
|
cardNumberPlaceholderChar: 'X',
|
56
45
|
genericPlaceholder: "XXXX XXXX XXXX XXXX",
|
57
46
|
typeInputSelector: '[name="cc_type"]',
|
58
47
|
numberInputSelector: '[name="cc_number"]',
|
59
|
-
|
48
|
+
expMonthInputSelector: '[name="cc_exp_month"]',
|
49
|
+
expYearInputSelector: '[name="cc_exp_year"]',
|
60
50
|
nameInputSelector: '[name="cc_name"]',
|
61
51
|
cvcInputSelector: '[name="cc_cvc"]',
|
62
|
-
currentDate: new Date(),
|
63
52
|
initialValues: {},
|
64
53
|
validationState: {},
|
65
54
|
strings: {
|
66
|
-
hiddenFaceFillPrompt: "Click here to<br
|
55
|
+
hiddenFaceFillPrompt: "<strong>Click here</strong> to <br>fill in the other side.",
|
67
56
|
hiddenFaceErrorWarning: "There's a problem on the other side.",
|
68
|
-
hiddenFaceSwitchPrompt: "
|
57
|
+
hiddenFaceSwitchPrompt: "Forget something?<br> Flip the card over."
|
69
58
|
}
|
70
59
|
};
|
71
60
|
this.options = $.extend(optDefaults, opts);
|
72
61
|
this._conformDOM();
|
73
|
-
this.
|
74
|
-
this.
|
75
|
-
this.
|
76
|
-
this._flipToInvalidSide();
|
62
|
+
this._bindInputEvents();
|
63
|
+
this._importImplicitOptions();
|
64
|
+
this.render();
|
77
65
|
}
|
78
66
|
|
67
|
+
Skeuocard.prototype._log = function() {
|
68
|
+
var msg;
|
69
|
+
msg = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
70
|
+
if ((typeof console !== "undefined" && console !== null ? console.log : void 0) && !!this.options.debug) {
|
71
|
+
if (this.options.debug != null) {
|
72
|
+
return console.log.apply(console, ["[skeuocard]"].concat(__slice.call(msg)));
|
73
|
+
}
|
74
|
+
}
|
75
|
+
};
|
76
|
+
|
77
|
+
Skeuocard.prototype.trigger = function() {
|
78
|
+
var args, _ref;
|
79
|
+
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
80
|
+
return (_ref = this.el.container).trigger.apply(_ref, args);
|
81
|
+
};
|
82
|
+
|
83
|
+
Skeuocard.prototype.bind = function() {
|
84
|
+
var args, _ref;
|
85
|
+
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
86
|
+
return (_ref = this.el.container).bind.apply(_ref, args);
|
87
|
+
};
|
88
|
+
|
89
|
+
/*
|
90
|
+
Transform the elements within the container, conforming the DOM so that it
|
91
|
+
becomes styleable, and that the underlying inputs are hidden.
|
92
|
+
*/
|
93
|
+
|
94
|
+
|
79
95
|
Skeuocard.prototype._conformDOM = function() {
|
80
|
-
var el, fieldName, fieldValue, _ref, _ref1, _ref2,
|
81
|
-
_this = this;
|
82
96
|
this.el.container.removeClass('no-js');
|
83
97
|
this.el.container.addClass("skeuocard js");
|
84
98
|
this.el.container.find("> :not(input,select,textarea)").remove();
|
@@ -86,419 +100,367 @@
|
|
86
100
|
this.el.underlyingFields = {
|
87
101
|
type: this.el.container.find(this.options.typeInputSelector),
|
88
102
|
number: this.el.container.find(this.options.numberInputSelector),
|
89
|
-
|
103
|
+
expMonth: this.el.container.find(this.options.expMonthInputSelector),
|
104
|
+
expYear: this.el.container.find(this.options.expYearInputSelector),
|
90
105
|
name: this.el.container.find(this.options.nameInputSelector),
|
91
106
|
cvc: this.el.container.find(this.options.cvcInputSelector)
|
92
107
|
};
|
93
|
-
|
94
|
-
for (fieldName in _ref) {
|
95
|
-
fieldValue = _ref[fieldName];
|
96
|
-
this.el.underlyingFields[fieldName].val(fieldValue);
|
97
|
-
}
|
98
|
-
_ref1 = this.el.underlyingFields;
|
99
|
-
for (fieldName in _ref1) {
|
100
|
-
el = _ref1[fieldName];
|
101
|
-
this.options.initialValues[fieldName] = el.val();
|
102
|
-
}
|
103
|
-
_ref2 = this.el.underlyingFields;
|
104
|
-
for (fieldName in _ref2) {
|
105
|
-
el = _ref2[fieldName];
|
106
|
-
if (this.options.validationState[fieldName] === false || el.hasClass('invalid')) {
|
107
|
-
this._initialValidationState[fieldName] = false;
|
108
|
-
if (!el.hasClass('invalid')) {
|
109
|
-
el.addClass('invalid');
|
110
|
-
}
|
111
|
-
}
|
112
|
-
}
|
113
|
-
this.el.underlyingFields.number.bind("change", function(e) {
|
114
|
-
_this._inputViews.number.setValue(_this._getUnderlyingValue('number'));
|
115
|
-
return _this.render();
|
116
|
-
});
|
117
|
-
this.el.underlyingFields.exp.bind("change", function(e) {
|
118
|
-
_this._inputViews.exp.setValue(_this._getUnderlyingValue('exp'));
|
119
|
-
return _this.render();
|
120
|
-
});
|
121
|
-
this.el.underlyingFields.name.bind("change", function(e) {
|
122
|
-
_this._inputViews.exp.setValue(_this._getUnderlyingValue('name'));
|
123
|
-
return _this.render();
|
124
|
-
});
|
125
|
-
this.el.underlyingFields.cvc.bind("change", function(e) {
|
126
|
-
_this._inputViews.exp.setValue(_this._getUnderlyingValue('cvc'));
|
127
|
-
return _this.render();
|
128
|
-
});
|
129
|
-
this.el.surfaceFront = $("<div>").attr({
|
108
|
+
this.el.front = $("<div>").attr({
|
130
109
|
"class": "face front"
|
131
110
|
});
|
132
|
-
this.el.
|
111
|
+
this.el.back = $("<div>").attr({
|
133
112
|
"class": "face back"
|
134
113
|
});
|
135
114
|
this.el.cardBody = $("<div>").attr({
|
136
115
|
"class": "card-body"
|
137
116
|
});
|
138
|
-
this.el.
|
139
|
-
this.el.
|
117
|
+
this.el.front.appendTo(this.el.cardBody);
|
118
|
+
this.el.back.appendTo(this.el.cardBody);
|
140
119
|
this.el.cardBody.appendTo(this.el.container);
|
141
|
-
this._tabViews.front = new
|
142
|
-
|
143
|
-
this.el.surfaceFront.prepend(this._tabViews.front.el);
|
144
|
-
this.el.surfaceBack.prepend(this._tabViews.back.el);
|
145
|
-
this._tabViews.front.hide();
|
146
|
-
this._tabViews.back.hide();
|
147
|
-
this._tabViews.front.el.click(function() {
|
148
|
-
return _this.flip();
|
120
|
+
this._tabViews.front = new Skeuocard.prototype.FlipTabView(this, 'front', {
|
121
|
+
strings: this.options.strings
|
149
122
|
});
|
150
|
-
this._tabViews.back.
|
151
|
-
|
123
|
+
this._tabViews.back = new Skeuocard.prototype.FlipTabView(this, 'back', {
|
124
|
+
strings: this.options.strings
|
152
125
|
});
|
126
|
+
this.el.front.prepend(this._tabViews.front.el);
|
127
|
+
this.el.back.prepend(this._tabViews.back.el);
|
128
|
+
this._tabViews.front.hide();
|
129
|
+
this._tabViews.back.hide();
|
130
|
+
this._inputViews = {
|
131
|
+
number: new this.SegmentedCardNumberInputView(),
|
132
|
+
exp: new this.ExpirationInputView({
|
133
|
+
currentDate: this.options.currentDate
|
134
|
+
}),
|
135
|
+
name: new this.TextInputView({
|
136
|
+
"class": "cc-name",
|
137
|
+
placeholder: "YOUR NAME"
|
138
|
+
}),
|
139
|
+
cvc: new this.TextInputView({
|
140
|
+
"class": "cc-cvc",
|
141
|
+
placeholder: "XXX",
|
142
|
+
requireMaxLength: true
|
143
|
+
})
|
144
|
+
};
|
145
|
+
this._inputViews.number.el.addClass('cc-number');
|
146
|
+
this._inputViews.number.el.appendTo(this.el.front);
|
147
|
+
this._inputViews.name.el.appendTo(this.el.front);
|
148
|
+
this._inputViews.exp.el.addClass('cc-exp');
|
149
|
+
this._inputViews.exp.el.appendTo(this.el.front);
|
150
|
+
this._inputViews.cvc.el.appendTo(this.el.back);
|
153
151
|
return this.el.container;
|
154
152
|
};
|
155
153
|
|
156
|
-
|
157
|
-
|
154
|
+
/*
|
155
|
+
Import implicit initialization options from the DOM. Brings in things like
|
156
|
+
the accepted card type, initial validation state, existing values, etc.
|
157
|
+
*/
|
158
|
+
|
159
|
+
|
160
|
+
Skeuocard.prototype._importImplicitOptions = function() {
|
161
|
+
var fieldEl, fieldName, _initialExp, _ref,
|
158
162
|
_this = this;
|
159
|
-
|
163
|
+
_ref = this.el.underlyingFields;
|
164
|
+
for (fieldName in _ref) {
|
165
|
+
fieldEl = _ref[fieldName];
|
166
|
+
if (this.options.initialValues[fieldName] == null) {
|
167
|
+
this.options.initialValues[fieldName] = fieldEl.val();
|
168
|
+
} else {
|
169
|
+
this.options.initialValues[fieldName] = this.options.initialValues[fieldName].toString();
|
170
|
+
this._setUnderlyingValue(fieldName, this.options.initialValues[fieldName]);
|
171
|
+
}
|
172
|
+
if (this.options.initialValues[fieldName].length > 0) {
|
173
|
+
this._state['initiallyFilled'] = true;
|
174
|
+
}
|
175
|
+
if (this.options.validationState[fieldName] == null) {
|
176
|
+
this.options.validationState[fieldName] = !fieldEl.hasClass('invalid');
|
177
|
+
}
|
178
|
+
}
|
179
|
+
if (this.options.acceptedCardProducts == null) {
|
180
|
+
this.options.acceptedCardProducts = [];
|
160
181
|
this.el.underlyingFields.type.find('option').each(function(i, _el) {
|
161
|
-
var
|
182
|
+
var el, shortname;
|
162
183
|
el = $(_el);
|
163
|
-
|
164
|
-
return _this.options.acceptedCardProducts.push(
|
184
|
+
shortname = el.attr('data-sc-type') || el.attr('value');
|
185
|
+
return _this.options.acceptedCardProducts.push(shortname);
|
165
186
|
});
|
166
187
|
}
|
167
|
-
|
168
|
-
|
169
|
-
if (_ref = product.companyShortname, __indexOf.call(this.options.acceptedCardProducts, _ref) >= 0) {
|
170
|
-
this.acceptedCardProducts[matcher] = product;
|
171
|
-
}
|
188
|
+
if (this.options.initialValues.number.length > 0) {
|
189
|
+
this.set('number', this.options.initialValues.number);
|
172
190
|
}
|
173
|
-
|
191
|
+
if (this.options.initialValues.name.length > 0) {
|
192
|
+
this.set('name', this.options.initialValues.name);
|
193
|
+
}
|
194
|
+
if (this.options.initialValues.cvc.length > 0) {
|
195
|
+
this.set('cvc', this.options.initialValues.cvc);
|
196
|
+
}
|
197
|
+
if (this.options.initialValues.expYear.length > 0 && this.options.initialValues.expMonth.length > 0) {
|
198
|
+
_initialExp = new Date(parseInt(this.options.initialValues.expYear), parseInt(this.options.initialValues.expMonth) - 1, 1);
|
199
|
+
this.set('exp', _initialExp);
|
200
|
+
}
|
201
|
+
this._updateValidationForFace('front');
|
202
|
+
return this._updateValidationForFace('back');
|
174
203
|
};
|
175
204
|
|
176
|
-
Skeuocard.prototype.
|
177
|
-
|
178
|
-
|
179
|
-
matchedProduct = this.getProductForNumber(number);
|
180
|
-
matchedProductIdentifier = (matchedProduct != null ? matchedProduct.companyShortname : void 0) || '';
|
181
|
-
matchedIssuerIdentifier = (matchedProduct != null ? matchedProduct.issuerShortname : void 0) || '';
|
182
|
-
if ((this.productShortname !== matchedProductIdentifier) || (this.issuerShortname !== matchedIssuerIdentifier)) {
|
183
|
-
this.productShortname = matchedProductIdentifier;
|
184
|
-
this.issuerShortname = matchedIssuerIdentifier;
|
185
|
-
this.product = matchedProduct;
|
186
|
-
this._cardProductNeedsLayout = true;
|
187
|
-
this.trigger('productWillChange.skeuocard', [this, this.productShortname, matchedProductIdentifier]);
|
188
|
-
this._log("Triggering render because product changed.");
|
189
|
-
this.render();
|
190
|
-
return this.trigger('productDidChange.skeuocard', [this, this.productShortname, matchedProductIdentifier]);
|
191
|
-
}
|
205
|
+
Skeuocard.prototype.set = function(field, newValue) {
|
206
|
+
this._inputViews[field].setValue(newValue);
|
207
|
+
return this._inputViews[field].trigger('valueChanged', this._inputViews[field]);
|
192
208
|
};
|
193
209
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
210
|
+
/*
|
211
|
+
Bind interaction events to their appropriate handlers.
|
212
|
+
*/
|
213
|
+
|
214
|
+
|
215
|
+
Skeuocard.prototype._bindInputEvents = function() {
|
216
|
+
var _expirationChange,
|
217
|
+
_this = this;
|
218
|
+
this.el.underlyingFields.number.bind("change", function(e) {
|
219
|
+
_this._inputViews.number.setValue(_this._getUnderlyingValue('number'));
|
220
|
+
return _this.render();
|
199
221
|
});
|
200
|
-
|
201
|
-
|
202
|
-
|
222
|
+
_expirationChange = function(e) {
|
223
|
+
var month, year;
|
224
|
+
month = parseInt(_this._getUnderlyingValue('expMonth'));
|
225
|
+
year = parseInt(_this._getUnderlyingValue('expYear'));
|
226
|
+
_this._inputViews.exp.setValue(new Date(year, month - 1));
|
227
|
+
return _this.render();
|
228
|
+
};
|
229
|
+
this.el.underlyingFields.expMonth.bind("change", _expirationChange);
|
230
|
+
this.el.underlyingFields.expYear.bind("change", _expirationChange);
|
231
|
+
this.el.underlyingFields.name.bind("change", function(e) {
|
232
|
+
_this._inputViews.exp.setValue(_this._getUnderlyingValue('name'));
|
233
|
+
return _this.render();
|
203
234
|
});
|
204
|
-
this.
|
205
|
-
|
206
|
-
|
207
|
-
requireMaxLength: true
|
235
|
+
this.el.underlyingFields.cvc.bind("change", function(e) {
|
236
|
+
_this._inputViews.exp.setValue(_this._getUnderlyingValue('cvc'));
|
237
|
+
return _this.render();
|
208
238
|
});
|
209
|
-
this._inputViews.number.
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
_this.
|
217
|
-
|
218
|
-
|
239
|
+
this._inputViews.number.bind("change valueChanged", function(e, input) {
|
240
|
+
var cardNumber, matchedProduct, number, previousProduct, _ref, _ref1;
|
241
|
+
cardNumber = input.getValue();
|
242
|
+
_this._setUnderlyingValue('number', cardNumber);
|
243
|
+
_this._updateValidation('number', cardNumber);
|
244
|
+
number = _this._getUnderlyingValue('number');
|
245
|
+
matchedProduct = Skeuocard.prototype.CardProduct.firstMatchingNumber(number);
|
246
|
+
if (!((_ref = _this.product) != null ? _ref.eql(matchedProduct) : void 0)) {
|
247
|
+
_this._log("Product will change:", _this.product, "=>", matchedProduct);
|
248
|
+
if (_ref1 = matchedProduct != null ? matchedProduct.attrs.companyShortname : void 0, __indexOf.call(_this.options.acceptedCardProducts, _ref1) >= 0) {
|
249
|
+
_this.trigger('productWillChange.skeuocard', [_this, _this.product, matchedProduct]);
|
250
|
+
previousProduct = _this.product;
|
251
|
+
_this.el.container.removeClass('unaccepted');
|
252
|
+
_this._renderProduct(matchedProduct);
|
253
|
+
_this.product = matchedProduct;
|
254
|
+
} else if (matchedProduct != null) {
|
255
|
+
_this.trigger('productWillChange.skeuocard', [_this, _this.product, null]);
|
256
|
+
_this.el.container.addClass('unaccepted');
|
257
|
+
_this._renderProduct(null);
|
258
|
+
_this.product = null;
|
259
|
+
} else {
|
260
|
+
_this.trigger('productWillChange.skeuocard', [_this, _this.product, null]);
|
261
|
+
_this.el.container.removeClass('unaccepted');
|
262
|
+
_this._renderProduct(null);
|
263
|
+
_this.product = null;
|
264
|
+
}
|
265
|
+
return _this.trigger('productDidChange.skeuocard', [_this, previousProduct, _this.product]);
|
266
|
+
}
|
219
267
|
});
|
220
|
-
this._inputViews.exp.bind("keyup", function(e, input) {
|
221
|
-
|
222
|
-
|
268
|
+
this._inputViews.exp.bind("keyup valueChanged", function(e, input) {
|
269
|
+
var newDate;
|
270
|
+
newDate = input.getValue();
|
271
|
+
_this._updateValidation('exp', newDate);
|
272
|
+
if (newDate != null) {
|
273
|
+
_this._setUnderlyingValue('expMonth', newDate.getMonth() + 1);
|
274
|
+
return _this._setUnderlyingValue('expYear', newDate.getFullYear());
|
275
|
+
}
|
223
276
|
});
|
224
|
-
this._inputViews.name.bind("keyup", function(e) {
|
225
|
-
|
226
|
-
|
277
|
+
this._inputViews.name.bind("keyup valueChanged", function(e, input) {
|
278
|
+
var value;
|
279
|
+
value = input.getValue();
|
280
|
+
_this._setUnderlyingValue('name', value);
|
281
|
+
return _this._updateValidation('name', value);
|
227
282
|
});
|
228
|
-
this._inputViews.cvc.bind("keyup", function(e) {
|
229
|
-
|
230
|
-
|
283
|
+
this._inputViews.cvc.bind("keyup valueChanged", function(e, input) {
|
284
|
+
var value;
|
285
|
+
value = input.getValue();
|
286
|
+
_this._setUnderlyingValue('cvc', value);
|
287
|
+
return _this._updateValidation('cvc', value);
|
288
|
+
});
|
289
|
+
this.el.container.delegate("input", "keyup keydown", this._handleFieldTab.bind(this));
|
290
|
+
this._tabViews.front.el.click(function() {
|
291
|
+
return _this.flip();
|
292
|
+
});
|
293
|
+
return this._tabViews.back.el.click(function() {
|
294
|
+
return _this.flip();
|
231
295
|
});
|
232
|
-
this._inputViews.number.setValue(this._getUnderlyingValue('number'));
|
233
|
-
this._inputViews.exp.setValue(this._getUnderlyingValue('exp'));
|
234
|
-
this._inputViews.name.el.val(this._getUnderlyingValue('name'));
|
235
|
-
return this._inputViews.cvc.el.val(this._getUnderlyingValue('cvc'));
|
236
|
-
};
|
237
|
-
|
238
|
-
Skeuocard.prototype._log = function() {
|
239
|
-
var msg;
|
240
|
-
msg = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
241
|
-
if ((typeof console !== "undefined" && console !== null ? console.log : void 0) && !!this.options.debug) {
|
242
|
-
if (this.options.debug != null) {
|
243
|
-
return console.log.apply(console, ["[skeuocard]"].concat(__slice.call(msg)));
|
244
|
-
}
|
245
|
-
}
|
246
296
|
};
|
247
297
|
|
248
|
-
Skeuocard.prototype.
|
249
|
-
var
|
250
|
-
if (
|
298
|
+
Skeuocard.prototype._handleFieldTab = function(e) {
|
299
|
+
var backFieldEls, currentFieldEl, frontFieldEls, _currentFace, _oppositeFace;
|
300
|
+
if (e.which === 9) {
|
301
|
+
currentFieldEl = $(e.currentTarget);
|
251
302
|
_oppositeFace = this.visibleFace === 'front' ? 'back' : 'front';
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
_errorCounts[(_ref1 = this.product) != null ? _ref1.layout[fieldName] : void 0]++;
|
303
|
+
_currentFace = this.visibleFace === 'front' ? 'front' : 'back';
|
304
|
+
backFieldEls = this.el[_oppositeFace].find('input');
|
305
|
+
frontFieldEls = this.el[_currentFace].find('input');
|
306
|
+
if (this.visibleFace === 'front' && this.el.front.hasClass('filled') && backFieldEls.length > 0 && frontFieldEls.index(currentFieldEl) === frontFieldEls.length - 1 && !e.shiftKey) {
|
307
|
+
this.flip();
|
308
|
+
backFieldEls.first().focus();
|
309
|
+
e.preventDefault();
|
260
310
|
}
|
261
|
-
if (
|
262
|
-
|
311
|
+
if (this.visibleFace === 'back' && e.shiftKey) {
|
312
|
+
this.flip();
|
313
|
+
backFieldEls.last().focus();
|
314
|
+
e.preventDefault();
|
263
315
|
}
|
264
316
|
}
|
317
|
+
return true;
|
265
318
|
};
|
266
319
|
|
267
|
-
Skeuocard.prototype.
|
268
|
-
var
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
}
|
304
|
-
}
|
305
|
-
} else {
|
306
|
-
this._log("[render]", "Becoming generic.");
|
307
|
-
this._inputViews.exp.clear();
|
308
|
-
this._inputViews.cvc.clear();
|
309
|
-
this._inputViews.exp.hide();
|
310
|
-
this._inputViews.name.hide();
|
311
|
-
this._inputViews.cvc.hide();
|
312
|
-
this._inputViews.number.setGroupings([this.options.genericPlaceholder.length]);
|
313
|
-
/*
|
314
|
-
@_inputViews.number.reconfigure
|
315
|
-
groupings: [@options.genericPlaceholder.length],
|
316
|
-
placeholder: @options.genericPlaceholder
|
317
|
-
*/
|
318
|
-
|
319
|
-
this.el.container.removeClass(function(index, css) {
|
320
|
-
return (css.match(/\bproduct-\S+/g) || []).join(' ');
|
321
|
-
});
|
322
|
-
this.el.container.removeClass(function(index, css) {
|
323
|
-
return (css.match(/\bissuer-\S+/g) || []).join(' ');
|
324
|
-
});
|
325
|
-
}
|
326
|
-
this._cardProductNeedsLayout = false;
|
327
|
-
}
|
328
|
-
this._log("Validation state:", this._validationState);
|
329
|
-
this.showInitialValidationErrors();
|
330
|
-
_oppositeFace = this.visibleFace === 'front' ? 'back' : 'front';
|
331
|
-
_visibleFaceFilled = this._faceFillState[this.visibleFace];
|
332
|
-
_visibleFaceValid = this.isFaceValid(this.visibleFace);
|
333
|
-
_hiddenFaceFilled = this._faceFillState[_oppositeFace];
|
334
|
-
_hiddenFaceValid = this.isFaceValid(_oppositeFace);
|
335
|
-
if (_visibleFaceFilled && !_visibleFaceValid) {
|
336
|
-
this._log("Visible face is filled, but invalid; showing validation errors.");
|
337
|
-
this.showValidationErrors();
|
338
|
-
} else if (!_visibleFaceFilled) {
|
339
|
-
this._log("Visible face hasn't been filled; hiding validation errors.");
|
340
|
-
this.hideValidationErrors();
|
341
|
-
} else {
|
342
|
-
this._log("Visible face has been filled, and is valid.");
|
343
|
-
this.hideValidationErrors();
|
344
|
-
}
|
345
|
-
if (this.visibleFace === 'front' && this.fieldsForFace('back').length > 0) {
|
346
|
-
if (_visibleFaceFilled && _visibleFaceValid && !_hiddenFaceFilled) {
|
347
|
-
this._tabViews.front.prompt(this.options.strings.hiddenFaceFillPrompt, true);
|
348
|
-
} else if (_hiddenFaceFilled && !_hiddenFaceValid) {
|
349
|
-
this._tabViews.front.warn(this.options.strings.hiddenFaceErrorWarning, true);
|
350
|
-
} else if (_hiddenFaceFilled && _hiddenFaceValid) {
|
351
|
-
this._tabViews.front.prompt(this.options.strings.hiddenFaceSwitchPrompt, true);
|
352
|
-
} else {
|
353
|
-
this._tabViews.front.hide();
|
320
|
+
Skeuocard.prototype._updateValidation = function(fieldName, newValue) {
|
321
|
+
var fillStateChanged, isFilled, isFixed, isValid, needsFix, validationStateChanged;
|
322
|
+
if (this.product == null) {
|
323
|
+
return false;
|
324
|
+
}
|
325
|
+
isFilled = this.product[fieldName].isFilled(newValue);
|
326
|
+
needsFix = (this.options.validationState[fieldName] != null) === false;
|
327
|
+
isFixed = (this.options.initialValues[fieldName] != null) && newValue !== this.options.initialValues[fieldName];
|
328
|
+
isValid = this.product[fieldName].isValid(newValue) && ((needsFix && isFixed) || true);
|
329
|
+
fillStateChanged = this._state["" + fieldName + "Filled"] !== isFilled;
|
330
|
+
validationStateChanged = this._state["" + fieldName + "Valid"] !== isValid;
|
331
|
+
if (fillStateChanged) {
|
332
|
+
this.trigger("fieldFillStateWillChange.skeuocard", [this, fieldName, isFilled]);
|
333
|
+
this._inputViews[fieldName].el.toggleClass('filled', isFilled);
|
334
|
+
this._state["" + fieldName + "Filled"] = isFilled;
|
335
|
+
this.trigger("fieldFillStateDidChange.skeuocard", [this, fieldName, isFilled]);
|
336
|
+
}
|
337
|
+
if (validationStateChanged) {
|
338
|
+
this.trigger("fieldValidationStateWillChange.skeuocard", [this, fieldName, isFilled]);
|
339
|
+
this._inputViews[fieldName].el.toggleClass('valid', isValid);
|
340
|
+
this._inputViews[fieldName].el.toggleClass('invalid', !isValid);
|
341
|
+
this._state["" + fieldName + "Valid"] = isValid;
|
342
|
+
this.trigger("fieldValidationStateDidChange.skeuocard", [this, fieldName, isFilled]);
|
343
|
+
}
|
344
|
+
return this._updateValidationForFace(this.visibleFace);
|
345
|
+
};
|
346
|
+
|
347
|
+
Skeuocard.prototype._updateValidationForFace = function(face) {
|
348
|
+
var fieldsFilled, fieldsValid, fillStateChanged, isFilled, isValid, iv, validationStateChanged;
|
349
|
+
fieldsFilled = ((function() {
|
350
|
+
var _i, _len, _ref, _results;
|
351
|
+
_ref = this._inputViewsByFace[face];
|
352
|
+
_results = [];
|
353
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
354
|
+
iv = _ref[_i];
|
355
|
+
_results.push(iv.el.hasClass('filled'));
|
354
356
|
}
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
357
|
+
return _results;
|
358
|
+
}).call(this)).every(Boolean);
|
359
|
+
fieldsValid = ((function() {
|
360
|
+
var _i, _len, _ref, _results;
|
361
|
+
_ref = this._inputViewsByFace[face];
|
362
|
+
_results = [];
|
363
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
364
|
+
iv = _ref[_i];
|
365
|
+
_results.push(iv.el.hasClass('valid'));
|
360
366
|
}
|
367
|
+
return _results;
|
368
|
+
}).call(this)).every(Boolean);
|
369
|
+
isFilled = (fieldsFilled && (this.product != null)) || (this._state['initiallyFilled'] || false);
|
370
|
+
isValid = fieldsValid && (this.product != null);
|
371
|
+
fillStateChanged = this._state["" + face + "Filled"] !== isFilled;
|
372
|
+
validationStateChanged = this._state["" + face + "Valid"] !== isValid;
|
373
|
+
if (fillStateChanged) {
|
374
|
+
this.trigger("faceFillStateWillChange.skeuocard", [this, face, isFilled]);
|
375
|
+
this.el[face].toggleClass('filled', isFilled);
|
376
|
+
this._state["" + face + "Filled"] = isFilled;
|
377
|
+
this.trigger("faceFillStateDidChange.skeuocard", [this, face, isFilled]);
|
378
|
+
}
|
379
|
+
if (validationStateChanged) {
|
380
|
+
this.trigger("faceValidationStateWillChange.skeuocard", [this, face, isValid]);
|
381
|
+
this.el[face].toggleClass('valid', isValid);
|
382
|
+
this.el[face].toggleClass('invalid', !isValid);
|
383
|
+
this._state["" + face + "Valid"] = isValid;
|
384
|
+
return this.trigger("faceValidationStateDidChange.skeuocard", [this, face, isValid]);
|
385
|
+
}
|
386
|
+
};
|
387
|
+
|
388
|
+
/*
|
389
|
+
Assert rendering changes necessary for the current product. Passing a null
|
390
|
+
value instead of a product will revert the card to a generic state.
|
391
|
+
*/
|
392
|
+
|
393
|
+
|
394
|
+
Skeuocard.prototype._renderProduct = function(product) {
|
395
|
+
var destFace, fieldName, focused, view, viewEl, _ref, _ref1,
|
396
|
+
_this = this;
|
397
|
+
this._log("[_renderProduct]", "Rendering product:", product);
|
398
|
+
this.el.container.removeClass(function(index, css) {
|
399
|
+
return (css.match(/\b(product|issuer)-\S+/g) || []).join(' ');
|
400
|
+
});
|
401
|
+
if ((product != null ? product.attrs.companyShortname : void 0) != null) {
|
402
|
+
this.el.container.addClass("product-" + product.attrs.companyShortname);
|
361
403
|
}
|
362
|
-
if (
|
363
|
-
this.el.container.
|
364
|
-
this.el.container.addClass('invalid');
|
365
|
-
} else {
|
366
|
-
this.el.container.addClass('valid');
|
367
|
-
this.el.container.removeClass('invalid');
|
404
|
+
if ((product != null ? product.attrs.issuerShortname : void 0) != null) {
|
405
|
+
this.el.container.addClass("issuer-" + product.attrs.issuerShortname);
|
368
406
|
}
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
407
|
+
this._setUnderlyingValue('type', (product != null ? product.attrs.companyShortname : void 0) || null);
|
408
|
+
this._inputViews.number.setGroupings((product != null ? product.attrs.cardNumberGrouping : void 0) || [this.options.genericPlaceholder.length]);
|
409
|
+
if (product != null) {
|
410
|
+
this._inputViews.exp.reconfigure({
|
411
|
+
pattern: (product != null ? product.attrs.expirationFormat : void 0) || "MM/YY"
|
412
|
+
});
|
413
|
+
this._inputViews.cvc.attr({
|
414
|
+
maxlength: product.attrs.cvcLength,
|
415
|
+
placeholder: new Array(product.attrs.cvcLength + 1).join(this.options.cardNumberPlaceholderChar)
|
416
|
+
});
|
417
|
+
this._inputViewsByFace = {
|
418
|
+
front: [],
|
419
|
+
back: []
|
420
|
+
};
|
421
|
+
focused = $('*:focus');
|
422
|
+
_ref = product.attrs.layout;
|
423
|
+
for (fieldName in _ref) {
|
424
|
+
destFace = _ref[fieldName];
|
425
|
+
this._log("Moving", fieldName, "to", destFace);
|
426
|
+
viewEl = this._inputViews[fieldName].el.detach();
|
427
|
+
viewEl.appendTo(this.el[destFace]);
|
428
|
+
this._inputViewsByFace[destFace].push(this._inputViews[fieldName]);
|
429
|
+
this._inputViews[fieldName].show();
|
382
430
|
}
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
431
|
+
setTimeout(function() {
|
432
|
+
var fieldEl, fieldLength;
|
433
|
+
fieldEl = focused.first();
|
434
|
+
fieldLength = fieldEl[0].maxLength;
|
435
|
+
fieldEl.focus();
|
436
|
+
return fieldEl[0].setSelectionRange(fieldLength, fieldLength);
|
437
|
+
}, 10);
|
438
|
+
} else {
|
439
|
+
_ref1 = this._inputViews;
|
440
|
+
for (fieldName in _ref1) {
|
441
|
+
view = _ref1[fieldName];
|
442
|
+
if (fieldName !== 'number') {
|
443
|
+
view.hide();
|
444
|
+
}
|
397
445
|
}
|
398
446
|
}
|
399
|
-
return
|
447
|
+
return product;
|
400
448
|
};
|
401
449
|
|
402
|
-
Skeuocard.prototype.
|
403
|
-
var fieldName,
|
404
|
-
_ref = this.
|
450
|
+
Skeuocard.prototype._renderValidation = function() {
|
451
|
+
var fieldName, fieldView, _ref, _results;
|
452
|
+
_ref = this._inputViews;
|
405
453
|
_results = [];
|
406
454
|
for (fieldName in _ref) {
|
407
|
-
|
408
|
-
|
409
|
-
_results.push(this._inputViews[fieldName].el.removeClass('invalid'));
|
410
|
-
} else {
|
411
|
-
_results.push(void 0);
|
412
|
-
}
|
455
|
+
fieldView = _ref[fieldName];
|
456
|
+
_results.push(this._updateValidation(fieldName, fieldView.getValue()));
|
413
457
|
}
|
414
458
|
return _results;
|
415
459
|
};
|
416
460
|
|
417
|
-
Skeuocard.prototype.
|
418
|
-
|
419
|
-
|
420
|
-
} else {
|
421
|
-
this.el.underlyingFields[fieldName].addClass('invalid');
|
422
|
-
}
|
423
|
-
return this._validationState[fieldName] = valid;
|
424
|
-
};
|
425
|
-
|
426
|
-
Skeuocard.prototype.isFaceFilled = function(faceName) {
|
427
|
-
var fields, filled, name;
|
428
|
-
fields = this.fieldsForFace(faceName);
|
429
|
-
filled = (function() {
|
430
|
-
var _i, _len, _results;
|
431
|
-
_results = [];
|
432
|
-
for (_i = 0, _len = fields.length; _i < _len; _i++) {
|
433
|
-
name = fields[_i];
|
434
|
-
if (this._inputViews[name].isFilled()) {
|
435
|
-
_results.push(name);
|
436
|
-
}
|
437
|
-
}
|
438
|
-
return _results;
|
439
|
-
}).call(this);
|
440
|
-
if (fields.length > 0) {
|
441
|
-
return filled.length === fields.length;
|
442
|
-
} else {
|
443
|
-
return false;
|
444
|
-
}
|
445
|
-
};
|
446
|
-
|
447
|
-
Skeuocard.prototype.fieldsForFace = function(faceName) {
|
448
|
-
var face, fn, _ref;
|
449
|
-
if ((_ref = this.product) != null ? _ref.layout : void 0) {
|
450
|
-
return (function() {
|
451
|
-
var _ref1, _results;
|
452
|
-
_ref1 = this.product.layout;
|
453
|
-
_results = [];
|
454
|
-
for (fn in _ref1) {
|
455
|
-
face = _ref1[fn];
|
456
|
-
if (face === faceName) {
|
457
|
-
_results.push(fn);
|
458
|
-
}
|
459
|
-
}
|
460
|
-
return _results;
|
461
|
-
}).call(this);
|
462
|
-
}
|
463
|
-
return [];
|
464
|
-
};
|
465
|
-
|
466
|
-
Skeuocard.prototype._updateValidationStateForInputView = function(fieldName) {
|
467
|
-
var field, fieldValid;
|
468
|
-
field = this._inputViews[fieldName];
|
469
|
-
fieldValid = field.isValid() && !(this._initialValidationState[fieldName] === false && field.getValue() === this.options.initialValues[fieldName]);
|
470
|
-
if (fieldValid !== this._validationState[fieldName]) {
|
471
|
-
this.setFieldValidationState(fieldName, fieldValid);
|
472
|
-
this._faceFillState.front = this.isFaceFilled('front');
|
473
|
-
this._faceFillState.back = this.isFaceFilled('back');
|
474
|
-
this.trigger('validationStateDidChange.skeuocard', [this, this._validationState]);
|
475
|
-
this._log("Change in validation for " + fieldName + " triggers re-render.");
|
476
|
-
return this.render();
|
477
|
-
}
|
478
|
-
};
|
479
|
-
|
480
|
-
Skeuocard.prototype.isFaceValid = function(faceName) {
|
481
|
-
var fieldName, valid, _i, _len, _ref;
|
482
|
-
valid = true;
|
483
|
-
_ref = this.fieldsForFace(faceName);
|
484
|
-
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
485
|
-
fieldName = _ref[_i];
|
486
|
-
valid &= this._validationState[fieldName];
|
487
|
-
}
|
488
|
-
return !!valid;
|
489
|
-
};
|
490
|
-
|
491
|
-
Skeuocard.prototype.isValid = function() {
|
492
|
-
return this._validationState.number && this._validationState.exp && this._validationState.name && this._validationState.cvc;
|
493
|
-
};
|
494
|
-
|
495
|
-
Skeuocard.prototype._getUnderlyingValue = function(field) {
|
496
|
-
return this.el.underlyingFields[field].val();
|
497
|
-
};
|
498
|
-
|
499
|
-
Skeuocard.prototype._setUnderlyingValue = function(field, newValue) {
|
500
|
-
this.trigger('change.skeuocard', [this]);
|
501
|
-
return this.el.underlyingFields[field].val(newValue);
|
461
|
+
Skeuocard.prototype.render = function() {
|
462
|
+
this._renderProduct(this.product);
|
463
|
+
return this._renderValidation();
|
502
464
|
};
|
503
465
|
|
504
466
|
Skeuocard.prototype.flip = function() {
|
@@ -506,122 +468,123 @@
|
|
506
468
|
targetFace = this.visibleFace === 'front' ? 'back' : 'front';
|
507
469
|
this.trigger('faceWillBecomeVisible.skeuocard', [this, targetFace]);
|
508
470
|
this.visibleFace = targetFace;
|
509
|
-
this.render();
|
510
471
|
this.el.cardBody.toggleClass('flip');
|
511
|
-
surfaceName = this.visibleFace === 'front' ? '
|
512
|
-
this.el[surfaceName].find('input').first().focus();
|
472
|
+
surfaceName = this.visibleFace === 'front' ? 'front' : 'back';
|
473
|
+
this.el[surfaceName].find('.cc-field').not('.filled').find('input').first().focus();
|
513
474
|
return this.trigger('faceDidBecomeVisible.skeuocard', [this, targetFace]);
|
514
475
|
};
|
515
476
|
|
516
|
-
Skeuocard.prototype.
|
517
|
-
var
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
if (matcher.test(num)) {
|
524
|
-
issuer = this.getIssuerForNumber(num) || {};
|
525
|
-
return $.extend({}, d, issuer);
|
526
|
-
}
|
477
|
+
Skeuocard.prototype._setUnderlyingValue = function(field, newValue) {
|
478
|
+
var fieldEl, remapAttrKey, _newValue,
|
479
|
+
_this = this;
|
480
|
+
fieldEl = this.el.underlyingFields[field];
|
481
|
+
_newValue = (newValue || "").toString();
|
482
|
+
if (fieldEl == null) {
|
483
|
+
throw "Set underlying value of unknown field: " + field + ".";
|
527
484
|
}
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
}
|
485
|
+
this.trigger('change.skeuocard', [this]);
|
486
|
+
if (!fieldEl.is('select')) {
|
487
|
+
return this.el.underlyingFields[field].val(_newValue);
|
488
|
+
} else {
|
489
|
+
remapAttrKey = "data-sc-" + field.toLowerCase();
|
490
|
+
return fieldEl.find('option').each(function(i, _el) {
|
491
|
+
var optionEl;
|
492
|
+
optionEl = $(_el);
|
493
|
+
if (_newValue === (optionEl.attr(remapAttrKey) || optionEl.attr('value'))) {
|
494
|
+
return _this.el.underlyingFields[field].val(optionEl.attr('value'));
|
495
|
+
}
|
496
|
+
});
|
540
497
|
}
|
541
|
-
return void 0;
|
542
498
|
};
|
543
499
|
|
544
|
-
Skeuocard.prototype.
|
545
|
-
var
|
546
|
-
return this.el.underlyingFields
|
547
|
-
var el;
|
548
|
-
el = $(_el);
|
549
|
-
if (shortname === (el.attr('data-card-product-shortname') || el.attr('value'))) {
|
550
|
-
return el.val(el.attr('value'));
|
551
|
-
}
|
552
|
-
});
|
553
|
-
};
|
554
|
-
|
555
|
-
Skeuocard.prototype.trigger = function() {
|
556
|
-
var args, _ref;
|
557
|
-
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
558
|
-
return (_ref = this.el.container).trigger.apply(_ref, args);
|
500
|
+
Skeuocard.prototype._getUnderlyingValue = function(field) {
|
501
|
+
var _ref;
|
502
|
+
return (_ref = this.el.underlyingFields[field]) != null ? _ref.val() : void 0;
|
559
503
|
};
|
560
504
|
|
561
|
-
Skeuocard.prototype.
|
562
|
-
|
563
|
-
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
564
|
-
return (_ref = this.el.container).trigger.apply(_ref, args);
|
505
|
+
Skeuocard.prototype.isValid = function() {
|
506
|
+
return !this.el.front.hasClass('invalid') && !this.el.back.hasClass('invalid');
|
565
507
|
};
|
566
508
|
|
567
509
|
return Skeuocard;
|
568
510
|
|
569
511
|
})();
|
570
512
|
|
513
|
+
window.Skeuocard = Skeuocard;
|
514
|
+
|
571
515
|
/*
|
572
516
|
Skeuocard::FlipTabView
|
573
517
|
Handles rendering of the "flip button" control and its various warning and
|
574
518
|
prompt states.
|
575
|
-
|
576
|
-
TODO: Rebuild this so that it observes events and contains its own logic.
|
577
519
|
*/
|
578
520
|
|
579
521
|
|
580
522
|
Skeuocard.prototype.FlipTabView = (function() {
|
581
|
-
|
582
|
-
|
523
|
+
function FlipTabView(sc, face, opts) {
|
524
|
+
var _this = this;
|
583
525
|
if (opts == null) {
|
584
526
|
opts = {};
|
585
527
|
}
|
528
|
+
this.card = sc;
|
529
|
+
this.face = face;
|
586
530
|
this.el = $("<div class=\"flip-tab " + face + "\"><p></p></div>");
|
587
531
|
this.options = opts;
|
532
|
+
this._state = {};
|
533
|
+
this.card.bind('faceFillStateWillChange.skeuocard', this._faceStateChanged.bind(this));
|
534
|
+
this.card.bind('faceValidationStateWillChange.skeuocard', this._faceValidationChanged.bind(this));
|
535
|
+
this.card.bind('productWillChange.skeuocard', function(e, card, prevProduct, newProduct) {
|
536
|
+
if (newProduct == null) {
|
537
|
+
return _this.hide();
|
538
|
+
}
|
539
|
+
});
|
588
540
|
}
|
589
541
|
|
590
|
-
FlipTabView.prototype.
|
591
|
-
|
542
|
+
FlipTabView.prototype._faceStateChanged = function(e, card, face, isFilled) {
|
543
|
+
var oppositeFace;
|
544
|
+
oppositeFace = face === 'front' ? 'back' : 'front';
|
545
|
+
if (isFilled === true && this.card._inputViewsByFace[oppositeFace].length > 0) {
|
546
|
+
this.show();
|
547
|
+
}
|
548
|
+
if (face !== this.face) {
|
549
|
+
this._state.opposingFaceFilled = isFilled;
|
550
|
+
}
|
551
|
+
if (this._state.opposingFaceFilled !== true) {
|
552
|
+
return this.warn(this.options.strings.hiddenFaceFillPrompt, true);
|
553
|
+
}
|
592
554
|
};
|
593
555
|
|
594
|
-
FlipTabView.prototype.
|
595
|
-
if (
|
596
|
-
|
556
|
+
FlipTabView.prototype._faceValidationChanged = function(e, card, face, isValid) {
|
557
|
+
if (face !== this.face) {
|
558
|
+
this._state.opposingFaceValid = isValid;
|
597
559
|
}
|
560
|
+
if (this._state.opposingFaceValid) {
|
561
|
+
return this.prompt(this.options.strings.hiddenFaceSwitchPrompt);
|
562
|
+
} else {
|
563
|
+
if (this._state.opposingFaceFilled) {
|
564
|
+
return this.warn(this.options.strings.hiddenFaceErrorWarning);
|
565
|
+
} else {
|
566
|
+
return this.warn(this.options.strings.hiddenFaceFillPrompt);
|
567
|
+
}
|
568
|
+
}
|
569
|
+
};
|
570
|
+
|
571
|
+
FlipTabView.prototype._setText = function(text) {
|
572
|
+
return this.el.find('p').first().html(text);
|
573
|
+
};
|
574
|
+
|
575
|
+
FlipTabView.prototype.warn = function(message) {
|
598
576
|
this._resetClasses();
|
599
|
-
this.el.addClass('warn');
|
600
577
|
this._setText(message);
|
601
|
-
this.
|
602
|
-
if (withAnimation) {
|
603
|
-
this.el.removeClass('warn-anim');
|
604
|
-
return this.el.addClass('warn-anim');
|
605
|
-
}
|
578
|
+
return this.el.addClass('warn');
|
606
579
|
};
|
607
580
|
|
608
|
-
FlipTabView.prototype.prompt = function(message
|
609
|
-
if (withAnimation == null) {
|
610
|
-
withAnimation = false;
|
611
|
-
}
|
581
|
+
FlipTabView.prototype.prompt = function(message) {
|
612
582
|
this._resetClasses();
|
613
|
-
this.el.addClass('prompt');
|
614
583
|
this._setText(message);
|
615
|
-
this.
|
616
|
-
if (withAnimation) {
|
617
|
-
this.el.removeClass('valid-anim');
|
618
|
-
return this.el.addClass('valid-anim');
|
619
|
-
}
|
584
|
+
return this.el.addClass('prompt');
|
620
585
|
};
|
621
586
|
|
622
587
|
FlipTabView.prototype._resetClasses = function() {
|
623
|
-
this.el.removeClass('valid-anim');
|
624
|
-
this.el.removeClass('warn-anim');
|
625
588
|
this.el.removeClass('warn');
|
626
589
|
return this.el.removeClass('prompt');
|
627
590
|
};
|
@@ -639,95 +602,29 @@
|
|
639
602
|
})();
|
640
603
|
|
641
604
|
/*
|
642
|
-
Skeuocard::
|
605
|
+
# Skeuocard::SegmentedCardNumberInputView
|
606
|
+
# Provides a reconfigurable segmented input view for credit card numbers.
|
643
607
|
*/
|
644
608
|
|
645
609
|
|
646
|
-
Skeuocard.prototype.TextInputView = (function() {
|
647
|
-
|
648
|
-
function TextInputView() {}
|
649
|
-
|
650
|
-
TextInputView.prototype.bind = function() {
|
651
|
-
var args, _ref;
|
652
|
-
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
653
|
-
return (_ref = this.el).bind.apply(_ref, args);
|
654
|
-
};
|
655
|
-
|
656
|
-
TextInputView.prototype.trigger = function() {
|
657
|
-
var args, _ref;
|
658
|
-
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
659
|
-
return (_ref = this.el).trigger.apply(_ref, args);
|
660
|
-
};
|
661
|
-
|
662
|
-
TextInputView.prototype._getFieldCaretPosition = function(el) {
|
663
|
-
var input, sel, selLength;
|
664
|
-
input = el.get(0);
|
665
|
-
if (input.selectionEnd != null) {
|
666
|
-
return input.selectionEnd;
|
667
|
-
} else if (document.selection) {
|
668
|
-
input.focus();
|
669
|
-
sel = document.selection.createRange();
|
670
|
-
selLength = document.selection.createRange().text.length;
|
671
|
-
sel.moveStart('character', -input.value.length);
|
672
|
-
return selLength;
|
673
|
-
}
|
674
|
-
};
|
675
|
-
|
676
|
-
TextInputView.prototype._setFieldCaretPosition = function(el, pos) {
|
677
|
-
var input, range;
|
678
|
-
input = el.get(0);
|
679
|
-
if (input.createTextRange != null) {
|
680
|
-
range = input.createTextRange();
|
681
|
-
range.move("character", pos);
|
682
|
-
return range.select();
|
683
|
-
} else if (input.selectionStart != null) {
|
684
|
-
input.focus();
|
685
|
-
return input.setSelectionRange(pos, pos);
|
686
|
-
}
|
687
|
-
};
|
688
|
-
|
689
|
-
TextInputView.prototype.show = function() {
|
690
|
-
return this.el.show();
|
691
|
-
};
|
692
|
-
|
693
|
-
TextInputView.prototype.hide = function() {
|
694
|
-
return this.el.hide();
|
695
|
-
};
|
696
|
-
|
697
|
-
TextInputView.prototype.addClass = function() {
|
698
|
-
var args, _ref;
|
699
|
-
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
700
|
-
return (_ref = this.el).addClass.apply(_ref, args);
|
701
|
-
};
|
702
|
-
|
703
|
-
TextInputView.prototype.removeClass = function() {
|
704
|
-
var args, _ref;
|
705
|
-
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
706
|
-
return (_ref = this.el).removeClass.apply(_ref, args);
|
707
|
-
};
|
708
|
-
|
709
|
-
TextInputView.prototype._zeroPadNumber = function(num, places) {
|
710
|
-
var zero;
|
711
|
-
zero = places - num.toString().length + 1;
|
712
|
-
return Array(zero).join("0") + num;
|
713
|
-
};
|
714
|
-
|
715
|
-
return TextInputView;
|
716
|
-
|
717
|
-
})();
|
718
|
-
|
719
610
|
Skeuocard.prototype.SegmentedCardNumberInputView = (function() {
|
720
|
-
|
721
611
|
SegmentedCardNumberInputView.prototype._digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
722
612
|
|
723
|
-
SegmentedCardNumberInputView.prototype.
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
613
|
+
SegmentedCardNumberInputView.prototype._keys = {
|
614
|
+
backspace: 8,
|
615
|
+
tab: 9,
|
616
|
+
enter: 13,
|
617
|
+
del: 46,
|
618
|
+
arrowLeft: 37,
|
619
|
+
arrowUp: 38,
|
620
|
+
arrowRight: 39,
|
621
|
+
arrowDown: 40,
|
622
|
+
arrows: [37, 38, 39, 40],
|
623
|
+
command: 16,
|
624
|
+
alt: 17
|
728
625
|
};
|
729
626
|
|
730
|
-
SegmentedCardNumberInputView.prototype._specialKeys = [8, 9,
|
627
|
+
SegmentedCardNumberInputView.prototype._specialKeys = [8, 9, 13, 46, 37, 38, 39, 40, 16, 17];
|
731
628
|
|
732
629
|
function SegmentedCardNumberInputView(opts) {
|
733
630
|
if (opts == null) {
|
@@ -747,93 +644,124 @@
|
|
747
644
|
}
|
748
645
|
|
749
646
|
SegmentedCardNumberInputView.prototype._buildDOM = function() {
|
647
|
+
var _this = this;
|
750
648
|
this.el = $('<fieldset>');
|
649
|
+
this.el.addClass('cc-field');
|
751
650
|
this.el.delegate("input", "keypress", this._handleGroupKeyPress.bind(this));
|
752
651
|
this.el.delegate("input", "keydown", this._handleGroupKeyDown.bind(this));
|
753
652
|
this.el.delegate("input", "keyup", this._handleGroupKeyUp.bind(this));
|
754
653
|
this.el.delegate("input", "paste", this._handleGroupPaste.bind(this));
|
755
|
-
|
654
|
+
this.el.delegate("input", "change", this._handleGroupChange.bind(this));
|
655
|
+
this.el.delegate("input", "focus", function(e) {
|
656
|
+
return _this.el.addClass('focus');
|
657
|
+
});
|
658
|
+
return this.el.delegate("input", "blur", function(e) {
|
659
|
+
return _this.el.removeClass('focus');
|
660
|
+
});
|
756
661
|
};
|
757
662
|
|
758
663
|
SegmentedCardNumberInputView.prototype._handleGroupKeyDown = function(e) {
|
759
|
-
var currentTarget, inputGroupEl, inputMaxLength, nextInputEl, prevInputEl,
|
664
|
+
var currentTarget, cursorEnd, cursorStart, inputGroupEl, inputMaxLength, nextInputEl, prevInputEl, _ref;
|
760
665
|
if (e.ctrlKey || e.metaKey) {
|
761
666
|
return this._handleModifiedKeyDown(e);
|
762
667
|
}
|
763
668
|
inputGroupEl = $(e.currentTarget);
|
764
669
|
currentTarget = e.currentTarget;
|
765
|
-
|
670
|
+
cursorStart = currentTarget.selectionStart;
|
671
|
+
cursorEnd = currentTarget.selectionEnd;
|
766
672
|
inputMaxLength = currentTarget.maxLength;
|
767
673
|
prevInputEl = inputGroupEl.prevAll('input');
|
768
674
|
nextInputEl = inputGroupEl.nextAll('input');
|
769
|
-
if (e.which === 8 && prevInputEl.length > 0) {
|
770
|
-
if (selectionEnd === 0) {
|
771
|
-
this._focusField(prevInputEl.first(), 'end');
|
772
|
-
}
|
773
|
-
}
|
774
|
-
return true;
|
775
|
-
};
|
776
|
-
|
777
|
-
SegmentedCardNumberInputView.prototype._handleGroupKeyUp = function(e) {
|
778
|
-
var currentTarget, inputGroupEl, inputMaxLength, nextInputEl, selectionEnd, _ref;
|
779
|
-
inputGroupEl = $(e.currentTarget);
|
780
|
-
currentTarget = e.currentTarget;
|
781
|
-
selectionEnd = currentTarget.selectionEnd;
|
782
|
-
inputMaxLength = currentTarget.maxLength;
|
783
|
-
nextInputEl = inputGroupEl.nextAll('input');
|
784
|
-
if (e.ctrlKey || e.metaKey) {
|
785
|
-
return false;
|
786
|
-
}
|
787
|
-
if ((_ref = e.which) === 37 || _ref === 38 || _ref === 39 || _ref === 40) {
|
788
|
-
if (this._state.selectingAll) {
|
789
|
-
this._endSelectAll();
|
790
|
-
}
|
791
|
-
}
|
792
675
|
switch (e.which) {
|
793
|
-
case this.
|
794
|
-
if (
|
676
|
+
case this._keys.backspace:
|
677
|
+
if (prevInputEl.length > 0 && cursorEnd === 0) {
|
678
|
+
this._focusField(prevInputEl.first(), 'end');
|
679
|
+
}
|
680
|
+
break;
|
681
|
+
case this._keys.arrowUp:
|
682
|
+
if (cursorEnd === inputMaxLength) {
|
683
|
+
this._focusField(inputGroupEl, 'start');
|
684
|
+
} else {
|
795
685
|
this._focusField(inputGroupEl.prev(), 'end');
|
796
686
|
}
|
687
|
+
e.preventDefault();
|
797
688
|
break;
|
798
|
-
case this.
|
799
|
-
if (
|
689
|
+
case this._keys.arrowDown:
|
690
|
+
if (cursorEnd === inputMaxLength) {
|
800
691
|
this._focusField(inputGroupEl.next(), 'start');
|
692
|
+
} else {
|
693
|
+
this._focusField(inputGroupEl, 'end');
|
801
694
|
}
|
802
|
-
break;
|
803
|
-
case this._arrowKeys.up:
|
804
|
-
this._focusField(inputGroupEl.next(), 'start');
|
805
695
|
e.preventDefault();
|
806
696
|
break;
|
807
|
-
case this.
|
808
|
-
|
809
|
-
|
697
|
+
case this._keys.arrowLeft:
|
698
|
+
if (cursorEnd === 0) {
|
699
|
+
this._focusField(inputGroupEl.prev(), 'end');
|
700
|
+
e.preventDefault();
|
701
|
+
}
|
702
|
+
break;
|
703
|
+
case this._keys.arrowRight:
|
704
|
+
if (cursorEnd === inputMaxLength) {
|
705
|
+
this._focusField(inputGroupEl.next(), 'start');
|
706
|
+
e.preventDefault();
|
707
|
+
}
|
810
708
|
break;
|
811
709
|
default:
|
812
|
-
if (
|
813
|
-
|
814
|
-
this._focusField(nextInputEl.first(), 'start');
|
815
|
-
} else {
|
816
|
-
e.preventDefault();
|
817
|
-
}
|
710
|
+
if (!(_ref = e.which, __indexOf.call(this._specialKeys, _ref) >= 0) && (cursorStart === inputMaxLength && cursorEnd === inputMaxLength) && nextInputEl.length !== 0) {
|
711
|
+
this._focusField(nextInputEl.first(), 'start');
|
818
712
|
}
|
819
713
|
}
|
820
|
-
this.trigger('change', [this]);
|
821
714
|
return true;
|
822
715
|
};
|
823
716
|
|
824
717
|
SegmentedCardNumberInputView.prototype._handleGroupKeyPress = function(e) {
|
825
|
-
var
|
718
|
+
var inputGroupEl, isDigit, _ref, _ref1;
|
719
|
+
inputGroupEl = $(e.currentTarget);
|
720
|
+
isDigit = (_ref = String.fromCharCode(e.which), __indexOf.call(this._digits, _ref) >= 0);
|
721
|
+
if (e.ctrlKey || e.metaKey) {
|
722
|
+
return true;
|
723
|
+
}
|
724
|
+
if (e.which === 0) {
|
725
|
+
return true;
|
726
|
+
}
|
727
|
+
if ((!e.shiftKey && (_ref1 = e.which, __indexOf.call(this._specialKeys, _ref1) >= 0)) || isDigit) {
|
728
|
+
return true;
|
729
|
+
}
|
730
|
+
e.preventDefault();
|
731
|
+
return false;
|
732
|
+
};
|
733
|
+
|
734
|
+
SegmentedCardNumberInputView.prototype._handleGroupKeyUp = function(e) {
|
735
|
+
var currentTarget, cursorEnd, cursorStart, inputGroupEl, inputMaxLength, nextInputEl, _ref, _ref1, _ref2;
|
826
736
|
inputGroupEl = $(e.currentTarget);
|
827
737
|
currentTarget = e.currentTarget;
|
828
|
-
selectionEnd = currentTarget.selectionEnd;
|
829
738
|
inputMaxLength = currentTarget.maxLength;
|
830
|
-
|
739
|
+
cursorStart = currentTarget.selectionStart;
|
740
|
+
cursorEnd = currentTarget.selectionEnd;
|
831
741
|
nextInputEl = inputGroupEl.nextAll('input');
|
832
|
-
if (e.ctrlKey || e.metaKey
|
742
|
+
if (e.ctrlKey || e.metaKey) {
|
833
743
|
return true;
|
834
|
-
}
|
835
|
-
|
836
|
-
|
744
|
+
}
|
745
|
+
if (this._state.selectingAll && (_ref = e.which, __indexOf.call(this._specialKeys, _ref) >= 0) && e.which !== this._keys.command && e.which !== this._keys.alt) {
|
746
|
+
this._endSelectAll();
|
747
|
+
}
|
748
|
+
if (!(_ref1 = e.which, __indexOf.call(this._specialKeys, _ref1) >= 0) && !(e.shiftKey && e.which === this._keys.tab) && (cursorStart === inputMaxLength && cursorEnd === inputMaxLength) && nextInputEl.length !== 0) {
|
749
|
+
this._focusField(nextInputEl.first(), 'start');
|
750
|
+
}
|
751
|
+
if (!(e.shiftKey && (_ref2 = e.which, __indexOf.call(this._specialKeys, _ref2) >= 0))) {
|
752
|
+
this.trigger('change', [this]);
|
753
|
+
}
|
754
|
+
return true;
|
755
|
+
};
|
756
|
+
|
757
|
+
SegmentedCardNumberInputView.prototype._handleModifiedKeyDown = function(e) {
|
758
|
+
var char;
|
759
|
+
char = String.fromCharCode(e.which);
|
760
|
+
switch (char) {
|
761
|
+
case 'a':
|
762
|
+
case 'A':
|
763
|
+
this._beginSelectAll();
|
764
|
+
return e.preventDefault();
|
837
765
|
}
|
838
766
|
};
|
839
767
|
|
@@ -850,16 +778,6 @@
|
|
850
778
|
}, 50);
|
851
779
|
};
|
852
780
|
|
853
|
-
SegmentedCardNumberInputView.prototype._handleModifiedKeyDown = function(e) {
|
854
|
-
var char;
|
855
|
-
char = String.fromCharCode(e.which);
|
856
|
-
switch (char) {
|
857
|
-
case 'A':
|
858
|
-
this._beginSelectAll();
|
859
|
-
return e.preventDefault();
|
860
|
-
}
|
861
|
-
};
|
862
|
-
|
863
781
|
SegmentedCardNumberInputView.prototype._handleGroupChange = function(e) {
|
864
782
|
return e.stopPropagation();
|
865
783
|
};
|
@@ -870,14 +788,14 @@
|
|
870
788
|
|
871
789
|
SegmentedCardNumberInputView.prototype._beginSelectAll = function() {
|
872
790
|
var fieldEl;
|
873
|
-
if (this.
|
874
|
-
this._state.selectingAll = true;
|
791
|
+
if (!this.el.hasClass('selecting-all')) {
|
875
792
|
this._state.lastGrouping = this.options.groupings;
|
876
|
-
this._state.
|
793
|
+
this._state.lastLength = this.getValue().length;
|
877
794
|
this.setGroupings(this.optDefaults.groupings);
|
878
795
|
this.el.addClass('selecting-all');
|
879
796
|
fieldEl = this.el.find("input");
|
880
|
-
|
797
|
+
fieldEl[0].setSelectionRange(0, fieldEl.val().length);
|
798
|
+
return this._state.selectingAll = true;
|
881
799
|
} else {
|
882
800
|
fieldEl = this.el.find("input");
|
883
801
|
return fieldEl[0].setSelectionRange(0, fieldEl.val().length);
|
@@ -885,14 +803,12 @@
|
|
885
803
|
};
|
886
804
|
|
887
805
|
SegmentedCardNumberInputView.prototype._endSelectAll = function() {
|
888
|
-
if (this.
|
889
|
-
|
806
|
+
if (this.el.hasClass('selecting-all')) {
|
807
|
+
this._state.selectingAll = false;
|
808
|
+
if (this._state.lastLength === this.getValue().length) {
|
890
809
|
this.setGroupings(this._state.lastGrouping);
|
891
|
-
} else {
|
892
|
-
this._focusField(this.el.find('input').last(), 'end');
|
893
810
|
}
|
894
|
-
this.el.removeClass('selecting-all');
|
895
|
-
return this._state.selectingAll = false;
|
811
|
+
return this.el.removeClass('selecting-all');
|
896
812
|
}
|
897
813
|
};
|
898
814
|
|
@@ -949,8 +865,8 @@
|
|
949
865
|
field = this.el.find('input').last();
|
950
866
|
this._focusField(field, place);
|
951
867
|
} else {
|
952
|
-
field =
|
953
|
-
fieldOffset =
|
868
|
+
field = null;
|
869
|
+
fieldOffset = null;
|
954
870
|
_lastStartPos = 0;
|
955
871
|
_ref = this.options.groupings;
|
956
872
|
for (groupIndex = _i = 0, _len = _ref.length; _i < _len; groupIndex = ++_i) {
|
@@ -1019,35 +935,6 @@
|
|
1019
935
|
});
|
1020
936
|
};
|
1021
937
|
|
1022
|
-
SegmentedCardNumberInputView.prototype.isFilled = function() {
|
1023
|
-
return this.getValue().length === this.maxLength();
|
1024
|
-
};
|
1025
|
-
|
1026
|
-
SegmentedCardNumberInputView.prototype.isValid = function() {
|
1027
|
-
return this.isFilled() && this.isValidLuhn(this.getValue());
|
1028
|
-
};
|
1029
|
-
|
1030
|
-
SegmentedCardNumberInputView.prototype.isValidLuhn = function(identifier) {
|
1031
|
-
var alt, i, num, sum, _i, _ref;
|
1032
|
-
sum = 0;
|
1033
|
-
alt = false;
|
1034
|
-
for (i = _i = _ref = identifier.length - 1; _i >= 0; i = _i += -1) {
|
1035
|
-
num = parseInt(identifier.charAt(i), 10);
|
1036
|
-
if (isNaN(num)) {
|
1037
|
-
return false;
|
1038
|
-
}
|
1039
|
-
if (alt) {
|
1040
|
-
num *= 2;
|
1041
|
-
if (num > 9) {
|
1042
|
-
num = (num % 10) + 1;
|
1043
|
-
}
|
1044
|
-
}
|
1045
|
-
alt = !alt;
|
1046
|
-
sum += num;
|
1047
|
-
}
|
1048
|
-
return sum % 10 === 0;
|
1049
|
-
};
|
1050
|
-
|
1051
938
|
SegmentedCardNumberInputView.prototype.bind = function() {
|
1052
939
|
var args, _ref;
|
1053
940
|
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
@@ -1089,37 +976,43 @@
|
|
1089
976
|
*/
|
1090
977
|
|
1091
978
|
|
1092
|
-
Skeuocard.prototype.ExpirationInputView = (function(
|
1093
|
-
|
1094
|
-
__extends(ExpirationInputView, _super);
|
1095
|
-
|
979
|
+
Skeuocard.prototype.ExpirationInputView = (function() {
|
1096
980
|
function ExpirationInputView(opts) {
|
1097
981
|
var _this = this;
|
1098
982
|
if (opts == null) {
|
1099
983
|
opts = {};
|
1100
984
|
}
|
1101
|
-
opts.dateFormatter || (opts.dateFormatter = function(date) {
|
1102
|
-
return date.getDate() + "-" + (date.getMonth() + 1) + "-" + date.getFullYear();
|
1103
|
-
});
|
1104
|
-
opts.dateParser || (opts.dateParser = function(value) {
|
1105
|
-
var dateParts;
|
1106
|
-
dateParts = value.split('-');
|
1107
|
-
return new Date(dateParts[2], dateParts[1] - 1, dateParts[0]);
|
1108
|
-
});
|
1109
|
-
opts.currentDate || (opts.currentDate = new Date());
|
1110
985
|
opts.pattern || (opts.pattern = "MM/YY");
|
1111
986
|
this.options = opts;
|
1112
|
-
this.date =
|
1113
|
-
this.value = void 0;
|
987
|
+
this.date = null;
|
1114
988
|
this.el = $("<fieldset>");
|
989
|
+
this.el.addClass('cc-field');
|
1115
990
|
this.el.delegate("input", "keydown", function(e) {
|
1116
991
|
return _this._onKeyDown(e);
|
1117
992
|
});
|
1118
993
|
this.el.delegate("input", "keyup", function(e) {
|
1119
994
|
return _this._onKeyUp(e);
|
1120
995
|
});
|
996
|
+
this.el.delegate("input", "focus", function(e) {
|
997
|
+
return _this.el.addClass('focus');
|
998
|
+
});
|
999
|
+
this.el.delegate("input", "blur", function(e) {
|
1000
|
+
return _this.el.removeClass('focus');
|
1001
|
+
});
|
1121
1002
|
}
|
1122
1003
|
|
1004
|
+
ExpirationInputView.prototype.bind = function() {
|
1005
|
+
var args, _ref;
|
1006
|
+
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
1007
|
+
return (_ref = this.el).bind.apply(_ref, args);
|
1008
|
+
};
|
1009
|
+
|
1010
|
+
ExpirationInputView.prototype.trigger = function() {
|
1011
|
+
var args, _ref;
|
1012
|
+
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
1013
|
+
return (_ref = this.el).trigger.apply(_ref, args);
|
1014
|
+
};
|
1015
|
+
|
1123
1016
|
ExpirationInputView.prototype._getFieldCaretPosition = function(el) {
|
1124
1017
|
var input, sel, selLength;
|
1125
1018
|
input = el.get(0);
|
@@ -1197,6 +1090,12 @@
|
|
1197
1090
|
}
|
1198
1091
|
};
|
1199
1092
|
|
1093
|
+
ExpirationInputView.prototype._zeroPadNumber = function(num, places) {
|
1094
|
+
var zero;
|
1095
|
+
zero = places - num.toString().length + 1;
|
1096
|
+
return Array(zero).join("0") + num;
|
1097
|
+
};
|
1098
|
+
|
1200
1099
|
ExpirationInputView.prototype._updateFieldValues = function() {
|
1201
1100
|
var currentDate,
|
1202
1101
|
_this = this;
|
@@ -1228,24 +1127,13 @@
|
|
1228
1127
|
});
|
1229
1128
|
};
|
1230
1129
|
|
1231
|
-
ExpirationInputView.prototype.
|
1130
|
+
ExpirationInputView.prototype.setValue = function(newDate) {
|
1232
1131
|
this.date = newDate;
|
1233
|
-
this.value = this.options.dateFormatter(newDate);
|
1234
|
-
return this._updateFieldValues();
|
1235
|
-
};
|
1236
|
-
|
1237
|
-
ExpirationInputView.prototype.setValue = function(newValue) {
|
1238
|
-
this.value = newValue;
|
1239
|
-
this.date = this.options.dateParser(newValue);
|
1240
1132
|
return this._updateFieldValues();
|
1241
1133
|
};
|
1242
1134
|
|
1243
|
-
ExpirationInputView.prototype.getDate = function() {
|
1244
|
-
return this.date;
|
1245
|
-
};
|
1246
|
-
|
1247
1135
|
ExpirationInputView.prototype.getValue = function() {
|
1248
|
-
return this.
|
1136
|
+
return this.date;
|
1249
1137
|
};
|
1250
1138
|
|
1251
1139
|
ExpirationInputView.prototype.reconfigure = function(opts) {
|
@@ -1294,6 +1182,10 @@
|
|
1294
1182
|
}
|
1295
1183
|
};
|
1296
1184
|
|
1185
|
+
ExpirationInputView.prototype.getRawValue = function(fieldType) {
|
1186
|
+
return parseInt(this.el.find(".cc-exp-field-" + fieldType).val());
|
1187
|
+
};
|
1188
|
+
|
1297
1189
|
ExpirationInputView.prototype._onKeyUp = function(e) {
|
1298
1190
|
var arrowKeys, dateObj, day, groupCaretPos, groupEl, groupMaxLength, groupValLength, month, nextInputEl, pattern, specialKeys, year, _ref, _ref1;
|
1299
1191
|
e.stopPropagation();
|
@@ -1316,18 +1208,16 @@
|
|
1316
1208
|
if ((_ref1 = e.which, __indexOf.call(specialKeys, _ref1) < 0) && groupEl.val().length === groupMaxLength && !$.isEmptyObject(nextInputEl) && this._getFieldCaretPosition(groupEl) === groupMaxLength) {
|
1317
1209
|
nextInputEl.focus();
|
1318
1210
|
}
|
1319
|
-
day =
|
1320
|
-
month =
|
1321
|
-
year =
|
1211
|
+
day = this.getRawValue('d') || 1;
|
1212
|
+
month = this.getRawValue('m');
|
1213
|
+
year = this.getRawValue('y');
|
1322
1214
|
if (month === 0 || year === 0) {
|
1323
|
-
this.value = "";
|
1324
1215
|
this.date = null;
|
1325
1216
|
} else {
|
1326
1217
|
if (year < 2000) {
|
1327
1218
|
year += 2000;
|
1328
1219
|
}
|
1329
1220
|
dateObj = new Date(year, month - 1, day);
|
1330
|
-
this.value = this.options.dateFormatter(dateObj);
|
1331
1221
|
this.date = dateObj;
|
1332
1222
|
}
|
1333
1223
|
this.trigger("keyup", [this]);
|
@@ -1338,126 +1228,352 @@
|
|
1338
1228
|
return this.el.find("input");
|
1339
1229
|
};
|
1340
1230
|
|
1341
|
-
ExpirationInputView.prototype.
|
1342
|
-
|
1343
|
-
_ref = this.groupEls;
|
1344
|
-
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
1345
|
-
inputEl = _ref[_i];
|
1346
|
-
el = $(inputEl);
|
1347
|
-
if (el.val().length !== parseInt(el.attr('maxlength'))) {
|
1348
|
-
return false;
|
1349
|
-
}
|
1350
|
-
}
|
1351
|
-
return true;
|
1231
|
+
ExpirationInputView.prototype.show = function() {
|
1232
|
+
return this.el.show();
|
1352
1233
|
};
|
1353
1234
|
|
1354
|
-
ExpirationInputView.prototype.
|
1355
|
-
return this.
|
1235
|
+
ExpirationInputView.prototype.hide = function() {
|
1236
|
+
return this.el.hide();
|
1356
1237
|
};
|
1357
1238
|
|
1358
1239
|
return ExpirationInputView;
|
1359
1240
|
|
1360
|
-
})(
|
1241
|
+
})();
|
1361
1242
|
|
1362
|
-
|
1243
|
+
/*
|
1244
|
+
Skeuocard::TextInputView
|
1245
|
+
*/
|
1363
1246
|
|
1364
|
-
__extends(TextInputView, _super);
|
1365
1247
|
|
1248
|
+
Skeuocard.prototype.TextInputView = (function() {
|
1366
1249
|
function TextInputView(opts) {
|
1367
|
-
|
1250
|
+
var _this = this;
|
1251
|
+
this.el = $('<div>');
|
1252
|
+
this.inputEl = $("<input>").attr({
|
1368
1253
|
type: 'text',
|
1369
1254
|
placeholder: opts.placeholder,
|
1370
1255
|
"class": opts["class"]
|
1371
1256
|
});
|
1257
|
+
this.el.append(this.inputEl);
|
1258
|
+
this.el.addClass('cc-field');
|
1372
1259
|
this.options = opts;
|
1260
|
+
this.el.delegate("input", "focus", function(e) {
|
1261
|
+
return _this.el.addClass('focus');
|
1262
|
+
});
|
1263
|
+
this.el.delegate("input", "blur", function(e) {
|
1264
|
+
return _this.el.removeClass('focus');
|
1265
|
+
});
|
1266
|
+
this.el.delegate("input", "keyup", function(e) {
|
1267
|
+
e.stopPropagation();
|
1268
|
+
return _this.trigger('keyup', [_this]);
|
1269
|
+
});
|
1373
1270
|
}
|
1374
1271
|
|
1375
1272
|
TextInputView.prototype.clear = function() {
|
1376
|
-
return this.
|
1273
|
+
return this.inputEl.val("");
|
1377
1274
|
};
|
1378
1275
|
|
1379
1276
|
TextInputView.prototype.attr = function() {
|
1380
1277
|
var args, _ref;
|
1381
1278
|
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
1382
|
-
return (_ref = this.
|
1279
|
+
return (_ref = this.inputEl).attr.apply(_ref, args);
|
1383
1280
|
};
|
1384
1281
|
|
1385
|
-
TextInputView.prototype.
|
1386
|
-
return this.
|
1282
|
+
TextInputView.prototype.setValue = function(newValue) {
|
1283
|
+
return this.inputEl.val(newValue);
|
1387
1284
|
};
|
1388
1285
|
|
1389
|
-
TextInputView.prototype.
|
1390
|
-
|
1391
|
-
return this.el.val().length === parseInt(this.el.attr('maxlength'));
|
1392
|
-
} else {
|
1393
|
-
return this.isFilled();
|
1394
|
-
}
|
1286
|
+
TextInputView.prototype.getValue = function() {
|
1287
|
+
return this.inputEl.val();
|
1395
1288
|
};
|
1396
1289
|
|
1397
|
-
TextInputView.prototype.
|
1398
|
-
|
1290
|
+
TextInputView.prototype.bind = function() {
|
1291
|
+
var args, _ref;
|
1292
|
+
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
1293
|
+
return (_ref = this.el).bind.apply(_ref, args);
|
1399
1294
|
};
|
1400
1295
|
|
1401
|
-
|
1296
|
+
TextInputView.prototype.trigger = function() {
|
1297
|
+
var args, _ref;
|
1298
|
+
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
1299
|
+
return (_ref = this.el).trigger.apply(_ref, args);
|
1300
|
+
};
|
1402
1301
|
|
1403
|
-
|
1302
|
+
TextInputView.prototype.show = function() {
|
1303
|
+
return this.el.show();
|
1304
|
+
};
|
1404
1305
|
|
1405
|
-
|
1306
|
+
TextInputView.prototype.hide = function() {
|
1307
|
+
return this.el.hide();
|
1308
|
+
};
|
1309
|
+
|
1310
|
+
return TextInputView;
|
1311
|
+
|
1312
|
+
})();
|
1406
1313
|
|
1407
1314
|
/*
|
1408
|
-
|
1315
|
+
Skeuocard::CardProduct
|
1409
1316
|
*/
|
1410
1317
|
|
1411
1318
|
|
1412
|
-
|
1319
|
+
Skeuocard.prototype.CardProduct = (function() {
|
1320
|
+
CardProduct._registry = [];
|
1321
|
+
|
1322
|
+
CardProduct.create = function(opts) {
|
1323
|
+
return this._registry.push(new Skeuocard.prototype.CardProduct(opts));
|
1324
|
+
};
|
1325
|
+
|
1326
|
+
CardProduct.firstMatchingShortname = function(shortname) {
|
1327
|
+
var card, _i, _len, _ref;
|
1328
|
+
_ref = this._registry;
|
1329
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
1330
|
+
card = _ref[_i];
|
1331
|
+
if (card.attrs.companyShortname === shortname) {
|
1332
|
+
return card;
|
1333
|
+
}
|
1334
|
+
}
|
1335
|
+
return null;
|
1336
|
+
};
|
1337
|
+
|
1338
|
+
CardProduct.firstMatchingNumber = function(number) {
|
1339
|
+
var card, combinedOptions, variation, _i, _len, _ref;
|
1340
|
+
_ref = this._registry;
|
1341
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
1342
|
+
card = _ref[_i];
|
1343
|
+
if (card.pattern.test(number)) {
|
1344
|
+
if ((variation = card.firstVariationMatchingNumber(number))) {
|
1345
|
+
combinedOptions = $.extend({}, card.attrs, variation);
|
1346
|
+
return new Skeuocard.prototype.CardProduct(combinedOptions);
|
1347
|
+
}
|
1348
|
+
return new Skeuocard.prototype.CardProduct(card.attrs);
|
1349
|
+
}
|
1350
|
+
}
|
1351
|
+
return null;
|
1352
|
+
};
|
1353
|
+
|
1354
|
+
function CardProduct(attrs) {
|
1355
|
+
this.attrs = $.extend({}, attrs);
|
1356
|
+
this.pattern = this.attrs.pattern;
|
1357
|
+
this._variances = [];
|
1358
|
+
this.name = {
|
1359
|
+
isFilled: this._isCardNameFilled.bind(this),
|
1360
|
+
isValid: this._isCardNameValid.bind(this)
|
1361
|
+
};
|
1362
|
+
this.number = {
|
1363
|
+
isFilled: this._isCardNumberFilled.bind(this),
|
1364
|
+
isValid: this._isCardNumberValid.bind(this)
|
1365
|
+
};
|
1366
|
+
this.exp = {
|
1367
|
+
isFilled: this._isCardExpirationFilled.bind(this),
|
1368
|
+
isValid: this._isCardExpirationValid.bind(this)
|
1369
|
+
};
|
1370
|
+
this.cvc = {
|
1371
|
+
isFilled: this._isCardCVCFilled.bind(this),
|
1372
|
+
isValid: this._isCardCVCValid.bind(this)
|
1373
|
+
};
|
1374
|
+
}
|
1375
|
+
|
1376
|
+
CardProduct.prototype.createVariation = function(attrs) {
|
1377
|
+
return this._variances.push(attrs);
|
1378
|
+
};
|
1379
|
+
|
1380
|
+
CardProduct.prototype.firstVariationMatchingNumber = function(number) {
|
1381
|
+
var variance, _i, _len, _ref;
|
1382
|
+
_ref = this._variances;
|
1383
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
1384
|
+
variance = _ref[_i];
|
1385
|
+
if (variance.pattern.test(number)) {
|
1386
|
+
return variance;
|
1387
|
+
}
|
1388
|
+
}
|
1389
|
+
return null;
|
1390
|
+
};
|
1391
|
+
|
1392
|
+
CardProduct.prototype.fieldsForLayoutFace = function(faceName) {
|
1393
|
+
var face, fieldName, _ref, _results;
|
1394
|
+
_ref = this.attrs.layout;
|
1395
|
+
_results = [];
|
1396
|
+
for (fieldName in _ref) {
|
1397
|
+
face = _ref[fieldName];
|
1398
|
+
if (face === faceName) {
|
1399
|
+
_results.push(fieldName);
|
1400
|
+
}
|
1401
|
+
}
|
1402
|
+
return _results;
|
1403
|
+
};
|
1404
|
+
|
1405
|
+
CardProduct.prototype._id = function() {
|
1406
|
+
var ident;
|
1407
|
+
ident = this.attrs.companyShortname;
|
1408
|
+
if (this.attrs.issuerShortname != null) {
|
1409
|
+
ident += this.attrs.issuerShortname;
|
1410
|
+
}
|
1411
|
+
return ident;
|
1412
|
+
};
|
1413
|
+
|
1414
|
+
CardProduct.prototype.eql = function(otherCardProduct) {
|
1415
|
+
return (otherCardProduct != null ? otherCardProduct._id() : void 0) === this._id();
|
1416
|
+
};
|
1417
|
+
|
1418
|
+
CardProduct.prototype._daysInMonth = function(m, y) {
|
1419
|
+
switch (m) {
|
1420
|
+
case 1:
|
1421
|
+
if ((y % 4 === 0 && y % 100) || y % 400 === 0) {
|
1422
|
+
return 29;
|
1423
|
+
} else {
|
1424
|
+
return 28;
|
1425
|
+
}
|
1426
|
+
case 3:
|
1427
|
+
case 5:
|
1428
|
+
case 8:
|
1429
|
+
case 10:
|
1430
|
+
return 30;
|
1431
|
+
default:
|
1432
|
+
return 31;
|
1433
|
+
}
|
1434
|
+
};
|
1435
|
+
|
1436
|
+
CardProduct.prototype._isCardNumberFilled = function(number) {
|
1437
|
+
var _ref;
|
1438
|
+
if (this.attrs.cardNumberLength != null) {
|
1439
|
+
return (_ref = number.length, __indexOf.call(this.attrs.cardNumberLength, _ref) >= 0);
|
1440
|
+
}
|
1441
|
+
};
|
1442
|
+
|
1443
|
+
CardProduct.prototype._isCardExpirationFilled = function(exp) {
|
1444
|
+
var currentDate, day, month, year;
|
1445
|
+
currentDate = Skeuocard.currentDate;
|
1446
|
+
if (!((exp != null) && (exp.getMonth != null) && (exp.getFullYear != null))) {
|
1447
|
+
return false;
|
1448
|
+
}
|
1449
|
+
day = exp.getDate();
|
1450
|
+
month = exp.getMonth();
|
1451
|
+
year = exp.getFullYear();
|
1452
|
+
return (day > 0 && day <= this._daysInMonth(month, year)) && (month >= 0 && month <= 11) && (year >= 1900 && year <= currentDate.getFullYear() + 10);
|
1453
|
+
};
|
1454
|
+
|
1455
|
+
CardProduct.prototype._isCardCVCFilled = function(cvc) {
|
1456
|
+
return cvc.length === this.attrs.cvcLength;
|
1457
|
+
};
|
1458
|
+
|
1459
|
+
CardProduct.prototype._isCardNameFilled = function(name) {
|
1460
|
+
return name.length > 0;
|
1461
|
+
};
|
1462
|
+
|
1463
|
+
CardProduct.prototype._isCardNumberValid = function(number) {
|
1464
|
+
return /^\d+$/.test(number) && (this.attrs.validateLuhn === false || this._isValidLuhn(number)) && this._isCardNumberFilled(number);
|
1465
|
+
};
|
1466
|
+
|
1467
|
+
CardProduct.prototype._isCardExpirationValid = function(exp) {
|
1468
|
+
var currentDate, day, isDateInFuture, month, year;
|
1469
|
+
if (!((exp != null) && (exp.getMonth != null) && (exp.getFullYear != null))) {
|
1470
|
+
return false;
|
1471
|
+
}
|
1472
|
+
currentDate = Skeuocard.currentDate;
|
1473
|
+
day = exp.getDate();
|
1474
|
+
month = exp.getMonth();
|
1475
|
+
year = exp.getFullYear();
|
1476
|
+
isDateInFuture = (year === currentDate.getFullYear() && month >= currentDate.getMonth()) || year > currentDate.getFullYear();
|
1477
|
+
return isDateInFuture && this._isCardExpirationFilled(exp);
|
1478
|
+
};
|
1479
|
+
|
1480
|
+
CardProduct.prototype._isCardCVCValid = function(cvc) {
|
1481
|
+
return this._isCardCVCFilled(cvc);
|
1482
|
+
};
|
1483
|
+
|
1484
|
+
CardProduct.prototype._isCardNameValid = function(name) {
|
1485
|
+
return this._isCardNameFilled(name);
|
1486
|
+
};
|
1487
|
+
|
1488
|
+
CardProduct.prototype._isValidLuhn = function(number) {
|
1489
|
+
var alt, i, num, sum, _i, _ref;
|
1490
|
+
sum = 0;
|
1491
|
+
alt = false;
|
1492
|
+
for (i = _i = _ref = number.length - 1; _i >= 0; i = _i += -1) {
|
1493
|
+
num = parseInt(number.charAt(i), 10);
|
1494
|
+
if (isNaN(num)) {
|
1495
|
+
return false;
|
1496
|
+
}
|
1497
|
+
if (alt) {
|
1498
|
+
num *= 2;
|
1499
|
+
if (num > 9) {
|
1500
|
+
num = (num % 10) + 1;
|
1501
|
+
}
|
1502
|
+
}
|
1503
|
+
alt = !alt;
|
1504
|
+
sum += num;
|
1505
|
+
}
|
1506
|
+
return sum % 10 === 0;
|
1507
|
+
};
|
1508
|
+
|
1509
|
+
return CardProduct;
|
1510
|
+
|
1511
|
+
})();
|
1512
|
+
|
1513
|
+
/*
|
1514
|
+
# Seed CardProducts.
|
1515
|
+
*/
|
1413
1516
|
|
1414
|
-
|
1517
|
+
|
1518
|
+
Skeuocard.prototype.CardProduct.create({
|
1519
|
+
pattern: /^(36|38|30[0-5])/,
|
1415
1520
|
companyName: "Diners Club",
|
1416
1521
|
companyShortname: "dinersclubintl",
|
1417
1522
|
cardNumberGrouping: [4, 6, 4],
|
1523
|
+
cardNumberLength: [14],
|
1418
1524
|
expirationFormat: "MM/YY",
|
1419
1525
|
cvcLength: 3,
|
1526
|
+
validateLuhn: true,
|
1420
1527
|
layout: {
|
1421
1528
|
number: 'front',
|
1422
1529
|
exp: 'front',
|
1423
1530
|
name: 'front',
|
1424
1531
|
cvc: 'back'
|
1425
1532
|
}
|
1426
|
-
};
|
1533
|
+
});
|
1427
1534
|
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1535
|
+
Skeuocard.prototype.CardProduct.create({
|
1536
|
+
pattern: /^35/,
|
1537
|
+
companyName: "JCB",
|
1538
|
+
companyShortname: "jcb",
|
1539
|
+
cardNumberGrouping: [4, 4, 4, 4],
|
1540
|
+
cardNumberLength: [16],
|
1541
|
+
expirationFormat: "MM/'YY",
|
1433
1542
|
cvcLength: 3,
|
1543
|
+
validateLuhn: true,
|
1434
1544
|
layout: {
|
1435
1545
|
number: 'front',
|
1436
1546
|
exp: 'front',
|
1437
1547
|
name: 'front',
|
1438
1548
|
cvc: 'back'
|
1439
1549
|
}
|
1440
|
-
};
|
1550
|
+
});
|
1441
1551
|
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1552
|
+
Skeuocard.prototype.CardProduct.create({
|
1553
|
+
pattern: /^3[47]/,
|
1554
|
+
companyName: "American Express",
|
1555
|
+
companyShortname: "amex",
|
1556
|
+
cardNumberGrouping: [4, 6, 5],
|
1557
|
+
cardNumberLength: [15],
|
1446
1558
|
expirationFormat: "MM/YY",
|
1447
|
-
cvcLength:
|
1559
|
+
cvcLength: 4,
|
1560
|
+
validateLuhn: true,
|
1448
1561
|
layout: {
|
1449
1562
|
number: 'front',
|
1450
1563
|
exp: 'front',
|
1451
1564
|
name: 'front',
|
1452
|
-
cvc: '
|
1565
|
+
cvc: 'front'
|
1453
1566
|
}
|
1454
|
-
};
|
1567
|
+
});
|
1455
1568
|
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1569
|
+
Skeuocard.prototype.CardProduct.create({
|
1570
|
+
pattern: /^(6706|6771|6709)/,
|
1571
|
+
companyName: "Laser Card Services Ltd.",
|
1572
|
+
companyShortname: "laser",
|
1459
1573
|
cardNumberGrouping: [4, 4, 4, 4],
|
1574
|
+
cardNumberLength: [16, 17, 18, 19],
|
1460
1575
|
expirationFormat: "MM/YY",
|
1576
|
+
validateLuhn: true,
|
1461
1577
|
cvcLength: 3,
|
1462
1578
|
layout: {
|
1463
1579
|
number: 'front',
|
@@ -1465,27 +1581,33 @@
|
|
1465
1581
|
name: 'front',
|
1466
1582
|
cvc: 'back'
|
1467
1583
|
}
|
1468
|
-
};
|
1584
|
+
});
|
1469
1585
|
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1586
|
+
Skeuocard.prototype.CardProduct.create({
|
1587
|
+
pattern: /^4/,
|
1588
|
+
companyName: "Visa",
|
1589
|
+
companyShortname: "visa",
|
1590
|
+
cardNumberGrouping: [4, 4, 4, 4],
|
1591
|
+
cardNumberLength: [13, 14, 15, 16],
|
1474
1592
|
expirationFormat: "MM/YY",
|
1475
|
-
|
1593
|
+
validateLuhn: true,
|
1594
|
+
cvcLength: 3,
|
1476
1595
|
layout: {
|
1477
1596
|
number: 'front',
|
1478
1597
|
exp: 'front',
|
1479
1598
|
name: 'front',
|
1480
|
-
cvc: '
|
1599
|
+
cvc: 'back'
|
1481
1600
|
}
|
1482
|
-
};
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1601
|
+
});
|
1602
|
+
|
1603
|
+
Skeuocard.prototype.CardProduct.create({
|
1604
|
+
pattern: /^(62|88)/,
|
1605
|
+
companyName: "China UnionPay",
|
1606
|
+
companyShortname: "unionpay",
|
1607
|
+
cardNumberGrouping: [19],
|
1608
|
+
cardNumberLength: [16, 17, 18, 19],
|
1488
1609
|
expirationFormat: "MM/YY",
|
1610
|
+
validateLuhn: false,
|
1489
1611
|
cvcLength: 3,
|
1490
1612
|
layout: {
|
1491
1613
|
number: 'front',
|
@@ -1493,13 +1615,16 @@
|
|
1493
1615
|
name: 'front',
|
1494
1616
|
cvc: 'back'
|
1495
1617
|
}
|
1496
|
-
};
|
1618
|
+
});
|
1497
1619
|
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1620
|
+
Skeuocard.prototype.CardProduct.create({
|
1621
|
+
pattern: /^5[1-5]/,
|
1622
|
+
companyName: "Mastercard",
|
1623
|
+
companyShortname: "mastercard",
|
1501
1624
|
cardNumberGrouping: [4, 4, 4, 4],
|
1625
|
+
cardNumberLength: [16],
|
1502
1626
|
expirationFormat: "MM/YY",
|
1627
|
+
validateLuhn: true,
|
1503
1628
|
cvcLength: 3,
|
1504
1629
|
layout: {
|
1505
1630
|
number: 'front',
|
@@ -1507,13 +1632,16 @@
|
|
1507
1632
|
name: 'front',
|
1508
1633
|
cvc: 'back'
|
1509
1634
|
}
|
1510
|
-
};
|
1635
|
+
});
|
1511
1636
|
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1637
|
+
Skeuocard.prototype.CardProduct.create({
|
1638
|
+
pattern: /^(5018|5020|5038|6304|6759|676[1-3])/,
|
1639
|
+
companyName: "Maestro (MasterCard)",
|
1640
|
+
companyShortname: "maestro",
|
1515
1641
|
cardNumberGrouping: [4, 4, 4, 4],
|
1642
|
+
cardNumberLength: [12, 13, 14, 15, 16, 17, 18, 19],
|
1516
1643
|
expirationFormat: "MM/YY",
|
1644
|
+
validateLuhn: true,
|
1517
1645
|
cvcLength: 3,
|
1518
1646
|
layout: {
|
1519
1647
|
number: 'front',
|
@@ -1521,13 +1649,16 @@
|
|
1521
1649
|
name: 'front',
|
1522
1650
|
cvc: 'back'
|
1523
1651
|
}
|
1524
|
-
};
|
1652
|
+
});
|
1525
1653
|
|
1526
|
-
|
1654
|
+
Skeuocard.prototype.CardProduct.create({
|
1655
|
+
pattern: /^(6011|65|64[4-9]|622)/,
|
1527
1656
|
companyName: "Discover",
|
1528
1657
|
companyShortname: "discover",
|
1529
1658
|
cardNumberGrouping: [4, 4, 4, 4],
|
1659
|
+
cardNumberLength: [16],
|
1530
1660
|
expirationFormat: "MM/YY",
|
1661
|
+
validateLuhn: true,
|
1531
1662
|
cvcLength: 3,
|
1532
1663
|
layout: {
|
1533
1664
|
number: 'front',
|
@@ -1535,25 +1666,21 @@
|
|
1535
1666
|
name: 'front',
|
1536
1667
|
cvc: 'back'
|
1537
1668
|
}
|
1538
|
-
};
|
1539
|
-
|
1540
|
-
CCIssuers = {};
|
1541
|
-
|
1542
|
-
/*
|
1543
|
-
Hack fixes the Chase Sapphire card's stupid (nice?) layout non-conformity.
|
1544
|
-
*/
|
1669
|
+
});
|
1545
1670
|
|
1671
|
+
visaProduct = Skeuocard.prototype.CardProduct.firstMatchingShortname('visa');
|
1546
1672
|
|
1547
|
-
|
1673
|
+
visaProduct.createVariation({
|
1674
|
+
pattern: /^414720/,
|
1548
1675
|
issuingAuthority: "Chase",
|
1549
1676
|
issuerName: "Chase Sapphire Card",
|
1550
1677
|
issuerShortname: "chase-sapphire",
|
1551
1678
|
layout: {
|
1679
|
+
name: 'front',
|
1552
1680
|
number: 'front',
|
1553
1681
|
exp: 'front',
|
1554
|
-
name: 'front',
|
1555
1682
|
cvc: 'front'
|
1556
1683
|
}
|
1557
|
-
};
|
1684
|
+
});
|
1558
1685
|
|
1559
1686
|
}).call(this);
|