@syncfusion/ej2-inputs 30.2.4 → 31.1.17

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 (110) hide show
  1. package/dist/ej2-inputs.min.js +2 -2
  2. package/dist/ej2-inputs.umd.min.js +2 -2
  3. package/dist/ej2-inputs.umd.min.js.map +1 -1
  4. package/dist/es6/ej2-inputs.es2015.js +1 -1
  5. package/dist/es6/ej2-inputs.es2015.js.map +1 -1
  6. package/dist/es6/ej2-inputs.es5.js +1 -1
  7. package/dist/es6/ej2-inputs.es5.js.map +1 -1
  8. package/dist/global/ej2-inputs.min.js +2 -2
  9. package/dist/global/ej2-inputs.min.js.map +1 -1
  10. package/dist/global/index.d.ts +1 -1
  11. package/dist/ts/color-picker/color-picker-model.d.ts +186 -0
  12. package/dist/ts/color-picker/color-picker.d.ts +398 -0
  13. package/dist/ts/color-picker/color-picker.ts +2209 -0
  14. package/dist/ts/color-picker/index.d.ts +5 -0
  15. package/dist/ts/color-picker/index.ts +5 -0
  16. package/dist/ts/common/index.d.ts +4 -0
  17. package/dist/ts/common/index.ts +4 -0
  18. package/dist/ts/common/signature-base.d.ts +340 -0
  19. package/dist/ts/common/signature-base.ts +1170 -0
  20. package/dist/ts/form-validator/form-validator-model.d.ts +121 -0
  21. package/dist/ts/form-validator/form-validator.d.ts +312 -0
  22. package/dist/ts/form-validator/form-validator.ts +1068 -0
  23. package/dist/ts/form-validator/index.d.ts +5 -0
  24. package/dist/ts/form-validator/index.ts +5 -0
  25. package/dist/ts/index.d.ts +18 -0
  26. package/dist/ts/index.ts +18 -0
  27. package/dist/ts/input/index.d.ts +4 -0
  28. package/dist/ts/input/index.ts +4 -0
  29. package/dist/ts/input/input.d.ts +345 -0
  30. package/dist/ts/input/input.ts +1277 -0
  31. package/dist/ts/maskedtextbox/base/index.d.ts +4 -0
  32. package/dist/ts/maskedtextbox/base/index.ts +4 -0
  33. package/dist/ts/maskedtextbox/base/mask-base.d.ts +134 -0
  34. package/dist/ts/maskedtextbox/base/mask-base.ts +1207 -0
  35. package/dist/ts/maskedtextbox/index.d.ts +5 -0
  36. package/dist/ts/maskedtextbox/index.ts +5 -0
  37. package/dist/ts/maskedtextbox/maskedtextbox/index.d.ts +5 -0
  38. package/dist/ts/maskedtextbox/maskedtextbox/index.ts +5 -0
  39. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox-model.d.ts +167 -0
  40. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox.d.ts +307 -0
  41. package/dist/ts/maskedtextbox/maskedtextbox/maskedtextbox.ts +709 -0
  42. package/dist/ts/numerictextbox/index.d.ts +5 -0
  43. package/dist/ts/numerictextbox/index.ts +5 -0
  44. package/dist/ts/numerictextbox/numerictextbox-model.d.ts +228 -0
  45. package/dist/ts/numerictextbox/numerictextbox.d.ts +431 -0
  46. package/dist/ts/numerictextbox/numerictextbox.ts +1672 -0
  47. package/dist/ts/otp-input/index.d.ts +5 -0
  48. package/dist/ts/otp-input/index.ts +5 -0
  49. package/dist/ts/otp-input/otp-input-model.d.ts +165 -0
  50. package/dist/ts/otp-input/otp-input.d.ts +365 -0
  51. package/dist/ts/otp-input/otp-input.ts +924 -0
  52. package/dist/ts/rating/index.d.ts +5 -0
  53. package/dist/ts/rating/index.ts +5 -0
  54. package/dist/ts/rating/rating-model.d.ts +239 -0
  55. package/dist/ts/rating/rating.d.ts +416 -0
  56. package/dist/ts/rating/rating.ts +1101 -0
  57. package/dist/ts/signature/index.d.ts +5 -0
  58. package/dist/ts/signature/index.ts +5 -0
  59. package/dist/ts/signature/signature-model.d.ts +131 -0
  60. package/dist/ts/signature/signature.d.ts +167 -0
  61. package/dist/ts/signature/signature.ts +266 -0
  62. package/dist/ts/slider/index.d.ts +5 -0
  63. package/dist/ts/slider/index.ts +5 -0
  64. package/dist/ts/slider/slider-model.d.ts +401 -0
  65. package/dist/ts/slider/slider.d.ts +721 -0
  66. package/dist/ts/slider/slider.ts +3497 -0
  67. package/dist/ts/smart-textarea/caret-helper.d.ts +17 -0
  68. package/dist/ts/smart-textarea/caret-helper.ts +95 -0
  69. package/dist/ts/smart-textarea/index.d.ts +5 -0
  70. package/dist/ts/smart-textarea/index.ts +5 -0
  71. package/dist/ts/smart-textarea/smart-textarea-model.d.ts +44 -0
  72. package/dist/ts/smart-textarea/smart-textarea.d.ts +80 -0
  73. package/dist/ts/smart-textarea/smart-textarea.ts +518 -0
  74. package/dist/ts/speech-to-text/index.d.ts +5 -0
  75. package/dist/ts/speech-to-text/index.ts +5 -0
  76. package/dist/ts/speech-to-text/speech-to-text-model.d.ts +270 -0
  77. package/dist/ts/speech-to-text/speech-to-text.d.ts +487 -0
  78. package/dist/ts/speech-to-text/speech-to-text.ts +1008 -0
  79. package/dist/ts/textarea/index.d.ts +5 -0
  80. package/dist/ts/textarea/index.ts +5 -0
  81. package/dist/ts/textarea/textarea-model.d.ts +161 -0
  82. package/dist/ts/textarea/textarea.d.ts +237 -0
  83. package/dist/ts/textarea/textarea.ts +812 -0
  84. package/dist/ts/textbox/index.d.ts +5 -0
  85. package/dist/ts/textbox/index.ts +5 -0
  86. package/dist/ts/textbox/textbox-model.d.ts +155 -0
  87. package/dist/ts/textbox/textbox.d.ts +288 -0
  88. package/dist/ts/textbox/textbox.ts +872 -0
  89. package/dist/ts/uploader/index.d.ts +5 -0
  90. package/dist/ts/uploader/index.ts +5 -0
  91. package/dist/ts/uploader/uploader-model.d.ts +555 -0
  92. package/dist/ts/uploader/uploader.d.ts +1216 -0
  93. package/dist/ts/uploader/uploader.ts +4391 -0
  94. package/package.json +62 -12
  95. package/src/input/input.js +1 -1
  96. package/styles/bootstrap5.3-lite.css +3 -3
  97. package/styles/bootstrap5.3.css +5 -5
  98. package/styles/input/_layout.scss +0 -1
  99. package/styles/input/tailwind3.css +0 -1
  100. package/styles/tailwind-dark-lite.css +3 -3
  101. package/styles/tailwind-dark.css +5 -5
  102. package/styles/tailwind-lite.css +3 -3
  103. package/styles/tailwind.css +5 -5
  104. package/styles/tailwind3-lite.css +0 -1
  105. package/styles/tailwind3.css +0 -1
  106. package/styles/uploader/_bootstrap5.3-definition.scss +1 -1
  107. package/styles/uploader/_tailwind-definition.scss +1 -1
  108. package/styles/uploader/bootstrap5.3.css +5 -5
  109. package/styles/uploader/tailwind-dark.css +5 -5
  110. package/styles/uploader/tailwind.css +5 -5
@@ -0,0 +1,1068 @@
1
+ import { selectAll, select, createElement, Base, EmitType, detach } from '@syncfusion/ej2-base';
2
+ import { extend, isNullOrUndefined, IKeyValue, EventHandler } from '@syncfusion/ej2-base';
3
+ import { Property, NotifyPropertyChanges, INotifyPropertyChanged, Event, onIntlChange } from '@syncfusion/ej2-base';
4
+ import { Internationalization, L10n } from '@syncfusion/ej2-base';
5
+ import { FormValidatorModel } from './form-validator-model';
6
+
7
+ /**
8
+ * global declarations
9
+ */
10
+ export const regex: { [key: string]: RegExp } = {
11
+ /* eslint-disable no-useless-escape */
12
+ EMAIL: new RegExp('^[A-Za-z0-9._%+-]{1,}@[A-Za-z0-9._%+-]{1,}([.]{1}[a-zA-Z0-9]{2,}' +
13
+ '|[.]{1}[a-zA-Z0-9]{2,4}[.]{1}[a-zA-Z0-9]{2,4})$'),
14
+ /* eslint-disable-next-line security/detect-unsafe-regex */
15
+ URL: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/m,
16
+ DATE_ISO: new RegExp('^([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$'),
17
+ DIGITS: new RegExp('^[0-9]*$'),
18
+ PHONE: new RegExp('^[+]?[0-9]{9,13}$'),
19
+ CREDITCARD: new RegExp('^\\d{13,16}$')
20
+ /* eslint-enable no-useless-escape */
21
+ };
22
+
23
+ /**
24
+ * ErrorOption values
25
+ *
26
+ * @private
27
+ */
28
+ export enum ErrorOption {
29
+ /**
30
+ * Defines the error message.
31
+ */
32
+ Message,
33
+ /**
34
+ * Defines the error element type.
35
+ */
36
+ Label
37
+ }
38
+
39
+ /**
40
+ * FormValidator class enables you to validate the form fields based on your defined rules
41
+ * ```html
42
+ * <form id='formId'>
43
+ * <input type='text' name='Name' />
44
+ * <input type='text' name='Age' />
45
+ * </form>
46
+ * <script>
47
+ * let formObject = new FormValidator('#formId', {
48
+ * rules: { Name: { required: true }, Age: { range: [18, 30] } };
49
+ * });
50
+ * formObject.validate();
51
+ * </script>
52
+ * ```
53
+ */
54
+ @NotifyPropertyChanges
55
+ export class FormValidator extends Base<HTMLFormElement> implements INotifyPropertyChanged {
56
+ private validated: string[] = [];
57
+ private errorRules: ErrorRule[] = [];
58
+ private allowSubmit: boolean = false;
59
+ private required: string = 'required';
60
+ private infoElement: HTMLElement = null;
61
+ private inputElement: HTMLInputElement = null;
62
+ private selectQuery: string = 'input:not([type=reset]):not([type=button]), select, textarea';
63
+ private inputElements: HTMLInputElement[];
64
+ private l10n: L10n;
65
+ private internationalization: Internationalization;
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
+ private localyMessage: any = {};
68
+ /**
69
+ * default locale variable
70
+ */
71
+ @Property('')
72
+ public locale: string;
73
+
74
+
75
+ /**
76
+ * Ignores input fields based on the class name
77
+ *
78
+ * @default 'e-hidden'
79
+ */
80
+ @Property('e-hidden')
81
+ public ignore: string;
82
+
83
+ /**
84
+ * Maps the input fields with validation rules
85
+ *
86
+ * @default {}
87
+ */
88
+ @Property()
89
+ public rules: { [name: string]: { [rule: string]: Object } };
90
+
91
+ /**
92
+ * Sets the defined css class to error fields
93
+ *
94
+ * @default 'e-error'
95
+ */
96
+ @Property('e-error')
97
+ public errorClass: string;
98
+
99
+ /**
100
+ * Sets the defined css class to valid fields
101
+ *
102
+ * @default 'e-valid'
103
+ */
104
+ @Property('e-valid')
105
+ public validClass: string;
106
+
107
+ /**
108
+ * Specify HTML element for error
109
+ *
110
+ * @default 'label'
111
+ */
112
+ @Property('label')
113
+ public errorElement: string;
114
+
115
+ /**
116
+ * Specify HTML element for error container
117
+ *
118
+ * @default 'div'
119
+ */
120
+ @Property('div')
121
+ public errorContainer: string;
122
+
123
+ /**
124
+ * Option to display the error
125
+ *
126
+ * @default ErrorOption.Label
127
+ * @deprecated
128
+ */
129
+ @Property(ErrorOption.Label)
130
+ public errorOption: ErrorOption;
131
+
132
+ /**
133
+ * Triggers when a field's focused out
134
+ *
135
+ * @event focusout
136
+ */
137
+ @Event()
138
+ public focusout: EmitType<Event>;
139
+
140
+ /**
141
+ * Trigger when keyup is triggered in any fields
142
+ *
143
+ * @event keyup
144
+ */
145
+ @Event()
146
+ public keyup: EmitType<KeyboardEvent>;
147
+
148
+ /**
149
+ * Triggers when a check box field is clicked
150
+ *
151
+ * @event click
152
+ */
153
+ @Event()
154
+ public click: EmitType<Event>;
155
+
156
+ /**
157
+ * Trigger when a select/drop-down field is changed
158
+ *
159
+ * @event change
160
+ */
161
+ @Event()
162
+ public change: EmitType<Event>;
163
+
164
+ /**
165
+ * Triggers before form is being submitted
166
+ *
167
+ * @event submit
168
+ */
169
+ @Event()
170
+ public submit: EmitType<Event>;
171
+
172
+ /**
173
+ * Triggers before validation starts
174
+ *
175
+ * @event validationBegin
176
+ */
177
+ @Event()
178
+ public validationBegin: EmitType<Object | ValidArgs>;
179
+
180
+ /**
181
+ * Triggers after validation is completed
182
+ *
183
+ * @event validationComplete
184
+ */
185
+ @Event()
186
+ public validationComplete: EmitType<Object | FormEventArgs>;
187
+
188
+ /**
189
+ * Assigns the custom function to place the error message in the page.
190
+ *
191
+ * @event customPlacement
192
+ */
193
+ /* eslint-disable @typescript-eslint/no-explicit-any */
194
+ @Event()
195
+ public customPlacement: EmitType<HTMLElement | any>;
196
+
197
+ /* eslint-enable @typescript-eslint/no-explicit-any */
198
+
199
+ /**
200
+ * Add validation rules to the corresponding input element based on `name` attribute.
201
+ *
202
+ * @param {string} name `name` of form field.
203
+ * @param {Object} rules Validation rules for the corresponding element.
204
+ * @returns {void}
205
+ */
206
+ public addRules(name: string, rules: Object): void {
207
+ if (name) {
208
+ // eslint-disable-next-line no-prototype-builtins
209
+ if (this.rules.hasOwnProperty(name)) {
210
+ extend(this.rules[`${name}`], rules, {});
211
+ } else {
212
+ this.rules[`${name}`] = <IKeyValue>rules;
213
+ }
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Remove validation to the corresponding field based on name attribute.
219
+ * When no parameter is passed, remove all the validations in the form.
220
+ *
221
+ * @param {string} name Input name attribute value.
222
+ * @param {string[]} rules List of validation rules need to be remove from the corresponding element.
223
+ * @returns {void}
224
+ */
225
+ public removeRules(name?: string, rules?: string[]): void {
226
+ if (!name && !rules) {
227
+ this.rules = {};
228
+ } else if (this.rules[`${name}`] && !rules) {
229
+ delete this.rules[`${name}`];
230
+ } else if (!isNullOrUndefined(this.rules[`${name}`] && rules)) {
231
+ for (let i: number = 0; i < rules.length; i++) {
232
+ delete this.rules[`${name}`][rules[parseInt(i.toString(), 10)]];
233
+ }
234
+ } else {
235
+ return;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Validate the current form values using defined rules.
241
+ * Returns `true` when the form is valid otherwise `false`
242
+ *
243
+ * @param {string} selected - Optional parameter to validate specified element.
244
+ * @returns {boolean} - Returns form validation status.
245
+ */
246
+ public validate(selected?: string): boolean {
247
+ const rules: string[] = Object.keys(this.rules);
248
+ if (selected && rules.length) {
249
+ this.validateRules(selected);
250
+ //filter the selected element it don't have any valid input element
251
+ return rules.indexOf(selected) !== -1 && this.errorRules.filter((data: ErrorRule) => {
252
+ return data.name === selected;
253
+ }).length === 0;
254
+ } else {
255
+ this.errorRules = [];
256
+ for (const name of rules) {
257
+ this.validateRules(name);
258
+ }
259
+ return this.errorRules.length === 0;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Reset the value of all the fields in form.
265
+ *
266
+ * @returns {void}
267
+ */
268
+ public reset(): void {
269
+ this.element.reset();
270
+ this.clearForm();
271
+ }
272
+
273
+ /**
274
+ * Get input element by name.
275
+ *
276
+ * @param {string} name - Input element name attribute value.
277
+ * @returns {HTMLInputElement} - Returns the input element.
278
+ */
279
+ public getInputElement(name: string): HTMLInputElement {
280
+ this.inputElement = <HTMLInputElement>(select('[name="' + name + '"]', this.element));
281
+ return this.inputElement;
282
+ }
283
+
284
+ /**
285
+ * Destroy the form validator object and error elements.
286
+ *
287
+ * @returns {void}
288
+ */
289
+ public destroy(): void {
290
+ this.reset();
291
+ this.unwireEvents();
292
+ this.rules = {};
293
+ const elements: HTMLElement[] = selectAll('.' + this.errorClass + ', .' + this.validClass, this.element);
294
+ for (const element of elements) {
295
+ detach(element);
296
+ }
297
+ super.destroy();
298
+ this.infoElement = null;
299
+ onIntlChange.off('notifyExternalChange', this.afterLocalization);
300
+ }
301
+
302
+ /**
303
+ * Specifies the default messages for validation rules.
304
+ *
305
+ * @default { List of validation message }
306
+ */
307
+ public defaultMessages: { [rule: string]: string } = {
308
+ required: 'This field is required.',
309
+ email: 'Please enter a valid email address.',
310
+ url: 'Please enter a valid URL.',
311
+ date: 'Please enter a valid date.',
312
+ dateIso: 'Please enter a valid date ( ISO ).',
313
+ creditcard: 'Please enter valid card number',
314
+ number: 'Please enter a valid number.',
315
+ digits: 'Please enter only digits.',
316
+ maxLength: 'Please enter no more than {0} characters.',
317
+ minLength: 'Please enter at least {0} characters.',
318
+ rangeLength: 'Please enter a value between {0} and {1} characters long.',
319
+ range: 'Please enter a value between {0} and {1}.',
320
+ max: 'Please enter a value less than or equal to {0}.',
321
+ min: 'Please enter a value greater than or equal to {0}.',
322
+ regex: 'Please enter a correct value.',
323
+ tel: 'Please enter a valid phone number.',
324
+ pattern: 'Please enter a correct pattern value.',
325
+ equalTo: 'Please enter the valid match text'
326
+ };
327
+
328
+ /**
329
+ * @param {FormValidatorModel} newProp - Returns the dynamic property value of the component.
330
+ * @param {FormValidatorModel} oldProp - Returns the previous property value of the component.
331
+ * @returns {void}
332
+ * @private
333
+ */
334
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
335
+ public onPropertyChanged(newProp: FormValidatorModel, oldProp?: FormValidatorModel): void {
336
+ for (const prop of Object.keys(newProp)) {
337
+ switch (prop) {
338
+ case 'locale':
339
+ this.localeFunc();
340
+ break;
341
+ }
342
+ }
343
+ }
344
+
345
+ /**
346
+ * @private
347
+ * @returns {void}
348
+ */
349
+ public localeFunc(): void {
350
+ for (const key of Object.keys(this.defaultMessages)) {
351
+ this.l10n.setLocale(this.locale);
352
+ const value: string = this.l10n.getConstant(key);
353
+ this.localyMessage[`${key}`] = value;
354
+ }
355
+ }
356
+
357
+
358
+ /**
359
+ * @private
360
+ * @returns {string} - Returns the component name.
361
+ */
362
+ public getModuleName(): string {
363
+ return 'formvalidator';
364
+ }
365
+
366
+ /**
367
+ * @param {any} args - Specifies the culture name.
368
+ * @returns {void}
369
+ * @private
370
+ */
371
+ public afterLocalization(args: any): void {
372
+ this.locale = args.locale;
373
+ this.localeFunc();
374
+ }
375
+
376
+ /**
377
+ * Allows you to refresh the form validator base events to the elements inside the form.
378
+ *
379
+ * @returns {void}
380
+ */
381
+ public refresh(): void {
382
+ this.unwireEvents();
383
+ this.inputElements = <HTMLInputElement[]>selectAll(this.selectQuery, this.element);
384
+ this.wireEvents();
385
+ }
386
+
387
+ /**
388
+ * Initializes the FormValidator.
389
+ *
390
+ * @param {string | HTMLFormElement} element - Specifies the element to render as component.
391
+ * @param {FormValidatorModel} options - Specifies the FormValidator model.
392
+ * @private
393
+ */
394
+ public constructor(element: string | HTMLFormElement, options?: FormValidatorModel) {
395
+ super(options, element);
396
+ if (typeof this.rules === 'undefined') {
397
+ this.rules = {};
398
+ }
399
+ this.l10n = new L10n('formValidator', this.defaultMessages, this.locale);
400
+ if (this.locale) {
401
+ this.localeFunc();
402
+ }
403
+ onIntlChange.on('notifyExternalChange', this.afterLocalization, this);
404
+
405
+ element = typeof element === 'string' ? <HTMLFormElement>select(element, document) : element;
406
+ // Set novalidate to prevent default HTML5 form validation
407
+ if (this.element != null) {
408
+ this.element.setAttribute('novalidate', '');
409
+ this.inputElements = <HTMLInputElement[]>selectAll(this.selectQuery, this.element);
410
+ this.createHTML5Rules();
411
+ this.wireEvents();
412
+ } else {
413
+ return undefined;
414
+ }
415
+ }
416
+
417
+ private clearForm(): void {
418
+ this.errorRules = [];
419
+ this.validated = [];
420
+ const elements: HTMLElement[] = selectAll(this.selectQuery, this.element);
421
+ for (const element of elements) {
422
+ const input: HTMLInputElement = <HTMLInputElement>element;
423
+ input.removeAttribute('aria-invalid');
424
+ const inputParent: HTMLElement = input.parentElement;
425
+ const grandParent: HTMLElement = inputParent.parentElement;
426
+ if (inputParent.classList.contains('e-control-wrapper') || inputParent.classList.contains('e-wrapper') ||
427
+ (input.classList.contains('e-input') && inputParent.classList.contains('e-input-group'))) {
428
+ inputParent.classList.remove(this.errorClass);
429
+ }
430
+ else if ((grandParent != null) && (grandParent.classList.contains('e-control-wrapper') ||
431
+ grandParent.classList.contains('e-wrapper'))) {
432
+ grandParent.classList.remove(this.errorClass);
433
+ }
434
+ else {
435
+ input.classList.remove(this.errorClass);
436
+ }
437
+ if (input.name.length > 0) {
438
+ this.getInputElement(input.name);
439
+ this.getErrorElement(input.name);
440
+ this.hideMessage(input.name);
441
+ }
442
+ if (inputParent.classList.contains('e-control-wrapper') || inputParent.classList.contains('e-wrapper') ||
443
+ (input.classList.contains('e-input') && inputParent.classList.contains('e-input-group'))) {
444
+ inputParent.classList.remove(this.validClass);
445
+ }
446
+ else if ((grandParent != null) && (grandParent.classList.contains('e-control-wrapper') ||
447
+ grandParent.classList.contains('e-wrapper'))) {
448
+ grandParent.classList.remove(this.validClass);
449
+ }
450
+ else {
451
+ input.classList.remove(this.validClass);
452
+ }
453
+ }
454
+ }
455
+
456
+ private createHTML5Rules(): void {
457
+ const defRules: string[] = ['required', 'validateHidden', 'regex', 'rangeLength', 'maxLength', 'minLength', 'dateIso', 'digits',
458
+ 'pattern', 'data-val-required', 'type', 'data-validation', 'min', 'max', 'range', 'equalTo', 'data-val-minlength-min',
459
+ 'data-val-equalto-other', 'data-val-maxlength-max', 'data-val-range-min', 'data-val-regex-pattern', 'data-val-length-max',
460
+ 'data-val-creditcard', 'data-val-phone'];
461
+ const acceptedTypes: string[] = ['hidden', 'email', 'url', 'date', 'number', 'tel'];
462
+ for (const input of (this.inputElements)) {
463
+ // Default attribute rules
464
+ const allRule: { [key: string]: Object } = {};
465
+ for (let rule of defRules) {
466
+ if (input.getAttribute(rule) !== null) {
467
+ switch (rule) {
468
+ case 'required':
469
+ this.defRule(input, allRule, rule, input.required);
470
+ break;
471
+ case 'data-validation':
472
+ rule = input.getAttribute(rule);
473
+ this.defRule(input, allRule, rule, true);
474
+ break;
475
+ case 'type':
476
+ if (acceptedTypes.indexOf(input.type) !== -1) {
477
+ this.defRule(input, allRule, input.type, true);
478
+ }
479
+ break;
480
+ case 'rangeLength':
481
+ case 'range':
482
+ this.defRule(input, allRule, rule, JSON.parse(input.getAttribute(rule)));
483
+ break;
484
+ case 'equalTo': {
485
+ const id: string = input.getAttribute(rule);
486
+ this.defRule(input, allRule, rule, id);
487
+ }
488
+ break;
489
+ default:
490
+ if (input.getAttribute('data-val') === 'true') {
491
+ this.annotationRule(input, allRule, rule, input.getAttribute(rule));
492
+ } else {
493
+ this.defRule(input, allRule, rule, input.getAttribute(rule));
494
+ }
495
+ }
496
+ }
497
+ }
498
+ //adding pattern type validation
499
+ if (Object.keys(allRule).length !== 0) {
500
+ this.addRules(input.name, allRule);
501
+ }
502
+ }
503
+ }
504
+
505
+ private annotationRule(input: HTMLInputElement, ruleCon: { [key: string]: Object }, ruleName: string, value: Object): void {
506
+ const annotationRule: string[] = ruleName.split('-');
507
+ const rulesList: string[] = ['required', 'creditcard', 'phone', 'maxlength', 'minlength', 'range', 'regex', 'equalto'];
508
+ const ruleFirstName: string = annotationRule[annotationRule.length - 1];
509
+ const ruleSecondName: string = annotationRule[annotationRule.length - 2];
510
+ if (rulesList.indexOf(ruleFirstName) !== -1) {
511
+ switch (ruleFirstName) {
512
+ case 'required':
513
+ this.defRule(input, ruleCon, 'required', value);
514
+ break;
515
+ case 'creditcard':
516
+ this.defRule(input, ruleCon, 'creditcard', value);
517
+ break;
518
+ case 'phone':
519
+ this.defRule(input, ruleCon, 'tel', value);
520
+ break;
521
+ }
522
+ } else if (rulesList.indexOf(ruleSecondName) !== -1) {
523
+ switch (ruleSecondName) {
524
+ case 'maxlength':
525
+ this.defRule(input, ruleCon, 'maxLength', value);
526
+ break;
527
+ case 'minlength':
528
+ this.defRule(input, ruleCon, 'minLength', value);
529
+ break;
530
+ case 'range': {
531
+ const minvalue: string = input.getAttribute('data-val-range-min');
532
+ const maxvalue: string = input.getAttribute('data-val-range-max');
533
+ this.defRule(input, ruleCon, 'range', [minvalue, maxvalue]);
534
+ }
535
+ break;
536
+ case 'equalto': {
537
+ const id: string[] = input.getAttribute(ruleName).split('.');
538
+ this.defRule(input, ruleCon, 'equalTo', id[id.length - 1]);
539
+ }
540
+ break;
541
+ case 'regex':
542
+ this.defRule(input, ruleCon, 'regex', value);
543
+ break;
544
+ }
545
+ }
546
+ }
547
+
548
+ private defRule(input: HTMLInputElement, ruleCon: { [key: string]: Object }, ruleName: string, value: Object): void {
549
+ const message: string = input.getAttribute('data-' + ruleName + '-message');
550
+ const annotationMessage: string = input.getAttribute('data-val-' + ruleName);
551
+ let customMessage: string;
552
+ if (this.rules[input.name] && ruleName !== 'validateHidden' && ruleName !== 'hidden') {
553
+ this.getInputElement(input.name);
554
+ customMessage = this.getErrorMessage(this.rules[input.name][`${ruleName}`], ruleName);
555
+ }
556
+ if (message) {
557
+ value = [value, message];
558
+ } else if (annotationMessage) {
559
+ value = [value, annotationMessage];
560
+ } else if (customMessage) {
561
+ value = [value, customMessage];
562
+ }
563
+ ruleCon[`${ruleName}`] = value;
564
+ }
565
+
566
+ // Wire events to the form elements
567
+ private wireEvents(): void {
568
+ for (const input of (this.inputElements)) {
569
+ if (FormValidator.isCheckable(input)) {
570
+ EventHandler.add(input, 'click', this.clickHandler, this);
571
+ } else if (input.tagName === 'SELECT') {
572
+ EventHandler.add(input, 'change', this.changeHandler, this);
573
+ } else {
574
+ EventHandler.add(input, 'focusout', this.focusOutHandler, this);
575
+ EventHandler.add(input, 'keyup', this.keyUpHandler, this);
576
+ }
577
+ }
578
+ EventHandler.add(this.element, 'submit', this.submitHandler, this);
579
+ EventHandler.add(this.element, 'reset', this.resetHandler, this);
580
+ }
581
+
582
+ // UnWire events to the form elements
583
+ private unwireEvents(): void {
584
+ for (const input of (this.inputElements)) {
585
+ EventHandler.clearEvents(input);
586
+ }
587
+ EventHandler.remove(this.element, 'submit', this.submitHandler);
588
+ EventHandler.remove(this.element, 'reset', this.resetHandler);
589
+ }
590
+
591
+ // Handle input element focusout event
592
+ private focusOutHandler(e: Event): void {
593
+ this.trigger('focusout', e);
594
+ //FormValidator.triggerCallback(this.focusout, e);
595
+ const element: HTMLInputElement = <HTMLInputElement>e.target;
596
+ if (this.rules[element.name]) {
597
+ if (this.rules[element.name][this.required] || element.value.length > 0) {
598
+ this.validate(element.name);
599
+ } else if (this.validated.indexOf(element.name) === -1) {
600
+ this.validated.push(element.name);
601
+ }
602
+ }
603
+ }
604
+
605
+ // Handle input element keyup event
606
+ private keyUpHandler(e: KeyboardEvent): void {
607
+ this.trigger('keyup', e);
608
+ const element: HTMLInputElement = <HTMLInputElement>e.target;
609
+ // List of keys need to prevent while validation
610
+ const excludeKeys: number[] = [16, 17, 18, 20, 35, 36, 37, 38, 39, 40, 45, 144, 225];
611
+ if (e.which === 9 && (!this.rules[element.name] || (this.rules[element.name] && !this.rules[element.name][this.required]))) {
612
+ return;
613
+ }
614
+ if (this.validated.indexOf(element.name) !== -1 && this.rules[element.name] && excludeKeys.indexOf(e.which) === -1) {
615
+ this.validate(element.name);
616
+ }
617
+ }
618
+
619
+ // Handle input click event
620
+ private clickHandler(e: Event): void {
621
+ this.trigger('click', e);
622
+ const element: HTMLInputElement = <HTMLInputElement>e.target;
623
+ // If element type is not submit allow validation
624
+ if (element.type !== 'submit') {
625
+ this.validate(element.name);
626
+ } else if (element.getAttribute('formnovalidate') !== null) {
627
+ // Prevent form validation, if submit button has formnovalidate attribute
628
+ this.allowSubmit = true;
629
+ }
630
+ }
631
+
632
+ // Handle input change event
633
+ private changeHandler(e: Event): void {
634
+ this.trigger('change', e);
635
+ const element: HTMLInputElement = <HTMLInputElement>e.target;
636
+ this.validate(element.name);
637
+ }
638
+
639
+ // Handle form submit event
640
+ private submitHandler(e: Event): void {
641
+ this.trigger('submit', e);
642
+ //FormValidator.triggerCallback(this.submit, e);
643
+ // Prevent form submit if validation failed
644
+ if (!this.allowSubmit && !this.validate()) {
645
+ e.preventDefault();
646
+ } else {
647
+ this.allowSubmit = false;
648
+ }
649
+ }
650
+
651
+ // Handle form reset
652
+ private resetHandler(): void {
653
+ this.clearForm();
654
+ }
655
+
656
+ // Validate each rule based on input element name
657
+ private validateRules(name: string): void {
658
+ if (!this.rules[`${name}`]) {
659
+ return;
660
+ }
661
+ const rules: string[] = Object.keys(this.rules[`${name}`]);
662
+ let hiddenType: boolean = false;
663
+ let validateHiddenType: boolean = false;
664
+ const vhPos: number = rules.indexOf('validateHidden');
665
+ const hPos: number = rules.indexOf('hidden');
666
+ this.getInputElement(name);
667
+ if (hPos !== -1) {
668
+ hiddenType = true;
669
+ }
670
+ if (vhPos !== -1) {
671
+ validateHiddenType = true;
672
+ }
673
+ if (!hiddenType || (hiddenType && validateHiddenType)) {
674
+ if (vhPos !== -1) {
675
+ rules.splice(vhPos, 1);
676
+ }
677
+ if (hPos !== -1) {
678
+ rules.splice((hPos - 1), 1);
679
+ }
680
+ this.getErrorElement(name);
681
+ for (const rule of rules) {
682
+ const errorMessage: string = this.getErrorMessage(this.rules[`${name}`][`${rule}`], rule);
683
+ const errorRule: ErrorRule = { name: name, message: errorMessage };
684
+ const eventArgs: FormEventArgs = {
685
+ inputName: name,
686
+ element: this.inputElement,
687
+ message: errorMessage
688
+ };
689
+ if (!this.isValid(name, rule) && !this.inputElement.classList.contains(this.ignore)) {
690
+ this.removeErrorRules(name);
691
+ this.errorRules.push(errorRule);
692
+ // Set aria attributes to invalid elements
693
+ this.inputElement.setAttribute('aria-invalid', 'true');
694
+ this.inputElement.setAttribute('aria-describedby', this.inputElement.id + '-info');
695
+ const inputParent: HTMLElement = this.inputElement.parentElement;
696
+ const grandParent: HTMLElement = inputParent.parentElement;
697
+ if (inputParent.classList.contains('e-control-wrapper') || inputParent.classList.contains('e-wrapper') ||
698
+ (this.inputElement.classList.contains('e-input') && inputParent.classList.contains('e-input-group'))) {
699
+ inputParent.classList.add(this.errorClass);
700
+ inputParent.classList.remove(this.validClass);
701
+ }
702
+ else if ((grandParent != null) && (grandParent.classList.contains('e-control-wrapper') ||
703
+ grandParent.classList.contains('e-wrapper'))) {
704
+ grandParent.classList.add(this.errorClass);
705
+ grandParent.classList.remove(this.validClass);
706
+ }
707
+ else {
708
+ this.inputElement.classList.add(this.errorClass);
709
+ this.inputElement.classList.remove(this.validClass);
710
+ }
711
+ if (!this.infoElement) {
712
+ this.createErrorElement(name, errorRule.message, this.inputElement);
713
+ } else {
714
+ this.showMessage(errorRule);
715
+ }
716
+ eventArgs.errorElement = this.infoElement;
717
+ eventArgs.status = 'failure';
718
+ if (inputParent.classList.contains('e-control-wrapper') || inputParent.classList.contains('e-wrapper') ||
719
+ (this.inputElement.classList.contains('e-input') && inputParent.classList.contains('e-input-group'))) {
720
+ inputParent.classList.add(this.errorClass);
721
+ inputParent.classList.remove(this.validClass);
722
+ }
723
+ else if ((grandParent != null) && (grandParent.classList.contains('e-control-wrapper') ||
724
+ grandParent.classList.contains('e-wrapper'))) {
725
+ grandParent.classList.add(this.errorClass);
726
+ grandParent.classList.remove(this.validClass);
727
+ }
728
+ else {
729
+ this.inputElement.classList.add(this.errorClass);
730
+ this.inputElement.classList.remove(this.validClass);
731
+ }
732
+ this.optionalValidationStatus(name, eventArgs);
733
+ this.trigger('validationComplete', eventArgs);
734
+ // Set aria-required to required rule elements
735
+ if (rule === 'required') {
736
+ this.inputElement.setAttribute('aria-required', 'true');
737
+ }
738
+ break;
739
+ } else {
740
+ this.hideMessage(name);
741
+ eventArgs.status = 'success';
742
+ this.trigger('validationComplete', eventArgs);
743
+ }
744
+ }
745
+ } else {
746
+ return;
747
+ }
748
+ }
749
+
750
+ // Update the optional validation status
751
+ private optionalValidationStatus(name: string, refer: FormEventArgs): void {
752
+ if (!this.rules[`${name}`][this.required] && !this.inputElement.value.length && !isNullOrUndefined(this.infoElement)) {
753
+ this.infoElement.innerHTML = this.inputElement.value;
754
+ this.infoElement.setAttribute('aria-invalid', 'false');
755
+ refer.status = '';
756
+ this.hideMessage(name);
757
+ }
758
+ }
759
+
760
+ // Check the input element whether it's value satisfy the validation rule or not
761
+ private isValid(name: string, rule: string): boolean {
762
+ const params: Object = this.rules[`${name}`][`${rule}`];
763
+ const param: Object = (params instanceof Array && typeof params[1] === 'string') ? params[0] : params;
764
+ const currentRule: { [key: string]: Object } = <IKeyValue>this.rules[`${name}`][`${rule}`];
765
+ const dateFormat: string = ((rule === 'min' || rule === 'max') && this.rules['' + name].date &&
766
+ typeof (this.rules['' + name].date) === 'string') ? this.rules['' + name].date as string : null;
767
+ const args: ValidArgs = { value: this.inputElement.value,
768
+ param: param, element: this.inputElement, formElement: this.element, format: dateFormat, culture: this.locale };
769
+ this.trigger('validationBegin', args);
770
+ if (!args.param && rule === 'required') {
771
+ return true;
772
+ }
773
+ if (currentRule && typeof currentRule[0] === 'function') {
774
+ const fn: () => boolean = <() => boolean>currentRule[0];
775
+ return fn.call(this, { element: this.inputElement, value: this.inputElement.value });
776
+ } else if (FormValidator.isCheckable(this.inputElement)) {
777
+ if (rule !== 'required') {
778
+ return true;
779
+ }
780
+ return selectAll('input[name="' + name + '"]:checked', this.element).length > 0;
781
+ } else {
782
+ return FormValidator.checkValidator[`${rule}`](args);
783
+ }
784
+ }
785
+
786
+ // Return default error message or custom error message
787
+ private getErrorMessage(ruleValue: Object, rule: string): string {
788
+ let message: string = this.inputElement.getAttribute('data-' + rule + '-message') ?
789
+ this.inputElement.getAttribute('data-' + rule + '-message') :
790
+ (ruleValue instanceof Array && typeof ruleValue[1] === 'string') ? ruleValue[1] :
791
+ (Object.keys(this.localyMessage).length !== 0) ? this.localyMessage[`${rule}`] : this.defaultMessages[`${rule}`];
792
+ const formats: string[] = message.match(/{(\d)}/g);
793
+ if (!isNullOrUndefined(formats)) {
794
+ for (let i: number = 0; i < formats.length; i++) {
795
+ const value: string = ruleValue instanceof Array ? ruleValue[parseInt(i.toString(), 10)] : ruleValue;
796
+ message = message.replace(formats[parseInt(i.toString(), 10)], value);
797
+ }
798
+ }
799
+ return message;
800
+ }
801
+
802
+ // Create error element based on name and error message
803
+ private createErrorElement(name: string, message: string, input: HTMLInputElement): void {
804
+ let errorElement: HTMLElement = createElement(this.errorElement, {
805
+ className: this.errorClass,
806
+ innerHTML: message,
807
+ attrs: { for: name }
808
+ });
809
+ // Create message design if errorOption is message
810
+ if (this.errorOption === ErrorOption.Message) {
811
+ errorElement.classList.remove(this.errorClass);
812
+ errorElement.classList.add('e-message');
813
+ errorElement = createElement(this.errorContainer, { className: this.errorClass, innerHTML: errorElement.outerHTML });
814
+ }
815
+ errorElement.id = this.inputElement.name + '-info';
816
+
817
+ // Append error message into MVC error message element
818
+ if (this.element.querySelector('[data-valmsg-for="' + input.id + '"]')) {
819
+ this.element.querySelector('[data-valmsg-for="' + input.id + '"]').appendChild(errorElement);
820
+ } else if (input.hasAttribute('data-msg-containerid') === true) {
821
+ // Append error message into custom div element
822
+ const containerId: string = input.getAttribute('data-msg-containerid');
823
+ const divElement: Element = select('#' + containerId, this.element);
824
+ divElement.appendChild(errorElement);
825
+ } else if (this.customPlacement != null) {
826
+ // Call custom placement function if customPlacement is not null
827
+ this.customPlacement.call(this, this.inputElement, errorElement);
828
+ } else {
829
+ const inputParent: HTMLElement = this.inputElement.parentElement;
830
+ const grandParent: HTMLElement = inputParent.parentElement;
831
+ if (inputParent.classList.contains('e-control-wrapper') || inputParent.classList.contains('e-wrapper')) {
832
+ grandParent.insertBefore(errorElement, inputParent.nextSibling);
833
+ }
834
+ else if (grandParent.classList.contains('e-control-wrapper') || grandParent.classList.contains('e-wrapper')) {
835
+ grandParent.parentElement.insertBefore(errorElement, grandParent.nextSibling);
836
+ }
837
+ else {
838
+ inputParent.insertBefore(errorElement, this.inputElement.nextSibling);
839
+ }
840
+ }
841
+ errorElement.style.display = 'block';
842
+ this.getErrorElement(name);
843
+ this.validated.push(name);
844
+ this.checkRequired(name);
845
+ }
846
+
847
+ // Get error element by name
848
+ private getErrorElement(name: string): HTMLElement {
849
+ this.infoElement = <HTMLElement>select(this.errorElement + '.' + this.errorClass, this.inputElement.parentElement);
850
+ if (!this.infoElement) {
851
+ this.infoElement = (<HTMLElement>select(this.errorElement + '.' + this.errorClass + '[for="' + name + '"]', this.element));
852
+ }
853
+ return this.infoElement;
854
+ }
855
+
856
+ // Remove existing rule from errorRules object
857
+ private removeErrorRules(name: string): void {
858
+ for (let i: number = 0; i < this.errorRules.length; i++) {
859
+ const rule: ErrorRule = this.errorRules[parseInt(i.toString(), 10)];
860
+ if (rule.name === name) {
861
+ this.errorRules.splice(i, 1);
862
+ }
863
+ }
864
+ }
865
+
866
+ // Show error message to the input element
867
+ private showMessage(errorRule: ErrorRule): void {
868
+ this.infoElement.style.display = 'block';
869
+ this.infoElement.innerHTML = errorRule.message;
870
+ this.checkRequired(errorRule.name);
871
+ }
872
+
873
+ // Hide error message based on input name
874
+ private hideMessage(name: string): void {
875
+ if (this.infoElement) {
876
+ this.infoElement.style.display = 'none';
877
+ this.removeErrorRules(name);
878
+ const inputParent: HTMLElement = this.inputElement.parentElement;
879
+ const grandParent: HTMLElement = inputParent.parentElement;
880
+ if (inputParent.classList.contains('e-control-wrapper') || inputParent.classList.contains('e-wrapper') ||
881
+ (this.inputElement.classList.contains('e-input') && inputParent.classList.contains('e-input-group'))) {
882
+ inputParent.classList.add(this.validClass);
883
+ inputParent.classList.remove(this.errorClass);
884
+ }
885
+ else if ((grandParent != null) && (grandParent.classList.contains('e-control-wrapper') || grandParent.classList.contains('e-wrapper'))) {
886
+ grandParent.classList.add(this.validClass);
887
+ grandParent.classList.remove(this.errorClass);
888
+ }
889
+ else {
890
+ this.inputElement.classList.add(this.validClass);
891
+ this.inputElement.classList.remove(this.errorClass);
892
+ }
893
+ this.inputElement.setAttribute('aria-invalid', 'false');
894
+ }
895
+ }
896
+
897
+ // Check whether the input element have required rule and its value is not empty
898
+ private checkRequired(name: string): void {
899
+ if (!this.rules[`${name}`][this.required] && !this.inputElement.value.length && !isNullOrUndefined(this.infoElement)) {
900
+ this.infoElement.innerHTML = this.inputElement.value;
901
+ this.infoElement.setAttribute('aria-invalid', 'false');
902
+ this.hideMessage(name);
903
+ }
904
+ }
905
+
906
+ // List of function to validate the rules
907
+ private static checkValidator: Validator = {
908
+ required: (option: ValidArgs): boolean => {
909
+ return !isNaN(Date.parse(option.value)) ? !isNaN(new Date(option.value).getTime()) : option.value.toString().length > 0;
910
+ },
911
+ email: (option: ValidArgs): boolean => {
912
+ return regex.EMAIL.test(option.value);
913
+ },
914
+ url: (option: ValidArgs): boolean => {
915
+ return regex.URL.test(option.value);
916
+ },
917
+ dateIso: (option: ValidArgs): boolean => {
918
+ return regex.DATE_ISO.test(option.value);
919
+ },
920
+ tel: (option: ValidArgs): boolean => {
921
+ return regex.PHONE.test(option.value);
922
+ },
923
+ creditcard: (option: ValidArgs): boolean => {
924
+ return regex.CREDITCARD.test(option.value);
925
+ },
926
+ number: (option: ValidArgs): boolean => {
927
+ return !isNaN(Number(option.value)) && String(option.value).indexOf(' ') === -1;
928
+ },
929
+ digits: (option: ValidArgs): boolean => {
930
+ return regex.DIGITS.test(option.value);
931
+ },
932
+ maxLength: (option: ValidArgs): boolean => {
933
+ return option.value.length <= Number(option.param);
934
+ },
935
+ minLength: (option: ValidArgs): boolean => {
936
+ return option.value.length >= Number(option.param);
937
+ },
938
+ rangeLength: (option: ValidArgs): boolean => {
939
+ const param: number[] = <number[]>option.param;
940
+ return option.value.length >= param[0] && option.value.length <= param[1];
941
+ },
942
+ range: (option: ValidArgs): boolean => {
943
+ const param: number[] = <number[]>option.param;
944
+ return !isNaN(Number(option.value)) && Number(option.value) >= param[0] && Number(option.value) <= param[1];
945
+ },
946
+ date: (option: ValidArgs): boolean => {
947
+ if (!isNullOrUndefined(option.param) && (typeof (option.param) === 'string' && option.param !== '')) {
948
+ const globalize: Internationalization = option.culture && option.culture !== '' ? new Internationalization(option.culture) : new Internationalization;
949
+ const dateOptions: { format: string, type: string, skeleton: string } = { format: option.param.toString(), type: 'dateTime', skeleton: 'yMd' };
950
+ const dateValue: Date = globalize.parseDate(option.value, dateOptions);
951
+ return (!isNullOrUndefined(dateValue) && dateValue instanceof Date && !isNaN(+dateValue));
952
+ }
953
+ else {
954
+ return !isNaN(new Date(option.value).getTime());
955
+ }
956
+ },
957
+ max: (option: ValidArgs): boolean => {
958
+ if (!isNaN(Number(option.value))) {
959
+ // Maximum rule validation for number
960
+ return +option.value <= +option.param;
961
+ }
962
+ // Maximum rule validation for date
963
+ if (option.format && option.format !== '') {
964
+ const globalize: Internationalization = option.culture && option.culture !== '' ? new Internationalization(option.culture) : new Internationalization;
965
+ const dateOptions: { format: string, type: string, skeleton: string } = { format: option.format.toString(), type: 'dateTime', skeleton: 'yMd' };
966
+ const dateValue: Date = globalize.parseDate(option.value, dateOptions);
967
+ const maxValue: Date = (typeof(option.param) === 'string') ? globalize.parseDate(JSON.parse(JSON.stringify(option.param)), dateOptions) : (option.param as Date);
968
+ return new Date(dateValue).getTime() <= new Date(maxValue).getTime();
969
+ } else {
970
+ return new Date(option.value).getTime() <= new Date(JSON.parse(JSON.stringify(option.param))).getTime();
971
+ }
972
+ },
973
+ min: (option: ValidArgs): boolean => {
974
+ if (!isNaN(Number(option.value))) {
975
+ // Minimum rule validation for number
976
+ return +option.value >= +option.param;
977
+ } else if ((option.value).indexOf(',') !== -1) {
978
+ const uNum: string = (option.value).replace(/,/g, '');
979
+ return parseFloat(uNum) >= Number(option.param); // Convert option.param to a number
980
+ } else {
981
+ // Minimum rule validation for date
982
+ if (option.format && option.format !== '') {
983
+ const globalize: Internationalization = option.culture && option.culture !== '' ? new Internationalization(option.culture) : new Internationalization;
984
+ const dateOptions: { format: string, type: string, skeleton: string } = { format: option.format.toString(), type: 'dateTime', skeleton: 'yMd' };
985
+ const dateValue: Date = globalize.parseDate(option.value, dateOptions);
986
+ const minValue: Date = (typeof(option.param) === 'string') ? globalize.parseDate(JSON.parse(JSON.stringify(option.param)), dateOptions) : (option.param as Date);
987
+ return new Date(dateValue).getTime() >= new Date(minValue).getTime();
988
+ } else {
989
+ return new Date(option.value).getTime() >= new Date(JSON.parse(JSON.stringify(option.param))).getTime();
990
+ }
991
+ }
992
+ },
993
+ regex: (option: ValidArgs): boolean => {
994
+ /* eslint-disable-next-line security/detect-non-literal-regexp */
995
+ return new RegExp(<string>option.param).test(option.value);
996
+ },
997
+ equalTo: (option: ValidArgs): boolean => {
998
+ const compareTo: HTMLInputElement = <HTMLInputElement>option.formElement.querySelector('#' + option.param);
999
+ option.param = compareTo.value;
1000
+ return option.param === option.value;
1001
+ }
1002
+ };
1003
+
1004
+ // Return boolean result if the input have checkable or submit types
1005
+ private static isCheckable(input: Element): boolean {
1006
+ const inputType: string = input.getAttribute('type');
1007
+ return inputType && (inputType === 'checkbox' || inputType === 'radio' || inputType === 'submit');
1008
+ }
1009
+ }
1010
+
1011
+ interface Validator {
1012
+ [rule: string]: (value: ValidArgs) => boolean
1013
+ }
1014
+
1015
+ export interface ValidArgs {
1016
+ /**
1017
+ * Returns the value in input element.
1018
+ */
1019
+ value: string
1020
+ /**
1021
+ * Returns the rule mapped for the input.
1022
+ */
1023
+ param?: Object
1024
+ /**
1025
+ * Returns the input element.
1026
+ */
1027
+ element?: HTMLElement
1028
+ /**
1029
+ * Returns the current form element.
1030
+ */
1031
+ formElement?: HTMLFormElement,
1032
+ /**
1033
+ * Returns the date format mapped for the input.
1034
+ */
1035
+ format? : string,
1036
+ /**
1037
+ * Returns the culture for the input.
1038
+ */
1039
+ culture? : string,
1040
+ }
1041
+
1042
+ interface ErrorRule {
1043
+ name: string
1044
+ message: string
1045
+ }
1046
+
1047
+ export interface FormEventArgs {
1048
+ /**
1049
+ * Returns the name of the input element.
1050
+ */
1051
+ inputName: string
1052
+ /**
1053
+ * Returns the error message.
1054
+ */
1055
+ message: string
1056
+ /**
1057
+ * Returns the input element.
1058
+ */
1059
+ element: HTMLInputElement
1060
+ /**
1061
+ * Returns the status input element.
1062
+ */
1063
+ status?: string
1064
+ /**
1065
+ * Returns the error element for corresponding input.
1066
+ */
1067
+ errorElement?: HTMLElement
1068
+ }