weaver 0.8.13 → 0.9.0

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