geico_design_kit 0.0.1-security.1 → 15.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of geico_design_kit might be problematic. Click here for more details.

Files changed (109) hide show
  1. package/.babelrc +5 -0
  2. package/LICENSE +0 -0
  3. package/alt.json +33 -0
  4. package/dist/analytics.js +119 -0
  5. package/dist/appState.js +56 -0
  6. package/dist/baseComponent.js +110 -0
  7. package/dist/components/Accordion.js +312 -0
  8. package/dist/components/AddressAutoComplete.js +220 -0
  9. package/dist/components/Alert.js +145 -0
  10. package/dist/components/BackgroundPattern.js +99 -0
  11. package/dist/components/BackgroundPatternPortfolio.js +242 -0
  12. package/dist/components/ButtonSwitch.js +236 -0
  13. package/dist/components/CardSelections.js +230 -0
  14. package/dist/components/CommonQuestionsSquares.js +169 -0
  15. package/dist/components/Confirmation.js +156 -0
  16. package/dist/components/ConsolidatedSummary.js +489 -0
  17. package/dist/components/CoverageGraph.js +201 -0
  18. package/dist/components/CreditCard.js +591 -0
  19. package/dist/components/CurrencyInput.js +302 -0
  20. package/dist/components/DatePicker.js +468 -0
  21. package/dist/components/DockedMessage.js +146 -0
  22. package/dist/components/DotNavigation.js +200 -0
  23. package/dist/components/EditComponent.js +128 -0
  24. package/dist/components/EditableTable.js +113 -0
  25. package/dist/components/InPageNavigation.js +360 -0
  26. package/dist/components/Loader.js +232 -0
  27. package/dist/components/MakePayment.js +361 -0
  28. package/dist/components/Modal.js +254 -0
  29. package/dist/components/MoreInfoButton.js +227 -0
  30. package/dist/components/MultipleSelectBox.js +217 -0
  31. package/dist/components/NavigationalBox.js +161 -0
  32. package/dist/components/Navigator.js +294 -0
  33. package/dist/components/PasswordMeter.js +201 -0
  34. package/dist/components/PayPlans.js +534 -0
  35. package/dist/components/SegmentedControl.js +327 -0
  36. package/dist/components/SortableTable.js +166 -0
  37. package/dist/components/Tabs.js +1 -0
  38. package/dist/components/TextAreaCountdown.js +219 -0
  39. package/dist/components/Timeline.js +498 -0
  40. package/dist/components/TimelineFilter.js +492 -0
  41. package/dist/components/ToTopArrow.js +153 -0
  42. package/dist/components/Tooltip.js +329 -0
  43. package/dist/components/Upsell.js +168 -0
  44. package/dist/components/VIN.js +271 -0
  45. package/dist/components/ValidateForm.js +938 -0
  46. package/dist/components/ViewMoreLess.js +191 -0
  47. package/dist/components/ZipCode.js +191 -0
  48. package/dist/components/portfolio.js +99 -0
  49. package/dist/geico-design-kit.js +141 -0
  50. package/dist/global/components.js +98 -0
  51. package/dist/global/footer.js +26 -0
  52. package/dist/global/nav.js +1257 -0
  53. package/dist/services/CharacterTypeService.js +106 -0
  54. package/dist/services/UserAgentService.js +73 -0
  55. package/dist/utils.js +79 -0
  56. package/package.json +29 -3
  57. package/src/analytics.js +82 -0
  58. package/src/appState.js +56 -0
  59. package/src/baseComponent.js +156 -0
  60. package/src/components/Accordion.js +336 -0
  61. package/src/components/AddressAutoComplete.js +236 -0
  62. package/src/components/Alert.js +135 -0
  63. package/src/components/BackgroundPattern.js +96 -0
  64. package/src/components/BackgroundPatternPortfolio.js +284 -0
  65. package/src/components/ButtonSwitch.js +241 -0
  66. package/src/components/CardSelections.js +240 -0
  67. package/src/components/CommonQuestionsSquares.js +179 -0
  68. package/src/components/Confirmation.js +160 -0
  69. package/src/components/ConsolidatedSummary.js +505 -0
  70. package/src/components/CoverageGraph.js +203 -0
  71. package/src/components/CreditCard.js +595 -0
  72. package/src/components/CurrencyInput.js +321 -0
  73. package/src/components/DatePicker.js +487 -0
  74. package/src/components/DockedMessage.js +142 -0
  75. package/src/components/DotNavigation.js +206 -0
  76. package/src/components/EditComponent.js +130 -0
  77. package/src/components/EditableTable.js +106 -0
  78. package/src/components/InPageNavigation.js +391 -0
  79. package/src/components/Loader.js +272 -0
  80. package/src/components/MakePayment.js +397 -0
  81. package/src/components/Modal.js +279 -0
  82. package/src/components/MoreInfoButton.js +243 -0
  83. package/src/components/MultipleSelectBox.js +211 -0
  84. package/src/components/NavigationalBox.js +163 -0
  85. package/src/components/Navigator.js +338 -0
  86. package/src/components/PasswordMeter.js +209 -0
  87. package/src/components/PayPlans.js +604 -0
  88. package/src/components/SegmentedControl.js +365 -0
  89. package/src/components/SortableTable.js +176 -0
  90. package/src/components/Tabs.js +0 -0
  91. package/src/components/TextAreaCountdown.js +231 -0
  92. package/src/components/Timeline.js +532 -0
  93. package/src/components/TimelineFilter.js +533 -0
  94. package/src/components/ToTopArrow.js +153 -0
  95. package/src/components/Tooltip.js +344 -0
  96. package/src/components/Upsell.js +196 -0
  97. package/src/components/VIN.js +289 -0
  98. package/src/components/ValidateForm.js +1030 -0
  99. package/src/components/ViewMoreLess.js +193 -0
  100. package/src/components/ZipCode.js +193 -0
  101. package/src/components/portfolio.js +106 -0
  102. package/src/geico-design-kit.js +144 -0
  103. package/src/global/components.js +92 -0
  104. package/src/global/footer.js +25 -0
  105. package/src/global/nav.js +1457 -0
  106. package/src/services/CharacterTypeService.js +107 -0
  107. package/src/services/UserAgentService.js +59 -0
  108. package/src/utils.js +82 -0
  109. package/README.md +0 -5
@@ -0,0 +1,1030 @@
1
+ //Require static assets
2
+
3
+ //
4
+ // PureJS Validation - Our component builds off the validation provided by validate.js
5
+ //
6
+
7
+ /*
8
+ * validate.js 1.4.1
9
+ * Copyright (c) 2011 - 2014 Rick Harrison, http://rickharrison.me
10
+ * validate.js is open sourced under the MIT license.
11
+ * Portions of validate.js are inspired by CodeIgniter.
12
+ * http://rickharrison.github.com/validate.js
13
+ */
14
+
15
+ (function(window, document, undefined) {
16
+ /*
17
+ * If you would like an application-wide config, change these defaults.
18
+ * Otherwise, use the setMessage() function to configure form specific messages.
19
+ */
20
+
21
+ const defaults = {
22
+ messages: {
23
+ required: 'The %s field is required.',
24
+ matches: 'The %s field does not match the %s field.',
25
+ "default": 'The %s field is still set to default, please change.',
26
+ valid_email: 'The %s field must contain a valid email address.',
27
+ valid_emails: 'The %s field must contain all valid email addresses.',
28
+ min_length: 'The %s field must be at least %s characters in length.',
29
+ max_length: 'The %s field must not exceed %s characters in length.',
30
+ exact_length: 'The %s field must be exactly %s characters in length.',
31
+ greater_than: 'The %s field must contain a number greater than %s.',
32
+ less_than: 'The %s field must contain a number less than %s.',
33
+ alpha: 'The %s field must only contain alphabetical characters.',
34
+ alpha_numeric: 'The %s field must only contain alpha-numeric characters.',
35
+ alpha_dash: 'The %s field must only contain alpha-numeric characters, underscores, and dashes.',
36
+ numeric: 'The %s field must contain only numbers.',
37
+ integer: 'The %s field must contain an integer.',
38
+ decimal: 'The %s field must contain a decimal number.',
39
+ is_natural: 'The %s field must contain only positive numbers.',
40
+ is_natural_no_zero: 'The %s field must contain a number greater than zero.',
41
+ valid_ip: 'The %s field must contain a valid IP.',
42
+ valid_base64: 'The %s field must contain a base64 string.',
43
+ valid_credit_card: 'The %s field must contain a valid credit card number.',
44
+ is_file_type: 'The %s field must contain only %s files.',
45
+ valid_url: 'The %s field must contain a valid URL.',
46
+ },
47
+ callback: function(errors) {
48
+
49
+ }
50
+ };
51
+
52
+ /*
53
+ * Define the regular expressions that will be used
54
+ */
55
+
56
+ const ruleRegex = /^(.+?)\[(.+)\]$/,
57
+ numericRegex = /^[0-9]+$/,
58
+ integerRegex = /^\-?[0-9]+$/,
59
+ decimalRegex = /^\-?[0-9]*\.?[0-9]+$/,
60
+ emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9]){1,})+$/,
61
+ alphaRegex = /^[a-z]+$/i,
62
+ alphaNumericRegex = /^[a-z0-9]+$/i,
63
+ alphaDashRegex = /^[a-z0-9_\-]+$/i,
64
+ naturalRegex = /^[0-9]+$/i,
65
+ naturalNoZeroRegex = /^[1-9][0-9]*$/i,
66
+ ipRegex = /^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})$/i,
67
+ base64Regex = /[^a-zA-Z0-9\/\+=]/i,
68
+ numericDashRegex = /^[\d\-\s]+$/,
69
+ urlRegex = /^((http|https):\/\/(\w+:{0,1}\w*@)?(\S+)|)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
70
+
71
+ /*
72
+ * The exposed public object to validate a form:
73
+ *
74
+ * @param formNameOrNode - String - The name attribute of the form (i.e. <form name="myForm"></form>) or node of the form element
75
+ * @param fields - Array - [{
76
+ * name: The name of the element (i.e. <input name="myField" />)
77
+ * display: 'Field Name'
78
+ * rules: required|matches[password_confirm]
79
+ * }]
80
+ * @param callback - Function - The callback after validation has been performed.
81
+ * @argument errors - An array of validation errors
82
+ * @argument event - The javascript event
83
+ */
84
+
85
+ let FormValidator = function(formNameOrNode, fields, callback) {
86
+ this.callback = callback || defaults.callback;
87
+ this.errors = [];
88
+ this.fields = {};
89
+ this.form = this._formByNameOrNode(formNameOrNode) || {};
90
+ this.messages = {};
91
+ this.handlers = {};
92
+ this.conditionals = {};
93
+
94
+ for (let i = 0, fieldLength = fields.length; i < fieldLength; i++) {
95
+ let field = fields[i];
96
+
97
+ // If passed in incorrectly, we need to skip the field.
98
+ if ((!field.name && !field.names) || !field.rules) {
99
+ continue;
100
+ }
101
+
102
+ /*
103
+ * Build the master fields array that has all the information needed to validate
104
+ */
105
+
106
+ if (field.names) {
107
+ for (let j = 0, fieldNamesLength = field.names.length; j < fieldNamesLength; j++) {
108
+ this._addField(field, field.names[j]);
109
+ }
110
+ } else {
111
+ this._addField(field, field.name);
112
+ }
113
+ }
114
+
115
+ /*
116
+ * Attach an event callback for the form submission
117
+ */
118
+
119
+ let _onsubmit = this.form.onsubmit;
120
+
121
+ this.form.onsubmit = (function(that) {
122
+ return function(evt) {
123
+ try {
124
+ return that._validateForm(evt) && (_onsubmit === undefined || _onsubmit());
125
+ } catch(e) {}
126
+ };
127
+ })(this);
128
+ },
129
+
130
+ attributeValue = function (element, attributeName) {
131
+
132
+ if ((element.length > 0) && (element[0].type === 'radio' || element[0].type === 'checkbox')) {
133
+ for (let i = 0; element.length > i; i++) {
134
+ if (element[i].checked) {
135
+ return element[i][attributeName];
136
+ }
137
+ }
138
+ return;
139
+ }
140
+
141
+ return element[attributeName];
142
+ };
143
+
144
+ /*
145
+ * @public
146
+ * Sets a custom message for one of the rules
147
+ */
148
+
149
+ FormValidator.prototype.setMessage = function(rule, message) {
150
+ this.messages[rule] = message;
151
+
152
+ // return this for chaining
153
+ return this;
154
+ };
155
+
156
+ /*
157
+ * @public
158
+ * Registers a callback for a custom rule (i.e. callback_username_check)
159
+ */
160
+
161
+ FormValidator.prototype.registerCallback = function(name, handler) {
162
+ if (name && typeof name === 'string' && handler && typeof handler === 'function') {
163
+ this.handlers[name] = handler;
164
+ }
165
+
166
+ // return this for chaining
167
+ return this;
168
+ };
169
+
170
+ /*
171
+ * @public
172
+ * Registers a conditional for a custom 'depends' rule
173
+ */
174
+
175
+ FormValidator.prototype.registerConditional = function(name, conditional) {
176
+ if (name && typeof name === 'string' && conditional && typeof conditional === 'function') {
177
+ this.conditionals[name] = conditional;
178
+ }
179
+
180
+ // return this for chaining
181
+ return this;
182
+ };
183
+
184
+ /*
185
+ * @private
186
+ * Determines if a form dom node was passed in or just a string representing the form name
187
+ */
188
+
189
+ FormValidator.prototype._formByNameOrNode = function(formNameOrNode) {
190
+ return (typeof formNameOrNode === 'object') ? formNameOrNode : document.forms[formNameOrNode];
191
+ };
192
+
193
+ /*
194
+ * @private
195
+ * Adds a file to the master fields array
196
+ */
197
+
198
+ FormValidator.prototype._addField = function(field, nameValue) {
199
+ this.fields[nameValue] = {
200
+ name: nameValue,
201
+ display: field.display || nameValue,
202
+ rules: field.rules,
203
+ depends: field.depends,
204
+ id: null,
205
+ element: null,
206
+ type: null,
207
+ value: null,
208
+ checked: null
209
+ };
210
+ };
211
+
212
+ /*
213
+ * @private
214
+ * Runs the validation when the form is submitted.
215
+ */
216
+
217
+ FormValidator.prototype._validateForm = function(evt) {
218
+ this.errors = [];
219
+
220
+ for (let key in this.fields) {
221
+
222
+ if (this.fields.hasOwnProperty(key)) {
223
+ let field = this.fields[key] || {};
224
+ let element = this.form.querySelectorAll('[name=' + field.name + ']');//this.form[field.name];
225
+ if(element.length === 1) {
226
+ element = element[0];
227
+ }
228
+
229
+ if (element && element !== undefined) {
230
+ field.element = element;
231
+ field.type = (element.length > 0) ? element[0].type : element.type;
232
+ field.id = attributeValue(element, 'id');
233
+ field.value = attributeValue(element, 'value');
234
+ field.checked = attributeValue(element, 'checked');
235
+
236
+ /*
237
+ * Run through the rules for each field.
238
+ * If the field has a depends conditional, only validate the field
239
+ * if it passes the custom function
240
+ */
241
+
242
+ if (field.depends && typeof field.depends === "function") {
243
+ if (field.depends.call(this, field)) {
244
+ this._validateField(field);
245
+ }
246
+ } else if (field.depends && typeof field.depends === "string" && this.conditionals[field.depends]) {
247
+ if (this.conditionals[field.depends].call(this,field)) {
248
+ this._validateField(field);
249
+ }
250
+ } else {
251
+ this._validateField(field);
252
+ }
253
+ }
254
+ }
255
+ }
256
+
257
+ if (typeof this.callback === 'function') {
258
+ this.callback(this.errors, evt);
259
+ }
260
+
261
+ if (this.errors.length > 0) {
262
+ if (evt && evt.preventDefault) {
263
+ evt.preventDefault();
264
+ return false;
265
+ } else if (event) {
266
+ // IE uses the global event variable
267
+ event.returnValue = false;
268
+ }
269
+ }
270
+
271
+ return true;
272
+ };
273
+
274
+ /*
275
+ * @private
276
+ * Looks at the fields value and evaluates it against the given rules
277
+ */
278
+
279
+ FormValidator.prototype._validateField = function(field) {
280
+ const rules = field.rules.split('|'),
281
+ indexOfRequired = field.rules.indexOf('required'),
282
+ isEmpty = (!field.value || field.value === '' || field.value === undefined);
283
+
284
+ /*
285
+ * Run through the rules and execute the validation methods as needed
286
+ */
287
+
288
+ for (let i = 0, ruleLength = rules.length; i < ruleLength; i++) {
289
+ let method = rules[i],
290
+ param = null,
291
+ failed = false,
292
+ parts = ruleRegex.exec(method);
293
+
294
+ /*
295
+ * If this field is not required and the value is empty, continue on to the next rule unless it's a callback.
296
+ * This ensures that a callback will always be called but other rules will be skipped.
297
+ */
298
+
299
+ if (indexOfRequired === -1 && method.indexOf('!callback_') === -1 && isEmpty) {
300
+ continue;
301
+ }
302
+
303
+ /*
304
+ * If the rule has a parameter (i.e. matches[param]) split it out
305
+ */
306
+
307
+ if (parts) {
308
+ method = parts[1];
309
+ param = parts[2];
310
+ }
311
+
312
+ if (method.charAt(0) === '!') {
313
+ method = method.substring(1, method.length);
314
+ }
315
+
316
+ /*
317
+ * If the hook is defined, run it to find any validation errors
318
+ */
319
+
320
+ if (typeof this._hooks[method] === 'function') {
321
+ if (!this._hooks[method].apply(this, [field, param])) {
322
+ failed = true;
323
+ }
324
+ } else if (method.substring(0, 9) === 'callback_') {
325
+ // Custom method. Execute the handler if it was registered
326
+ method = method.substring(9, method.length);
327
+
328
+ if (typeof this.handlers[method] === 'function') {
329
+ if (this.handlers[method].apply(this, [field.value, param, field]) === false) {
330
+ failed = true;
331
+ }
332
+ }
333
+ }
334
+
335
+ /*
336
+ * If the hook failed, add a message to the errors array
337
+ */
338
+
339
+ if (failed) {
340
+ // Make sure we have a message for this rule
341
+ let source = this.messages[field.name + '.' + method] || this.messages[method] || defaults.messages[method],
342
+ message = 'An error has occurred with the ' + field.display + ' field.';
343
+
344
+ if (source) {
345
+ message = source.replace('%s', field.display);
346
+
347
+ if (param) {
348
+ message = message.replace('%s', (this.fields[param]) ? this.fields[param].display : param);
349
+ }
350
+ }
351
+
352
+ this.errors.push({
353
+ id: field.id,
354
+ element: field.element,
355
+ name: field.name,
356
+ message: message,
357
+ rule: method
358
+ });
359
+
360
+ // Break out so as to not spam with validation errors (i.e. required and valid_email)
361
+ break;
362
+ }
363
+ }
364
+ };
365
+
366
+ /*
367
+ * @private
368
+ * Object containing all of the validation hooks
369
+ */
370
+
371
+ FormValidator.prototype._hooks = {
372
+ required: function(field) {
373
+ let value = field.value;
374
+
375
+ if ((field.type === 'checkbox') || (field.type === 'radio')) {
376
+ return (field.checked === true);
377
+ }
378
+
379
+ return (value !== null && value !== '');
380
+ },
381
+
382
+ "default": function(field, defaultName){
383
+ return field.value !== defaultName;
384
+ },
385
+
386
+ matches: function(field, matchName) {
387
+ let el = this.form[matchName];
388
+
389
+ if (el) {
390
+ return field.value === el.value;
391
+ }
392
+
393
+ return false;
394
+ },
395
+
396
+ valid_email: function(field) {
397
+ return emailRegex.test(field.value);
398
+ },
399
+
400
+ valid_emails: function(field) {
401
+ let result = field.value.split(",");
402
+
403
+ for (let i = 0, resultLength = result.length; i < resultLength; i++) {
404
+ if (!emailRegex.test(result[i])) {
405
+ return false;
406
+ }
407
+ }
408
+
409
+ return true;
410
+ },
411
+
412
+ min_length: function(field, length) {
413
+ if (!numericRegex.test(length)) {
414
+ return false;
415
+ }
416
+
417
+ return (field.value.length >= parseInt(length, 10));
418
+ },
419
+
420
+ max_length: function(field, length) {
421
+ if (!numericRegex.test(length)) {
422
+ return false;
423
+ }
424
+
425
+ return (field.value.length <= parseInt(length, 10));
426
+ },
427
+
428
+ exact_length: function(field, length) {
429
+ if (!numericRegex.test(length)) {
430
+ return false;
431
+ }
432
+
433
+ return (field.value.length === parseInt(length, 10));
434
+ },
435
+
436
+ greater_than: function(field, param) {
437
+ if (!decimalRegex.test(field.value)) {
438
+ return false;
439
+ }
440
+
441
+ return (parseFloat(field.value) > parseFloat(param));
442
+ },
443
+
444
+ less_than: function(field, param) {
445
+ if (!decimalRegex.test(field.value)) {
446
+ return false;
447
+ }
448
+
449
+ return (parseFloat(field.value) < parseFloat(param));
450
+ },
451
+
452
+ alpha: function(field) {
453
+ return (alphaRegex.test(field.value));
454
+ },
455
+
456
+ alpha_numeric: function(field) {
457
+ return (alphaNumericRegex.test(field.value));
458
+ },
459
+
460
+ alpha_dash: function(field) {
461
+ return (alphaDashRegex.test(field.value));
462
+ },
463
+
464
+ numeric: function(field) {
465
+ return (numericRegex.test(field.value));
466
+ },
467
+
468
+ integer: function(field) {
469
+ return (integerRegex.test(field.value));
470
+ },
471
+
472
+ decimal: function(field) {
473
+ return (decimalRegex.test(field.value));
474
+ },
475
+
476
+ is_natural: function(field) {
477
+ return (naturalRegex.test(field.value));
478
+ },
479
+
480
+ is_natural_no_zero: function(field) {
481
+ return (naturalNoZeroRegex.test(field.value));
482
+ },
483
+
484
+ valid_ip: function(field) {
485
+ return (ipRegex.test(field.value));
486
+ },
487
+
488
+ valid_base64: function(field) {
489
+ return (base64Regex.test(field.value));
490
+ },
491
+
492
+ valid_url: function(field) {
493
+ return (urlRegex.test(field.value));
494
+ },
495
+
496
+ valid_credit_card: function(field){
497
+ // Luhn Check Code from https://gist.github.com/4075533
498
+ // accept only digits, dashes or spaces
499
+ if (!numericDashRegex.test(field.value)) return false;
500
+
501
+ // The Luhn Algorithm. It's so pretty.
502
+ let nCheck = 0, nDigit = 0, bEven = false;
503
+ let strippedField = field.value.replace(/\D/g, "");
504
+
505
+ for (let n = strippedField.length - 1; n >= 0; n--) {
506
+ let cDigit = strippedField.charAt(n);
507
+ nDigit = parseInt(cDigit, 10);
508
+ if (bEven) {
509
+ if ((nDigit *= 2) > 9) nDigit -= 9;
510
+ }
511
+
512
+ nCheck += nDigit;
513
+ bEven = !bEven;
514
+ }
515
+
516
+ return (nCheck % 10) === 0;
517
+ },
518
+
519
+ is_file_type: function(field,type) {
520
+ if (field.type !== 'file') {
521
+ return true;
522
+ }
523
+
524
+ let ext = field.value.substr((field.value.lastIndexOf('.') + 1)),
525
+ typeArray = type.split(','),
526
+ inArray = false,
527
+ i = 0,
528
+ len = typeArray.length;
529
+
530
+ for (i; i < len; i++) {
531
+ if (ext === typeArray[i]) inArray = true;
532
+ }
533
+
534
+ return inArray;
535
+ }
536
+
537
+ };
538
+
539
+ window.FormValidator = FormValidator;
540
+
541
+ })(window, document);
542
+
543
+ import baseComponent from '../../src/baseComponent';
544
+
545
+ const validateSettings = [
546
+ {
547
+ setting : "content",
548
+ isRequired : true,
549
+ validate : "type",
550
+ possibleValues : ["string","object"],
551
+ errorMessage : ["GDK ValidateForm : Content must be defined and set to a DOM selector or Node"]
552
+ }
553
+ ];
554
+
555
+ class ValidateForm{
556
+
557
+ // 1. All input fields need error styling, if there is a server error on fields we are not directly validating
558
+ // 2. Numbers -- needs to be a number and if required
559
+ // 3. Email -- needs to be a email and if required
560
+ // 4. Phone Number -- needs match exact phone number pattern "(804) 341-3525"
561
+ // 5. SSN -- validate the hidden field needs match exact ssn pattern "123-12-4124"
562
+ // 6. Select Box -- if required
563
+ // 7. Checkbox -- if required
564
+ // 8. Radio Buttons -- if required
565
+ // 9. Text area -- if required
566
+
567
+ /**
568
+ * These are settings for the instantiation. Refer to the design kit section of this component for JS setting examples.
569
+ * @param {string|Object} options
570
+ * A reference to the html form container
571
+ */
572
+ constructor(options) {
573
+ console.log("Initialized the ValidateForm component");
574
+
575
+ this._internalVars = {
576
+ node: null//used for content item
577
+ };
578
+
579
+ //options with defaults set
580
+ this._defaults = {};
581
+
582
+ // Create options by extending defaults with the passed in arguments
583
+ if (options && typeof options === "object") {
584
+ this._options = baseComponent.extendDefaults(this._defaults, options);
585
+ }
586
+
587
+ // From the passed in CSS selector iterate over each form and build validation rules and events
588
+ this._forms = document.querySelectorAll(options.content);
589
+ this._validators = [];
590
+ this._rules = [];
591
+ for(let n = 0; this._forms.length > n; n++)
592
+ {
593
+ // Initialize the form
594
+ initializeForm.call(this, n);
595
+ }
596
+
597
+ //if the required options are valid set up the environment
598
+ if( baseComponent.validateSettings(this._options, validateSettings) ){
599
+ this._internalVars.contentType = baseComponent.getContentType(this);
600
+ setLocalVars.call(this);
601
+ setEvents.call(this);
602
+ }
603
+ }
604
+
605
+ //Public Methods
606
+
607
+ /**
608
+ * removes the node from the dom and any events attached
609
+ */
610
+ destroy(){
611
+ removeEvents.call(this);
612
+ this._internalVars.node.parentNode.removeChild(this._internalVars.node);
613
+
614
+ //a little garbage collection
615
+ for (let variableKey in this){
616
+ if (this.hasOwnProperty(variableKey)){
617
+ delete this[variableKey];
618
+ }
619
+ }
620
+ }
621
+ }
622
+
623
+ // Private Methods
624
+
625
+ /**
626
+ * Initialize the form
627
+ */
628
+ function initializeForm(n) {
629
+
630
+ return (function(that, n) {
631
+ that._rules[n] = buildRules(that._forms[n]);
632
+
633
+ that._validators[n] = new FormValidator(that._forms[n],
634
+ // Built dynamically via data attributes
635
+ that._rules[n],
636
+ // Callback on each validation pass blur or submit
637
+ function(errors, evt) {
638
+ // Clear any old errors
639
+ let wrappers = this.form.querySelectorAll('[class=form-field]');
640
+ for(let j = 0; wrappers.length > j; j++) {
641
+ // clear all field errors and reset the error class on wrapper
642
+ clearFieldError(wrappers[j]);
643
+ }
644
+
645
+ // Handle displaying any errors in the form
646
+ if(errors.length > 0) {
647
+
648
+ // add error classes to appropriate elements
649
+ for(let i = 0; errors.length > i; i++) {
650
+ let element = this.form.querySelectorAll('[name=' + errors[i].name + ']')[0];
651
+
652
+ // get this field's wrapper
653
+ let field_wrapper = closest(element, "form-field");
654
+
655
+ // Create and append the error message
656
+ addFieldError(field_wrapper, errors[i].message);
657
+ }
658
+ }
659
+ }
660
+ );
661
+
662
+ // Extend validation class
663
+ extendValidator(that._validators[n]);
664
+ })(this, n);
665
+ }
666
+
667
+ function addBlur(validator, element) {
668
+ return (function(that, element) {
669
+ return function(evt) {
670
+ let field = that.fields[element.getAttribute('name')] || {};
671
+
672
+ if (element && element !== undefined) {
673
+ field.element = element;
674
+ field.id = element.getAttribute('id');
675
+ field.name = element.getAttribute('name');
676
+ field.type = (element.length > 0) ? element[0].type : element.type;
677
+ field.value = element.value;
678
+ field.checked = element.checked;
679
+
680
+ // clear any errors relating to this field
681
+ if(that.errors.length > 0)
682
+ {
683
+ for(let i = 0; that.errors.length > i; i++) {
684
+ if(that.errors[i].name === field.name)
685
+ {
686
+ // splice don't delete
687
+ that.errors.splice(i, 1);
688
+ }
689
+ }
690
+ }
691
+
692
+ // get this field's wrapper
693
+ let field_wrapper = closest(element, "form-field");
694
+
695
+ // clear all field errors and reset the error class on wrapper
696
+ clearFieldError(field_wrapper);
697
+
698
+ // validate this field
699
+ that.errors = [];
700
+ that._validateField(field);
701
+
702
+ // Handle displaying any errors in the form
703
+ if(that.errors.length > 0) {
704
+ // add error classes to appropriate elements
705
+ for(let j = 0; that.errors.length > j; j++) {
706
+ // get this field's wrapper
707
+ field_wrapper = closest(that.errors[j].element, "form-field");
708
+
709
+ // Create and append the error message
710
+ addFieldError(field_wrapper, that.errors[j].message);
711
+ }
712
+ }
713
+ }
714
+ };
715
+ })(validator, element);
716
+ }
717
+
718
+ /**
719
+ * setEvents()
720
+ * Sets all the events needed for the component
721
+ */
722
+ function setEvents() {
723
+ // Go through each form's rulesets
724
+ for(let n = 0; this._rules.length > n; n++) {
725
+ // Go through each ruleset
726
+ for(let e = 0; this._rules[n].length > e; e++) {
727
+ // pull out the element
728
+ let element = this._rules[n][e].element;
729
+
730
+ if (element.type === 'checkbox' || element.type === 'radio'){
731
+ // Add an onchange event
732
+ element.onchange = addBlur(this._validators[n], element);
733
+ } else {
734
+ // Add an onblur event
735
+ element.onblur = addBlur(this._validators[n], element);
736
+ }
737
+ }
738
+ }
739
+ }
740
+
741
+
742
+ /**
743
+ * removeEvents()
744
+ * removes all events from the component
745
+ */
746
+ function removeEvents() {
747
+ }
748
+
749
+
750
+ /**
751
+ * buildRules()
752
+ * builds an array of validation rules based on the DOM attributes
753
+ */
754
+ function buildRules(form) {
755
+ form = (typeof form === 'object') ? form : document.forms[form];
756
+ let elements = form.querySelectorAll('[data-validate]');
757
+ let rules = [];
758
+ let unique_rules = [];
759
+
760
+ for(let i = 0; elements.length > i; i++) {
761
+ // set up a rule for this element
762
+ let rule = {
763
+ rules: elements[i].getAttribute("data-validate"),
764
+ element: elements[i],
765
+ name: elements[i].getAttribute("name")
766
+ };
767
+
768
+ if(!rule.name) {
769
+ console.log("Warning: Field element missing name attribute.", rule.element);
770
+ }
771
+
772
+ // Add rule to the list
773
+ rules.push(rule);
774
+ }
775
+
776
+ return rules;
777
+ }
778
+
779
+ /**
780
+ * Find the closest parent containing a class
781
+ */
782
+ function closest(el, cls) {
783
+ while ((el = el.parentNode) && (-1 === el.className.indexOf(cls)));
784
+ return el;
785
+ }
786
+
787
+ /**
788
+ * Add an error message to an element
789
+ */
790
+ function addFieldError(wrapper, error) {
791
+ // Make sure the wrapper has the error class
792
+ if(-1 === wrapper.className.indexOf("form-field--error")) {
793
+ wrapper.className = wrapper.className + " form-field--error";
794
+ }
795
+ if(wrapper.querySelector('input'))
796
+ wrapper.querySelector('input').setAttribute("aria-invalid","true");
797
+ if(wrapper.querySelector('textarea'))
798
+ wrapper.querySelector('textarea').setAttribute("aria-invalid","true");
799
+ if(wrapper.querySelector('select'))
800
+ wrapper.querySelector('select').setAttribute("aria-invalid","true");
801
+
802
+ // Get all spans and check ones bearing class "form-message"
803
+ const spans = wrapper.getElementsByTagName('span');
804
+ for(let e = 0; spans.length > e; e++) {
805
+
806
+ // Somewhat of a hack to do element content comparison when
807
+ // browsers render inner tags differently
808
+ const comparison_div = document.createElement("div");
809
+ comparison_div.innerHTML = error;
810
+
811
+ // If the discovered span is a message and contains the same text as the error var
812
+ if(-1 !== spans[e].className.indexOf("form-message") && spans[e].innerHTML === comparison_div.innerHTML) {
813
+ // Don't add the same message to a wrapper twice
814
+ return;
815
+ }
816
+ }
817
+
818
+ // Add an error to the element wrapper
819
+ const msg = document.createElement("span");
820
+ msg.id = "form-message-" + wrapper.querySelector('[data-validate]').id;
821
+ msg.innerHTML = error;
822
+ msg.className = "form-message";
823
+ let idString = "";
824
+ wrapper.appendChild(msg);
825
+ if(wrapper.querySelector('.form-descriptive-copy')){
826
+ idString = wrapper.querySelector('.form-descriptive-copy').id + " " + msg.id;
827
+ }else{
828
+ idString = msg.id;
829
+ }
830
+ wrapper.querySelector('[data-validate]').setAttribute('aria-describedby', idString);
831
+ }
832
+
833
+ /**
834
+ * Remove field errors from an element
835
+ */
836
+ function clearFieldError(wrapper) {
837
+ // remove error class from wrapper
838
+ if(-1 !== wrapper.className.indexOf("form-field--error")) {
839
+ wrapper.className = wrapper.className.replace("form-field--error", "");
840
+ }
841
+ if(wrapper.querySelector('input'))
842
+ wrapper.querySelector('input').setAttribute("aria-invalid","false");
843
+ if(wrapper.querySelector('textarea'))
844
+ wrapper.querySelector('textarea').setAttribute("aria-invalid","false");
845
+ if(wrapper.querySelector('select'))
846
+ wrapper.querySelector('select').setAttribute("aria-invalid","false");
847
+ // Get all spans and remove ones bearing class "form-message"
848
+ const spans = wrapper.getElementsByTagName('span');
849
+ for(let e = 0; spans.length > e; e++) {
850
+ if(-1 !== spans[e].className.indexOf("form-message")) {
851
+ spans[e].parentNode.removeChild(spans[e]);
852
+ }
853
+ }
854
+ wrapper.querySelector('[data-validate]').removeAttribute('aria-describedby');
855
+ }
856
+
857
+ /**
858
+ * Attach additional validator rules to validator objects
859
+ */
860
+ function extendValidator(validator) {
861
+
862
+ // custom validators
863
+ validator.registerCallback('ssn', function(value, masked_ssn_id) {
864
+ // get the masked SSN
865
+ const masked_ssn = document.getElementById(masked_ssn_id);
866
+ // Test to see if the masked SSN is valid: '###-##-####'
867
+ // var ssn_regex = /^\d{3}-?\d{2}-?\d{4}$/;
868
+ const ssn_regex = /^(?!219-09-9999|078-05-1120)(?!666|000|9\d{2})\d{3}-(?!00)\d{2}-(?!0{4})\d{4}$/;
869
+ return ssn_regex.test(masked_ssn.value);
870
+ });
871
+ validator.registerCallback('phone', function(value) {
872
+ // Test to see if the Phone is valid: '(###) ###-####'
873
+ const phone_regex = /^([\(]{1}[0-9]{3}[\)]{1}[ ]?[0-9]{3}[\-]{1}[0-9]{4})$/;
874
+ return phone_regex.test(value);
875
+ });
876
+ validator.registerCallback('date', function(value) {
877
+ // Test to see if the Date is valid: '##/##/####'
878
+ const date_regex = /^((0[1-9])|(1[0-2]))[\/]((0[1-9])|([1,2][0-9])|(3[0,1]))[\/]((19|20)[0-9][0-9])$/;
879
+ if(date_regex.test(value)) {
880
+ let date_parts = value.split("/");
881
+ // check to see if the month is still the correct month after
882
+ // casting the date string to a date, This works because if a
883
+ // day is past the last day of the month the JS Date object
884
+ // advances the number of days past the month's end into the
885
+ // next month.
886
+ return ((new Date(date_parts[2], parseInt(date_parts[0])-1, date_parts[1])).getMonth() === parseInt(date_parts[0])-1);
887
+ }
888
+ return false;
889
+ });
890
+ validator.registerCallback('date_short', function(value) {
891
+ // Test to see if the Date is valid: '##/####'
892
+ const date_regex = /^((0[1-9])|(1[0-2]))[\/]((19|20)[0-9][0-9])$/;
893
+ return date_regex.test(value);
894
+ });
895
+
896
+ // set variables for regex
897
+ let today = new Date();
898
+ let currentYear = today.getFullYear();
899
+ currentYear = currentYear.toString();
900
+ let twoDigitYear = currentYear.slice(-2);
901
+ let lastYearDigit = twoDigitYear.slice(1);
902
+
903
+ let datePatternStart;
904
+ let dateCurrentYear;
905
+ let datePatternEnd;
906
+
907
+ if (twoDigitYear < 20) {
908
+ datePatternStart = '^((0[1-9])|(1[0-2]))[\\/](';
909
+ dateCurrentYear = '(1[' + lastYearDigit +'-9])|';
910
+ datePatternEnd = '([2-9][0-9]))$';
911
+ } else {
912
+ datePatternStart = '^((0[1-9])|(1[0-2]))[\\/](';
913
+ dateCurrentYear = '';
914
+ datePatternEnd = '([2-9][0-9]))$';
915
+ }
916
+
917
+ validator.registerCallback('date_short_year', function(value) {
918
+ // Test to see if the Date is valid: '##/##'
919
+ const date_regex = new RegExp(datePatternStart + dateCurrentYear + datePatternEnd);
920
+ return date_regex.test(value);
921
+ });
922
+ validator.registerCallback('radio_required', function(value, element_name) {
923
+ // See if there are any checked input elements matching the element name passed in
924
+ const radio = this.form.querySelectorAll('input[name="' + element_name + '"]:checked');
925
+ return radio.length > 0;
926
+
927
+
928
+ });
929
+ validator.registerCallback('checkbox_required', function(value, element_name) {
930
+ // See if there are any checked input elements matching the element name passed in
931
+ const cb = this.form.querySelectorAll('input[name="' + element_name + '"]:checked');
932
+ return cb.length > 0;
933
+ });
934
+ validator.registerCallback('select_required', function(value, select_id) {
935
+ // See if there are any selected option elements under the provided select
936
+ const select_element = document.getElementById(select_id);
937
+ if(select_element.options[select_element.selectedIndex] !== undefined) {
938
+ // overwrite the "value" passed into this validator
939
+ value = select_element.options[select_element.selectedIndex].value;
940
+ if(value !== undefined && value) {
941
+ return true;
942
+ }
943
+ }
944
+
945
+ return false;
946
+ });
947
+ validator.registerCallback('timeInput', function(value, masked_timeInput_id) {
948
+ // get the masked timeInput
949
+ let masked_timeInput = document.getElementById(masked_timeInput_id),
950
+ timeInput_regex = /^(0?[1-9]|1[012]):[0-5][0-9]$/,
951
+ timeInputValue = masked_timeInput.value,
952
+ initialHour,
953
+ minutes;
954
+
955
+ if (timeInputValue.length === 5) {
956
+ initialHour = timeInputValue.substring(2, 0);
957
+ minutes = timeInputValue.substring(3);
958
+ if (initialHour > 12 && initialHour < 24) {
959
+ initialHour = initialHour - 12;
960
+ setTime.call(this, masked_timeInput, initialHour, minutes);
961
+ }
962
+ if (initialHour < 1) {
963
+ initialHour = 12;
964
+ setTime.call(this, masked_timeInput, initialHour, minutes);
965
+ }
966
+ }
967
+
968
+ return timeInput_regex.test(masked_timeInput.value);
969
+ });
970
+
971
+ validator.registerCallback('validateZipCode', function(value) {
972
+ const zipCodePattern = /^\d{5}$/;
973
+ return zipCodePattern.test(value);
974
+ });
975
+
976
+ validator.registerCallback('validateZipPlus4', function(value) {
977
+ const zipCodePattern = /^\d{5}-\d{4}$/;
978
+ return value.match('_____-____') || zipCodePattern.test(value);
979
+ });
980
+
981
+ function setTime(masked_timeInput, initialHour, minutes) {
982
+ let numHour,
983
+ newValue,
984
+ hour;
985
+ numHour = String(initialHour);
986
+ if (numHour.length < 2) {
987
+ hour = '0' + numHour;
988
+ } else {
989
+ hour = numHour;
990
+ }
991
+ Number(hour);
992
+ Number(initialHour);
993
+ newValue = hour + ':' + minutes;
994
+ masked_timeInput.value = newValue;
995
+ }
996
+
997
+
998
+ // Custom validation messages
999
+ validator.setMessage('required', 'Invalid entry.<br> Please check your entry and try again.');
1000
+ validator.setMessage('numeric', 'Invalid entry.<br> Please check your entry and try again.');
1001
+ validator.setMessage('ssn', 'Please enter a valid Social Security Number.');
1002
+ validator.setMessage('phone', 'Please enter a valid Phone Number.');
1003
+ validator.setMessage('radio_required', 'Please make a selection.');
1004
+ validator.setMessage('checkbox_required', 'Please make a selection.');
1005
+ validator.setMessage('select_required', 'Please make a selection.');
1006
+ validator.setMessage('valid_email', "Invalid email.<br> Please check your entry and try again.");
1007
+ validator.setMessage('date', "Invalid date format.<br> Please check your entry and try again.");
1008
+ validator.setMessage('date_short', "Invalid date format.<br> Please check your entry and try again.");
1009
+ validator.setMessage('timeInput', 'Please enter a valid time of day.');
1010
+ validator.setMessage('date_short_year', "Invalid date format.<br> Please check your entry and try again.");
1011
+ validator.setMessage('validateZipCode', "Please enter a valid<br/>zip code.");
1012
+ validator.setMessage('validateZipPlus4', "Please enter a valid<br/>zip code.");
1013
+ }
1014
+
1015
+ /**
1016
+ * setLocalVars()
1017
+ * set all the local vars to passed in options
1018
+ */
1019
+ function setLocalVars() {
1020
+ //determine the type of content passed in
1021
+ if(this._internalVars.contentType === 'string'){
1022
+ this._internalVars.node = document.querySelector(this._options.content);
1023
+ }else if(this._internalVars.contentType === 'domNode'){
1024
+ this._internalVars.node = this._options.content;
1025
+ }
1026
+ }
1027
+
1028
+
1029
+ export default ValidateForm;
1030
+