@componyx/ui 0.0.0-stage → 1.0.3
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.
- package/AppSkin/AppSkin.css +1268 -0
- package/AppSkin/AppSkin.js +2748 -0
- package/AppSkin/Themes/Default.css +241 -0
- package/Base/Base.js +3407 -0
- package/Base/ComponentIcons.css +66 -0
- package/Base/Font/ComponentIcons.woff +0 -0
- package/Base/Font/UI.woff +0 -0
- package/Base/Library.js +8432 -0
- package/Base/NavigationManager.js +462 -0
- package/Base/ResponsiveTemplates.js +371 -0
- package/Base/Sanitizer.js +288 -0
- package/Base/Themes/Default.css +255 -0
- package/Base/UI.css +618 -0
- package/Base/UIResourcePath.js +6 -0
- package/Bindary/Bindary.js +2011 -0
- package/Bindary/Modules/Connection.js +216 -0
- package/Bindary/Modules/Core.js +854 -0
- package/Bindary/Modules/DataUpdater.js +351 -0
- package/Bindary/Modules/ExpressionEngine.js +347 -0
- package/Bindary/Modules/ProxyManager.js +482 -0
- package/Bindary/Modules/Renderer.js +605 -0
- package/Bindary/Modules/Router.js +291 -0
- package/Bindary/Modules/Scroller.js +89 -0
- package/Bindary/Modules/Template.js +1273 -0
- package/BookmarkSpy/BookmarkSpy.js +430 -0
- package/Box/Box.css +36 -0
- package/Box/Box.js +1685 -0
- package/Button/Button.css +470 -0
- package/Button/Button.js +1398 -0
- package/Button/Themes/Default.css +200 -0
- package/Carousel/Carousel.css +273 -0
- package/Carousel/Carousel.js +607 -0
- package/ColorButton/ColorButton.css +39 -0
- package/ColorButton/ColorButton.js +336 -0
- package/ColorPicker/ColorPicker.css +359 -0
- package/ColorPicker/ColorPicker.js +972 -0
- package/ComboBox/ComboBox.css +357 -0
- package/ComboBox/ComboBox.js +2342 -0
- package/ComboBox/Themes/Default.css +23 -0
- package/DataPager/DataPager.css +72 -0
- package/DataPager/DataPager.js +674 -0
- package/DatePicker/DatePicker.css +227 -0
- package/DatePicker/DatePicker.js +1531 -0
- package/DatePicker/Themes/Default.css +26 -0
- package/Dialog/Dialog.css +106 -0
- package/Dialog/Dialog.js +572 -0
- package/Dialog/Themes/Default.css +10 -0
- package/Editor/Data/Emoji.json +8285 -0
- package/Editor/Data/Special.json +1042 -0
- package/Editor/Editor.css +1562 -0
- package/Editor/Editor.js +4058 -0
- package/Editor/Font/Editor.woff +0 -0
- package/Editor/Modules/ComponentFactory.js +565 -0
- package/Editor/Modules/ContentManager.js +748 -0
- package/Editor/Modules/DialogManager.js +1234 -0
- package/Editor/Modules/EventManager.js +458 -0
- package/Editor/Modules/Format.js +950 -0
- package/Editor/Modules/History.js +191 -0
- package/Editor/Modules/LayoutState.js +379 -0
- package/Editor/Modules/ListManager.js +296 -0
- package/Editor/Modules/MenuManager.js +388 -0
- package/Editor/Modules/NodeManager.js +681 -0
- package/Editor/Modules/ParagraphButtons.js +122 -0
- package/Editor/Modules/Resizer.js +176 -0
- package/Editor/Modules/SelectionRange.js +564 -0
- package/Editor/Modules/SourceViewBuilder.js +212 -0
- package/Editor/Modules/SymbolPicker.js +208 -0
- package/Editor/Modules/Table.js +2327 -0
- package/Editor/Modules/TableDialog.js +757 -0
- package/Editor/Modules/Toolbar.js +375 -0
- package/Editor/Modules/TooltipMenu.js +125 -0
- package/Editor/Modules/Utility.js +131 -0
- package/Editor/Themes/Default.css +204 -0
- package/FileUpload/FileUpload.css +156 -0
- package/FileUpload/FileUpload.js +898 -0
- package/FileUpload/Themes/Default.css +17 -0
- package/Form/Form.css +1934 -0
- package/Form/Form.js +4943 -0
- package/Form/Modules/ActionManager.js +368 -0
- package/Form/Modules/BuildPanelManager.js +319 -0
- package/Form/Modules/ComponentFactory.js +468 -0
- package/Form/Modules/ComponentPanel.js +265 -0
- package/Form/Modules/ConfigPanelManager.js +685 -0
- package/Form/Modules/DataObserver.js +717 -0
- package/Form/Modules/Databinder.js +602 -0
- package/Form/Modules/Draggable.js +756 -0
- package/Form/Modules/OptionsPanel.js +342 -0
- package/Form/Modules/Renderer.js +1889 -0
- package/Form/Modules/RuleEngine.js +585 -0
- package/Form/Modules/RulesPanel.js +1158 -0
- package/Form/Modules/Types.js +1709 -0
- package/Form/Modules/ValidationManager.js +194 -0
- package/Form/Themes/Default.css +216 -0
- package/FormField/FormField.css +468 -0
- package/FormField/FormField.js +576 -0
- package/FormField/Themes/Default.css +99 -0
- package/Grid/Grid.css +544 -0
- package/Grid/Grid.js +3761 -0
- package/Grid/Themes/Default.css +43 -0
- package/LICENSE +126 -0
- package/MaskedTextBox/MaskedTextBox.css +28 -0
- package/MaskedTextBox/MaskedTextBox.js +642 -0
- package/MaskedTextBox/Themes/Default.css +6 -0
- package/Menu/Menu.css +328 -0
- package/Menu/Menu.js +2386 -0
- package/Menu/Themes/Default.css +24 -0
- package/NumericBox/NumericBox.css +120 -0
- package/NumericBox/NumericBox.js +984 -0
- package/PanelBar/PanelBar.css +123 -0
- package/PanelBar/PanelBar.js +505 -0
- package/PanelBar/Themes/Default.css +51 -0
- package/PieChart/PieChart.css +95 -0
- package/PieChart/PieChart.js +820 -0
- package/PieChart/Themes/Default.css +9 -0
- package/README.md +178 -3
- package/Slider/Slider.css +348 -0
- package/Slider/Slider.js +1306 -0
- package/Slider/Themes/Default.css +44 -0
- package/TabStrip/TabStrip.css +52 -0
- package/TabStrip/TabStrip.js +362 -0
- package/TabStrip/Themes/Default.css +35 -0
- package/TimePicker/Themes/Default.css +30 -0
- package/TimePicker/TimePicker.css +253 -0
- package/TimePicker/TimePicker.js +1485 -0
- package/TooltipManager/Themes/Default.css +21 -0
- package/TooltipManager/TooltipManager.css +227 -0
- package/TooltipManager/TooltipManager.js +791 -0
- package/Validator/Validator.css +54 -0
- package/Validator/Validator.js +2508 -0
- package/componyx.html-data.json +619 -0
- package/index.js +27 -0
- package/package.json +43 -4
- package/types/AppSkin.d.ts +842 -0
- package/types/Base.d.ts +1034 -0
- package/types/Bindary.d.ts +1196 -0
- package/types/BookmarkSpy.d.ts +112 -0
- package/types/Box.d.ts +374 -0
- package/types/Button.d.ts +455 -0
- package/types/Carousel.d.ts +136 -0
- package/types/ColorButton.d.ts +120 -0
- package/types/ColorPicker.d.ts +225 -0
- package/types/ComboBox.d.ts +507 -0
- package/types/DataPager.d.ts +152 -0
- package/types/DatePicker.d.ts +354 -0
- package/types/Dialog.d.ts +160 -0
- package/types/Editor.d.ts +1289 -0
- package/types/FileUpload.d.ts +206 -0
- package/types/Form.d.ts +3078 -0
- package/types/FormField.d.ts +204 -0
- package/types/Grid.d.ts +911 -0
- package/types/Library.d.ts +1899 -0
- package/types/MaskedTextBox.d.ts +91 -0
- package/types/Menu.d.ts +577 -0
- package/types/NumericBox.d.ts +310 -0
- package/types/PanelBar.d.ts +223 -0
- package/types/PieChart.d.ts +224 -0
- package/types/ResponsiveTemplates.d.ts +108 -0
- package/types/Slider.d.ts +227 -0
- package/types/TabStrip.d.ts +185 -0
- package/types/TimePicker.d.ts +265 -0
- package/types/TooltipManager.d.ts +178 -0
- package/types/UI.d.ts +29 -0
- package/types/Validator.d.ts +653 -0
- package/types/index.d.ts +59 -0
- package/ui.css +11783 -0
- package/ui.js +79477 -0
- package/ui.min.css +138 -0
- package/ui.min.js +264 -0
- package/ui.min.js.map +1 -0
|
@@ -0,0 +1,2508 @@
|
|
|
1
|
+
/*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
|
|
2
|
+
import '../Base/Base.js';
|
|
3
|
+
import '../Base/Library.js';
|
|
4
|
+
import '../Box/Box.js';
|
|
5
|
+
import '../TooltipManager/TooltipManager.js';
|
|
6
|
+
|
|
7
|
+
(function (window)
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* A list of supported Validator HTML data-attributes.
|
|
11
|
+
* @typedef {Object} HTML_Attributes
|
|
12
|
+
* @memberof componyx.UI.Validator
|
|
13
|
+
* @property {String} ["data-ui-validator-id"] - The identifier of the Validator component on which the rule is added.
|
|
14
|
+
* @property {String} ["data-ui-validator-feedbackid"] - The identifier of the element used by the Validator to display validation result feedback.
|
|
15
|
+
* @property {String} ["data-ui-validator-autovalidate"] - A value ('true' or 'false') indicating if the field is validated when the field value changes.
|
|
16
|
+
* @property {String} ["data-ui-validator-live"] - A value ('true' or 'false') indicating if the field validation is done while typing.
|
|
17
|
+
* @property {String} ["data-ui-validator-getValue"] - A custom method to get the field value.
|
|
18
|
+
* @property {String} ["data-ui-validator-required-msg"] - Denotes an element as required validation field with the attribute value as message to display if validation fails.
|
|
19
|
+
* @property {String} ["data-ui-validator-required-when"] - The method name that returns a boolean value to determines whether the field is required (true) or not (false).
|
|
20
|
+
* @property {String} ["data-ui-validator-datatype-msg"] - Denotes an element as datatype validation field with the attribute value as message to display if validation fails.
|
|
21
|
+
* @property {componyx.UI.Validator.DataTypeOption} ["data-ui-validator-datatype-datatype"] - The data-type to which the field value must comply.
|
|
22
|
+
* @property {String} ["data-ui-validator-length-msg"] - Denotes an element as length validation field with the attribute value as message to display if validation fails.
|
|
23
|
+
* @property {String} ["data-ui-validator-length-min"] - The minimum length to which the field value must comply.
|
|
24
|
+
* @property {String} ["data-ui-validator-length-max"] - The maximum length to which the field value must comply.
|
|
25
|
+
* @property {String} ["data-ui-validator-range-msg"] - Denotes an element as range validation field with the attribute value as message to display if validation fails.
|
|
26
|
+
* @property {String} ["data-ui-validator-range-min"] - The minimum value to which the field value must comply.
|
|
27
|
+
* @property {String} ["data-ui-validator-range-max"] - The maximum value to which the field value must comply.
|
|
28
|
+
* @property {String} ["data-ui-validator-regex-msg"] - Denotes an element as regex validation field with the attribute value as message to display if validation fails.
|
|
29
|
+
* @property {String} ["data-ui-validator-regex-pattern"] - The pattern to which the field value must comply.
|
|
30
|
+
* @property {String} ["data-ui-validator-compare-msg"] - Denotes an element as compare validation field with the attribute value as message to display if validation fails.
|
|
31
|
+
* @property {String} ["data-ui-validator-compare-fields"] - The comparison field(s) with which the field value will be compared.
|
|
32
|
+
* @property {String} ["data-ui-validator-compare-operators"] - The comparison operator(s) for the comparison field validation.
|
|
33
|
+
* @property {String} ["data-ui-validator-custom-msg"] - Denotes an element as custom validation field with the attribute value as message to display if validation fails.
|
|
34
|
+
* @property {String} ["data-ui-validator-custom-onValidation"] - The event handler to invoke when the field is validated.
|
|
35
|
+
* @property {String} ["data-ui-validator-ajax-msg"] - Denotes an element as ajax validation field with the attribute value as message to display if validation fails.
|
|
36
|
+
* @property {String} ["data-ui-validator-ajax-onBeforeValidation"] - The event handler to invoke before the field is validated on the server.
|
|
37
|
+
* @property {String} ["data-ui-validator-ajax-onAfterValidation"] - The event handler to invoke after the field is validated on the server.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Validator class.
|
|
42
|
+
* @class
|
|
43
|
+
* @memberof componyx.UI
|
|
44
|
+
* @augments componyx.UI.base.Component
|
|
45
|
+
* @mixes componyx.UI.base.methods
|
|
46
|
+
* @param {String} id The id of the component.
|
|
47
|
+
* @param {Object|HTMLElement} properties The properties used to initialize the component or the container element.
|
|
48
|
+
* @property {componyx.UI.base.AjaxMethod} ajax.validate - AJAX method used to validate field values on the server.
|
|
49
|
+
* @returns {Object} An instance of the component.
|
|
50
|
+
*/
|
|
51
|
+
componyx.UI.Validator = function Validator(id, properties)
|
|
52
|
+
{
|
|
53
|
+
// define private properties
|
|
54
|
+
let _instance = this,
|
|
55
|
+
_themes = ['themeDefault'],
|
|
56
|
+
_fields = null,
|
|
57
|
+
_ajaxFields = {},
|
|
58
|
+
_formSubmit = false,
|
|
59
|
+
_liveTimerId = null,
|
|
60
|
+
_validatingTimerId = null,
|
|
61
|
+
_fieldValueFromCache = false,
|
|
62
|
+
_tooltipManager,
|
|
63
|
+
_classOption =
|
|
64
|
+
{
|
|
65
|
+
VALIDATOR: 'validator',
|
|
66
|
+
FEEDBACK: 'feedback',
|
|
67
|
+
SUMMARY: 'summary',
|
|
68
|
+
VALIDATING: 'validating',
|
|
69
|
+
VALID: 'valid',
|
|
70
|
+
INVALID: 'invalid',
|
|
71
|
+
ICON: 'icon',
|
|
72
|
+
MULTIPLE: 'multiple',
|
|
73
|
+
TOOLTIPTRIGGER: 'tooltip-trigger'
|
|
74
|
+
},
|
|
75
|
+
_feedbackStatus =
|
|
76
|
+
{
|
|
77
|
+
VALIDATING: 0,
|
|
78
|
+
VALID: 1,
|
|
79
|
+
INVALID: 2
|
|
80
|
+
},
|
|
81
|
+
_typeOption = componyx.UI.Validator.TypeOption,
|
|
82
|
+
_dataTypeOption = componyx.UI.Validator.DataTypeOption,
|
|
83
|
+
_showValidFieldsOption = componyx.UI.Validator.ShowValidFieldsOption,
|
|
84
|
+
_messageDisplayOption = componyx.UI.Validator.MessageDisplayOption,
|
|
85
|
+
_prefix = 'data-ui-validator-',
|
|
86
|
+
_query = 'input,textarea,select,[contenteditable=true]',
|
|
87
|
+
_nativeInputDataTypes = ['email', 'url', 'tel', 'number', 'date', 'time', 'range'],
|
|
88
|
+
_container = null,
|
|
89
|
+
_create = Object.create,
|
|
90
|
+
_attrBase =
|
|
91
|
+
{
|
|
92
|
+
msg: ''
|
|
93
|
+
},
|
|
94
|
+
_prop = (val) => ({ value: val, writable: true, enumerable: true }),
|
|
95
|
+
_Required = function (when) { this.when = _prop(when); },
|
|
96
|
+
_DataType = function (dataType) { this.dataType = _prop(dataType); },
|
|
97
|
+
_Range = function (min, max)
|
|
98
|
+
{
|
|
99
|
+
this.min = _prop(min);
|
|
100
|
+
this.max = _prop(max);
|
|
101
|
+
},
|
|
102
|
+
_RegEx = function (pattern) { this.pattern = _prop(pattern); },
|
|
103
|
+
_Compare = function (fields, operators)
|
|
104
|
+
{
|
|
105
|
+
this.fields = _prop(fields);
|
|
106
|
+
this.operators = _prop(operators);
|
|
107
|
+
},
|
|
108
|
+
_Custom = function (onValidation) { this.onValidation = _prop(onValidation); },
|
|
109
|
+
_Ajax = function (onBeforeValidation, onAfterValidation)
|
|
110
|
+
{
|
|
111
|
+
this.onBeforeValidation = _prop(onBeforeValidation);
|
|
112
|
+
this.onAfterValidation = _prop(onAfterValidation);
|
|
113
|
+
},
|
|
114
|
+
_attr =
|
|
115
|
+
{
|
|
116
|
+
feedbackId: '',
|
|
117
|
+
autoValidate: '',
|
|
118
|
+
live: '',
|
|
119
|
+
getValue: '',
|
|
120
|
+
required: _create(_attrBase, new _Required()),
|
|
121
|
+
dataType: _create(_attrBase, new _DataType()),
|
|
122
|
+
length: _create(_attrBase, new _Range()),
|
|
123
|
+
range: _create(_attrBase, new _Range()),
|
|
124
|
+
regex: _create(_attrBase, new _RegEx()),
|
|
125
|
+
compare: _create(_attrBase, new _Compare()),
|
|
126
|
+
custom: _create(_attrBase, new _Custom()),
|
|
127
|
+
ajax: _create(_attrBase, new _Ajax())
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
_attr.id = _prefix + 'id';
|
|
131
|
+
_attr.feedbackId = _prefix + 'feedbackid';
|
|
132
|
+
_attr.autoValidate = _prefix + 'autovalidate';
|
|
133
|
+
_attr.live = _prefix + 'live';
|
|
134
|
+
_attr.getValue = _prefix + 'getvalue';
|
|
135
|
+
|
|
136
|
+
// create correct attribute keys with 'data-ui-validator-' prefix
|
|
137
|
+
$lib.each(_attr, function (v, k)
|
|
138
|
+
{
|
|
139
|
+
if (v == _attr.id || v == _attr.feedbackId || v == _attr.autoValidate || v == _attr.live || v == _attr.getValue)
|
|
140
|
+
return;
|
|
141
|
+
|
|
142
|
+
$lib.each(_attr[k], function (sv, sk)
|
|
143
|
+
{
|
|
144
|
+
_attr[k][sk] = _prefix + k.toLowerCase() + '-' + sk.toLowerCase();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// define public properties
|
|
149
|
+
/**
|
|
150
|
+
* Gets or sets the css class of a feedback ul element.
|
|
151
|
+
* @type {String}
|
|
152
|
+
*/
|
|
153
|
+
this.cssClassFeedback = '';
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Gets or sets the css class of a summary ul element.
|
|
157
|
+
* @type {String}
|
|
158
|
+
*/
|
|
159
|
+
this.cssClassSummary = '';
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Gets or sets the css class of the feedback message when server-side validation is processing.
|
|
163
|
+
* @type {String}
|
|
164
|
+
*/
|
|
165
|
+
this.cssClassValidating = '';
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Gets or sets the css class of the feedback/summary message when the input field passes validation.
|
|
169
|
+
* @type {String}
|
|
170
|
+
*/
|
|
171
|
+
this.cssClassValid = '';
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Gets or sets the css class of the feedback/summary message when the input field fails validation.
|
|
175
|
+
* @type {String}
|
|
176
|
+
*/
|
|
177
|
+
this.cssClassInvalid = '';
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Gets or sets the css class of the icon in a feedback or summary message.
|
|
181
|
+
* @type {String}
|
|
182
|
+
*/
|
|
183
|
+
this.cssClassIcon = '';
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Gets or sets the css class of the tooltip trigger.
|
|
187
|
+
* @type {String}
|
|
188
|
+
*/
|
|
189
|
+
this.cssClassTooltipTrigger = '';
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Gets or sets the message which is shown when ajax validation is processing.
|
|
193
|
+
* @type {String}
|
|
194
|
+
*/
|
|
195
|
+
this.validatingFieldMessage = '';
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Gets or sets the message which is shown when a field passes validation.
|
|
199
|
+
* @type {String}
|
|
200
|
+
*/
|
|
201
|
+
this.validFieldMessage = '';
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Gets or sets the message which is shown when a field fails validation. Message is not shown when the ErrorMessage is defined on the model property.
|
|
205
|
+
* @type {String}
|
|
206
|
+
*/
|
|
207
|
+
this.invalidFieldMessage = '';
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Gets or sets the message which is shown when ajax validation throws an error.
|
|
211
|
+
* @type {String}
|
|
212
|
+
*/
|
|
213
|
+
this.ajaxErrorMessage = '';
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Gets or sets the delay for the validating field message which is shown when ajax validation is processing.
|
|
217
|
+
* @type {Number}
|
|
218
|
+
*/
|
|
219
|
+
this.validatingFieldMessageDelay = 50;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Gets or sets when valid fields are displayed.
|
|
223
|
+
* @type {componyx.UI.Validator.ShowValidFieldsOption}
|
|
224
|
+
*/
|
|
225
|
+
this.showValidFields = _showValidFieldsOption.NONE;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Gets or sets a value indicating how validation messages are being displayed.
|
|
229
|
+
* @type {componyx.UI.Validator.MessageDisplayOption}
|
|
230
|
+
*/
|
|
231
|
+
this.messageDisplay = _messageDisplayOption.FEEDBACKFIRST;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Gets or sets a value indicating how validation messages are being displayed in the summary container.
|
|
235
|
+
* @type {componyx.UI.Validator.MessageDisplayOption}
|
|
236
|
+
*/
|
|
237
|
+
this.summaryMessageDisplay = _messageDisplayOption.FEEDBACKFIRST;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Gets or sets a value indicating whether valid fields are also displayed in the summary container.
|
|
241
|
+
* @type {Boolean}
|
|
242
|
+
*/
|
|
243
|
+
this.showValidFieldsInSummary = false;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Gets or sets the identifying css class of the field feedback container.
|
|
247
|
+
* @type {String}
|
|
248
|
+
*/
|
|
249
|
+
this.feedbackIdentifyingCssClass = '';
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Gets or sets the container id of the validation message summary.
|
|
253
|
+
* @type {String}
|
|
254
|
+
*/
|
|
255
|
+
this.summaryContainerId = '';
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Gets or sets the form id of the form that holds the input fields when using multiple forms on a single page.
|
|
259
|
+
* @type {String|null}
|
|
260
|
+
*/
|
|
261
|
+
this.formId = null;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Gets or sets the input format for dates. Default: MM/dd/yyyy
|
|
265
|
+
* @type {String}
|
|
266
|
+
*/
|
|
267
|
+
this.dateFormat = 'mm/dd/yyyy';
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Gets or sets the input decimal separator for numbers. Default: '.'
|
|
271
|
+
* @type {String}
|
|
272
|
+
*/
|
|
273
|
+
this.decimalSeparator = '.';
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Gets or sets the input group separator for numbers. Default: ','
|
|
277
|
+
* @type {String}
|
|
278
|
+
*/
|
|
279
|
+
this.groupSeparator = ',';
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Gets or sets the initial validator rules (Array or JSON string).
|
|
283
|
+
* @type {componyx.UI.Validator.Rule[]|String|null}
|
|
284
|
+
*/
|
|
285
|
+
this.rules = null;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Gets or sets the fields which passed validation on postback.
|
|
289
|
+
* @type {String[]}
|
|
290
|
+
*/
|
|
291
|
+
this.validFields = {};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Gets or sets the fields which failed validation on postback.
|
|
295
|
+
* @type {String[]}
|
|
296
|
+
*/
|
|
297
|
+
this.invalidFields = {};
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* List containing the friendly field names.
|
|
301
|
+
* @type {String[]}
|
|
302
|
+
*/
|
|
303
|
+
this.friendlyFieldNames = {};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Gets or sets a value indicating whether auto generated field names should be corrected to static names.
|
|
307
|
+
* @type {Boolean}
|
|
308
|
+
*/
|
|
309
|
+
this.staticASPNETFieldNames = true;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Gets or sets a value indicating whether the validation status css classes are applied to the (input/textarea/select/contenteditable) field (defaults to true).
|
|
313
|
+
* @type {Boolean}
|
|
314
|
+
*/
|
|
315
|
+
this.validationCssOnField = true;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Gets or sets a value indicating whether field validation should be performed while typing (defaults to false).
|
|
319
|
+
* @type {Boolean}
|
|
320
|
+
*/
|
|
321
|
+
this.live = false;
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Gets or sets the delay in milliseconds before the live field validation is performed.
|
|
325
|
+
* @type {Number}
|
|
326
|
+
*/
|
|
327
|
+
this.liveDelay = 0;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Gets or sets a value indicating if fields are being detected when the component initializes.
|
|
331
|
+
* @type {Boolean}
|
|
332
|
+
*/
|
|
333
|
+
this.detectFields = true;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Gets or sets the tabindex of the tooltip trigger element.
|
|
337
|
+
* @type {String}
|
|
338
|
+
*/
|
|
339
|
+
this.tooltipTriggerTabIndex = '';
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Gets or sets the tooltip manager used to display tooltips.
|
|
343
|
+
* @type {String|null}
|
|
344
|
+
*/
|
|
345
|
+
this.tooltipManagerId = null;
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @class
|
|
350
|
+
* @augments componyx.UI.base.Events
|
|
351
|
+
* @memberof componyx.UI.Validator
|
|
352
|
+
* @property {componyx.UI.base.Event} onPreInit - Event which fires before the initialization but after loading resources.
|
|
353
|
+
* @property {componyx.UI.base.Event} onPostInit - Event which fires after the initialization of the validator.
|
|
354
|
+
* @property {componyx.UI.base.Event} onValidField - Event which fires when validation for a field passes. @see {@link componyx.UI.Validator.FieldEventArgs}
|
|
355
|
+
* @property {componyx.UI.base.Event} onInvalidField - Event which fires when validation for a field fails. @see {@link componyx.UI.Validator.FieldEventArgs}
|
|
356
|
+
* @property {componyx.UI.base.Event} onValid - Event which fires when validation passes.
|
|
357
|
+
* @property {componyx.UI.base.Event} onInvalid - Event which fires when validation fails.
|
|
358
|
+
* @property {componyx.UI.base.Event} onFieldFeedback - Event which fires when feedback for a field is set. @see {@link componyx.UI.Validator.FieldEventArgs}
|
|
359
|
+
* @see {@link componyx.UI.base.Events}
|
|
360
|
+
*/
|
|
361
|
+
function ValidatorEvents(events)
|
|
362
|
+
{
|
|
363
|
+
Object.assign(this, events);
|
|
364
|
+
this.onPreInit = $base.static.createEvent('onPreInit');
|
|
365
|
+
this.onPostInit = $base.static.createEvent('onPostInit');
|
|
366
|
+
this.onValidField = $base.static.createEvent('onValidField');
|
|
367
|
+
this.onInvalidField = $base.static.createEvent('onInvalidField');
|
|
368
|
+
this.onValid = $base.static.createEvent('onValid');
|
|
369
|
+
this.onInvalid = $base.static.createEvent('onInvalid');
|
|
370
|
+
this.onFieldFeedback = $base.static.createEvent('onFieldFeedback');
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Validator events
|
|
374
|
+
* @type {componyx.UI.Validator.ValidatorEvents}
|
|
375
|
+
*/
|
|
376
|
+
this.events = new ValidatorEvents(this.events);
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Validator field event arguments.
|
|
380
|
+
* @typedef {Object} FieldEventArgs
|
|
381
|
+
* @memberof componyx.UI.Validator
|
|
382
|
+
* @property {HTMLElement} fieldElement - The validated field element.
|
|
383
|
+
* @property {String} fieldName - The name of the field.
|
|
384
|
+
* @property {String} fieldValue - The value of the field.
|
|
385
|
+
* @property {Object} fieldData - The internal validation data of the field (validation settings and state).
|
|
386
|
+
* @property {String[]} msg - The validation messages.
|
|
387
|
+
* @property {HTMLUListElement} [feedbackElement] - The feedback list element (onFieldFeedback only, when feedback is displayed).
|
|
388
|
+
* @property {HTMLUListElement} [summaryElement] - The summary list element (onFieldFeedback only, when the field is summarised).
|
|
389
|
+
*/
|
|
390
|
+
|
|
391
|
+
// inherit base instance members
|
|
392
|
+
$base.Component.apply(this, [id, properties]);
|
|
393
|
+
|
|
394
|
+
// define ajax method
|
|
395
|
+
this.ajax.addMethod('validate');
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Returns true if the current validation state is valid, otherwise false
|
|
399
|
+
*/
|
|
400
|
+
this.isValid = function ()
|
|
401
|
+
{
|
|
402
|
+
return isValid();
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Validates the required field using the specified settings.
|
|
407
|
+
* @param {Object} settings The validation rule settings.
|
|
408
|
+
* @param {HTMLElement[]} field The field or fields in case of a Radio Button.
|
|
409
|
+
* @returns {Boolean} A value indicating if the field is valid (true) or invalid (false).
|
|
410
|
+
*/
|
|
411
|
+
this.validateRequired = function (settings, field)
|
|
412
|
+
{
|
|
413
|
+
return validateRequired(settings, field);
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Validates the field by value comparison using the specified settings.
|
|
418
|
+
* @param {Object} settings Validation rule settings (includes fields and operators).
|
|
419
|
+
* @param {String} value The field value.
|
|
420
|
+
* @param {componyx.UI.Validator.DataTypeOption|null} [dataType] Field value data type (default: string).
|
|
421
|
+
* @returns {Boolean} A value indicating if the field is valid (true) or invalid (false).
|
|
422
|
+
*/
|
|
423
|
+
this.validateCompare = function (settings, value, dataType)
|
|
424
|
+
{
|
|
425
|
+
return validateCompare(settings, value, dataType);
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Validates the field value range using the specified settings.
|
|
430
|
+
* @param {Object} settings The validation rule settings.
|
|
431
|
+
* @param {String} value The field value.
|
|
432
|
+
* @param {DataTypeOption} value The data type of the value.
|
|
433
|
+
* @returns {Boolean} A value indicating if the field is valid (true) or invalid (false).
|
|
434
|
+
*/
|
|
435
|
+
this.validateRange = function (settings, value, dataType)
|
|
436
|
+
{
|
|
437
|
+
return validateRange(settings, value, dataType);
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Validates the field value length using the specified settings.
|
|
442
|
+
* @param {Object} settings The validation rule settings.
|
|
443
|
+
* @param {String} value The field value.
|
|
444
|
+
* @returns {Boolean} A value indicating if the field is valid (true) or invalid (false).
|
|
445
|
+
*/
|
|
446
|
+
this.validateLength = function (settings, value)
|
|
447
|
+
{
|
|
448
|
+
return validateLength(settings, value);
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Validates the field value data-type using the specified settings.
|
|
453
|
+
* @param {Object} settings The validation rule settings.
|
|
454
|
+
* @param {String} value The field value.
|
|
455
|
+
* @returns {Boolean} A value indicating if the field is valid (true) or invalid (false).
|
|
456
|
+
*/
|
|
457
|
+
this.validateType = function (settings, value)
|
|
458
|
+
{
|
|
459
|
+
return validateType(settings, value);
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Validates the field value by the configured regex pattern using the specified settings.
|
|
464
|
+
* @param {Object} settings The validation rule settings.
|
|
465
|
+
* @param {String} value The field value.
|
|
466
|
+
* @returns {Boolean} A value indicating if the field is valid (true) or invalid (false).
|
|
467
|
+
*/
|
|
468
|
+
this.validateRegEx = function (settings, value)
|
|
469
|
+
{
|
|
470
|
+
return validateRegEx(settings, value);
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Gets all the validation fields with there defined rules.
|
|
475
|
+
* @returns {Object} An object with field-name as key and the validation rules as value.
|
|
476
|
+
*/
|
|
477
|
+
this.getFields = function ()
|
|
478
|
+
{
|
|
479
|
+
return _fields;
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Defines the template for a validating field in the feedback container. The template supports the below listed interpolations. Default value: {icon}{message}
|
|
484
|
+
* - {icon} This value will be replaced validation state icon.
|
|
485
|
+
* - {fieldName} This value will be replaced with the name of the validation field.
|
|
486
|
+
* - {message} This value will be replaced with the validation message.
|
|
487
|
+
* @param {HTMLElement|HTMLElement[]|DocumentFragment|String} content The content of the template
|
|
488
|
+
*/
|
|
489
|
+
this.setValidatingFieldFeedbackTemplate = function (content)
|
|
490
|
+
{
|
|
491
|
+
_instance.addTemplate('ValidatingFieldFeedback', content);
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Defines the template for a valid field in the feedback container. The template supports the below listed interpolations. Default value: {icon}{message}
|
|
496
|
+
* - {icon} This value will be replaced validation state icon.
|
|
497
|
+
* - {fieldName} This value will be replaced with the name of the validation field.
|
|
498
|
+
* - {message} This value will be replaced with the validation message.
|
|
499
|
+
* @param {HTMLElement|HTMLElement[]|DocumentFragment|String} content The content of the template
|
|
500
|
+
*/
|
|
501
|
+
this.setValidFieldFeedbackTemplate = function (content)
|
|
502
|
+
{
|
|
503
|
+
_instance.addTemplate('ValidFieldFeedback', content);
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Defines the template for an invalid field in the feedback container. The template supports the below listed interpolations. Default value: {icon}{tooltipTrigger}{multiple}[ 1-{messageCount} ]{/multiple}{/tooltipTrigger} {message}
|
|
508
|
+
* - {icon} This value will be replaced validation state icon.
|
|
509
|
+
* - {fieldName} This value will be replaced with the name of the validation field.
|
|
510
|
+
* - {message} This value will be replaced with the validation message.
|
|
511
|
+
* - {tooltipTrigger} This value will be replaced with the opening trigger element tag.
|
|
512
|
+
* - {/tooltipTrigger} This value will be replaced with the closing trigger element tag.
|
|
513
|
+
* - {multiple} This value will be replaced with the opening multiple element tag.
|
|
514
|
+
* - {/multiple} This value will be replaced with the closing multiple element tag.
|
|
515
|
+
* - {messageCount} This value will be replaced with the validation message count.
|
|
516
|
+
* @param {HTMLElement|HTMLElement[]|DocumentFragment|String} content The content of the template
|
|
517
|
+
*/
|
|
518
|
+
this.setInvalidFieldFeedbackTemplate = function (content)
|
|
519
|
+
{
|
|
520
|
+
_instance.addTemplate('InvalidFieldFeedback', content);
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Defines the template for a valid field in the summary container. The template supports the below listed interpolations. Default value: {icon}{fieldName} {message
|
|
525
|
+
* - {icon} This value will be replaced validation state icon.
|
|
526
|
+
* - {fieldName} This value will be replaced with the name of the validation field.
|
|
527
|
+
* - {message} This value will be replaced with the validation message.
|
|
528
|
+
* @param {HTMLElement|HTMLElement[]|DocumentFragment|String} content The content of the template
|
|
529
|
+
*/
|
|
530
|
+
this.setValidFieldSummaryTemplate = function (content)
|
|
531
|
+
{
|
|
532
|
+
_instance.addTemplate('ValidFieldSummary', content);
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Defines the template for an invalid field in the summary container. The template supports the below listed interpolations. Default value: {icon}{fieldName}: {tooltipTrigger}{multiple}[ 1-{messageCount} ]{/multiple}{/tooltipTrigger} {message}
|
|
537
|
+
* - {icon} This value will be replaced validation state icon.
|
|
538
|
+
* - {fieldName} This value will be replaced with the name of the validation field.
|
|
539
|
+
* - {message} This value will be replaced with the validation message.
|
|
540
|
+
* - {tooltipTrigger} This value will be replaced with the opening trigger element tag.
|
|
541
|
+
* - {/tooltipTrigger} This value will be replaced with the closing trigger element tag.
|
|
542
|
+
* - {multiple} This value will be replaced with the opening multiple element tag.
|
|
543
|
+
* - {/multiple} This value will be replaced with the closing multiple element tag.
|
|
544
|
+
* - {messageCount} This value will be replaced with the validation message count.
|
|
545
|
+
* @param {HTMLElement|HTMLElement[]|DocumentFragment|String} content The content of the template
|
|
546
|
+
*/
|
|
547
|
+
this.setInvalidFieldSummaryTemplate = function (content)
|
|
548
|
+
{
|
|
549
|
+
_instance.addTemplate('InvalidFieldSummary', content);
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Defines the template for an invalid field in the popup. The template supports the below listed interpolations. Default value: {icon}{fieldName}: {tooltipTrigger}{multiple}[ 1-{messageCount} ]{/multiple}{/tooltipTrigger} {message}
|
|
554
|
+
* - {message} This value will be replaced with the validation message.
|
|
555
|
+
* @param {HTMLElement|HTMLElement[]|DocumentFragment|String} content The content of the template
|
|
556
|
+
*/
|
|
557
|
+
this.setInvalidFieldPopupTemplate = function (content)
|
|
558
|
+
{
|
|
559
|
+
_instance.addTemplate('InvalidFieldPopup', content);
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// the validator does not render html output, so nothing to hide
|
|
563
|
+
this.hide = null;
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Initializes the Validator component if this has not yet been done.
|
|
567
|
+
*/
|
|
568
|
+
this.show = function ()
|
|
569
|
+
{
|
|
570
|
+
if (_instance.renderState == $base.static.RenderState.NONE)
|
|
571
|
+
this.render();
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Initializes the Validator component.
|
|
576
|
+
*/
|
|
577
|
+
this.init = function ()
|
|
578
|
+
{
|
|
579
|
+
if (_instance.renderState == $base.static.RenderState.RENDERING)
|
|
580
|
+
return;
|
|
581
|
+
|
|
582
|
+
const element = _instance.element;
|
|
583
|
+
let script = ['Box', 'TooltipManager'];
|
|
584
|
+
|
|
585
|
+
_instance.destroy(true, false); // keep events, keep the placeholder in the DOM
|
|
586
|
+
_instance.element = element;
|
|
587
|
+
_instance.hasTheme = false;
|
|
588
|
+
$UI.store[_instance.id] = _instance;
|
|
589
|
+
|
|
590
|
+
if (element) // a Validator created by another component does not have an element
|
|
591
|
+
{
|
|
592
|
+
$UI.elementStore.set(element, _instance); // destroy() removed it
|
|
593
|
+
$base.methods.setupMutationObserver.call(_instance);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
_instance.renderState = $base.static.RenderState.RENDERING;
|
|
597
|
+
|
|
598
|
+
// initialize css
|
|
599
|
+
_instance.registerResources(function ()
|
|
600
|
+
{
|
|
601
|
+
return ['Validator', script];
|
|
602
|
+
},
|
|
603
|
+
function ()
|
|
604
|
+
{
|
|
605
|
+
// create default templates
|
|
606
|
+
if (!_instance.hasTemplate('ValidatingFieldFeedback'))
|
|
607
|
+
_instance.addTemplate('ValidatingFieldFeedback', '{icon}{message}', true);
|
|
608
|
+
|
|
609
|
+
if (!_instance.hasTemplate('ValidFieldFeedback'))
|
|
610
|
+
_instance.addTemplate('ValidFieldFeedback', '{icon}{message}', true);
|
|
611
|
+
|
|
612
|
+
if (!_instance.hasTemplate('InvalidFieldFeedback'))
|
|
613
|
+
_instance.addTemplate('InvalidFieldFeedback', '{icon}{tooltipTrigger}{multiple}[ 1-{messageCount} ]{/multiple}{/tooltipTrigger} {message}', true);
|
|
614
|
+
|
|
615
|
+
if (!_instance.hasTemplate('ValidFieldSummary'))
|
|
616
|
+
_instance.addTemplate('ValidFieldSummary', '{icon}{fieldName} {message}', true);
|
|
617
|
+
|
|
618
|
+
if (!_instance.hasTemplate('InvalidFieldSummary'))
|
|
619
|
+
_instance.addTemplate('InvalidFieldSummary', '{icon}{fieldName}: {tooltipTrigger}{multiple}[ 1-{messageCount} ]{/multiple}{/tooltipTrigger} {message}', true);
|
|
620
|
+
|
|
621
|
+
if (!_instance.hasTemplate('InvalidFieldPopup'))
|
|
622
|
+
_instance.addTemplate('InvalidFieldPopup', '{message}', true);
|
|
623
|
+
|
|
624
|
+
_instance.renderState = $base.static.RenderState.RENDERED; // we need to set this state otherwise main UI postRender event might be blocked
|
|
625
|
+
createTooltipManager();
|
|
626
|
+
_instance.events.onPreInit.fire(_instance);
|
|
627
|
+
|
|
628
|
+
if ($UI.busy)
|
|
629
|
+
{
|
|
630
|
+
if (!$UI.onPostRender.has(postInit))
|
|
631
|
+
$UI.onPostRender.once(postInit);
|
|
632
|
+
}
|
|
633
|
+
else
|
|
634
|
+
postInit();
|
|
635
|
+
});
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Initializes the Validator component. Both init and render are the same for this component.
|
|
640
|
+
* @function
|
|
641
|
+
*/
|
|
642
|
+
this.render = this.init;
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Detects validation rules specified through data-attributes on input elements.
|
|
646
|
+
* @param {HTMLElement} [container] A container element to restrict detection to fields within the container.
|
|
647
|
+
* @param {boolean} [ignoreHidden] A value indicating whether hidden fields should be excluded from detection.
|
|
648
|
+
*/
|
|
649
|
+
this.detect = function (container, ignoreHidden = false)
|
|
650
|
+
{
|
|
651
|
+
let settings, dataType, detected = {},
|
|
652
|
+
getValidatorAttr = (el, attrFullName) =>
|
|
653
|
+
{
|
|
654
|
+
let val = el.getAttribute(attrFullName);
|
|
655
|
+
if (val != null)
|
|
656
|
+
return val;
|
|
657
|
+
|
|
658
|
+
if (attrFullName.startsWith('data-ui-validator-')) // check for shortened attribute without 'data-' prefix
|
|
659
|
+
{
|
|
660
|
+
const suffix = attrFullName.slice('data-ui-validator-'.length);
|
|
661
|
+
|
|
662
|
+
val = el.getAttribute('ui-val-' + suffix);
|
|
663
|
+
if (val != null) return val;
|
|
664
|
+
|
|
665
|
+
val = el.getAttribute('ui-validator-' + suffix);
|
|
666
|
+
if (val != null) return val;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return null;
|
|
670
|
+
},
|
|
671
|
+
hasValidatorAttr = (el, attrFullName) =>
|
|
672
|
+
{
|
|
673
|
+
return getValidatorAttr(el, attrFullName) != null;
|
|
674
|
+
},
|
|
675
|
+
setup = function (el, type, props)
|
|
676
|
+
{
|
|
677
|
+
var settings = _create(_attrBase, props);
|
|
678
|
+
settings.feedBackId = getValidatorAttr(el, _attr.feedbackId);
|
|
679
|
+
settings.autoValidate = hasValidatorAttr(el, _attr.autoValidate) ? getValidatorAttr(el, _attr.autoValidate) != 'false' : null;
|
|
680
|
+
settings.live = hasValidatorAttr(el, _attr.live) ? getValidatorAttr(el, _attr.live) != 'false' : null;
|
|
681
|
+
settings.getValue = getValidatorAttr(el, _attr.getValue);
|
|
682
|
+
settings.msg = getValidatorAttr(el, type.msg);
|
|
683
|
+
settings.native = (type === _attr.required && el.hasAttribute('required')) ||
|
|
684
|
+
(type === _attr.dataType && _nativeInputDataTypes.includes(el.type)) ||
|
|
685
|
+
(type === _attr.length && (el.hasAttribute('minlength') || el.hasAttribute('maxlength'))) ||
|
|
686
|
+
(type === _attr.range && (el.hasAttribute('min') || el.hasAttribute('max'))) ||
|
|
687
|
+
(type === _attr.regex && el.hasAttribute('pattern'));
|
|
688
|
+
settings.checkValidity = (settings.native) ? el.checkValidity.bind(el) : null;
|
|
689
|
+
|
|
690
|
+
return settings;
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
_container = container || getForm() || document;
|
|
694
|
+
let list = _container.querySelectorAll(_query);
|
|
695
|
+
|
|
696
|
+
if (ignoreHidden)
|
|
697
|
+
list = Array.from(list).filter(el => isVisible(el));
|
|
698
|
+
|
|
699
|
+
$lib.each(list, function (el)
|
|
700
|
+
{
|
|
701
|
+
let name = getFieldName(el);
|
|
702
|
+
|
|
703
|
+
if ($lib.isEmpty(name) || detected[name] || (hasValidatorAttr(el, _attr.id) && getValidatorAttr(el, _attr.id) != _instance.id))
|
|
704
|
+
return;
|
|
705
|
+
|
|
706
|
+
detected[name] = true;
|
|
707
|
+
|
|
708
|
+
if (hasValidatorAttr(el, _attr.required.msg))
|
|
709
|
+
{
|
|
710
|
+
settings = setup(el, _attr.required, new _Required(getValidatorAttr(el, _attr.required.when)));
|
|
711
|
+
_instance.addRule(name, settings, _typeOption.REQUIRED);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
if (hasValidatorAttr(el, _attr.dataType.msg))
|
|
715
|
+
{
|
|
716
|
+
dataType = getValidatorAttr(el, _attr.dataType.dataType);
|
|
717
|
+
settings = setup(el, _attr.dataType, new _DataType(!$lib.isEmpty(dataType) ? _dataTypeOption[dataType.toUpperCase()] : 0));
|
|
718
|
+
_instance.addRule(name, settings, _typeOption.DATATYPE);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (hasValidatorAttr(el, _attr.length.msg))
|
|
722
|
+
{
|
|
723
|
+
settings = setup(el, _attr.length, new _Range(getValidatorAttr(el, _attr.length.min), getValidatorAttr(el, _attr.length.max)));
|
|
724
|
+
_instance.addRule(name, settings, _typeOption.LENGTH);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (hasValidatorAttr(el, _attr.range.msg))
|
|
728
|
+
{
|
|
729
|
+
settings = setup(el, _attr.range, new _Range(getValidatorAttr(el, _attr.range.min), getValidatorAttr(el, _attr.range.max)));
|
|
730
|
+
_instance.addRule(name, settings, _typeOption.RANGE);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (hasValidatorAttr(el, _attr.regex.msg))
|
|
734
|
+
{
|
|
735
|
+
settings = setup(el, _attr.regex, new _RegEx(getValidatorAttr(el, _attr.regex.pattern)));
|
|
736
|
+
_instance.addRule(name, settings, _typeOption.REGEX);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (hasValidatorAttr(el, _attr.compare.msg))
|
|
740
|
+
{
|
|
741
|
+
const fields = (getValidatorAttr(el, _attr.compare.fields) || '').split(',').map(f => f.trim()),
|
|
742
|
+
operators = (getValidatorAttr(el, _attr.compare.operators) || '').split(',').map(o => o.trim());
|
|
743
|
+
|
|
744
|
+
settings = setup(el, _attr.compare, new _Compare(fields, operators));
|
|
745
|
+
_instance.addRule(name, settings, _typeOption.COMPARE);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (hasValidatorAttr(el, _attr.custom.onValidation))
|
|
749
|
+
{
|
|
750
|
+
settings = setup(el, _attr.custom, new _Custom(getMethod(getValidatorAttr(el, _attr.custom.onValidation))));
|
|
751
|
+
_instance.addRule(name, settings, _typeOption.CUSTOM);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (hasValidatorAttr(el, _attr.ajax.msg))
|
|
755
|
+
{
|
|
756
|
+
settings = setup(el, _attr.ajax, new _Ajax(
|
|
757
|
+
getMethod(getValidatorAttr(el, _attr.ajax.onBeforeValidation)),
|
|
758
|
+
getMethod(getValidatorAttr(el, _attr.ajax.onAfterValidation))
|
|
759
|
+
));
|
|
760
|
+
_instance.addRule(name, settings, _typeOption.AJAX);
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
_container = null;
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Adds a validation rule of the specified type for the given field name(s).
|
|
769
|
+
* @param {String|String[]} fieldName The name of the form field(s).
|
|
770
|
+
* @param {Object} settings The validation rule settings.
|
|
771
|
+
* @param {String} settings.feedbackId The identifier of the element used by the Validator to display validation result feedback.
|
|
772
|
+
* @param {Boolean} settings.autoValidate A value indicating if the field is validated when the field value changes.
|
|
773
|
+
* @param {Boolean} settings.live A value indicating if the field validation is done while typing.
|
|
774
|
+
* @param {Function} settings.getValue A custom method to return the value for this field. Passes in the field element and expects the field value as result.
|
|
775
|
+
* @param {String} settings.msg The message to display if validation fails.
|
|
776
|
+
* @param {String} [settings.when] The name of the method that returns a boolean value to determines whether the field is required (true) or not (false). (REQUIRED type validation).
|
|
777
|
+
* @param {Number|String} [settings.min] The minimum value. (LENGTH and RANGE type validation).
|
|
778
|
+
* @param {Number|String} [settings.max] The maximum value. (LENGTH and RANGE type validation).
|
|
779
|
+
* @param {RegExp} settings.pattern The Regular expression pattern. (REGEX type validation).
|
|
780
|
+
* @param {HTMLElement[]|String[]} settings.fields The HTML element(s), element name(s) or values to compare against.
|
|
781
|
+
* @param {String[]} settings.operators The operator(s) to use for comparison: < 'LessThan', <= 'LessThanOrEqualTo', > 'GreaterThan', >= 'GreaterThanOrEqualTo', or == 'EqualTo' (default). Can be a single entry (applied to all fields) or an array aligned with `fields`.
|
|
782
|
+
* @param {componyx.UI.Validator.DataTypeOption} settings.dataType The data-type to which the field value must comply. (DATATYPE type validation).
|
|
783
|
+
* @param {Boolean} settings.alwaysCheck A value indicating if the validation rule should always be checked even if the field validation has already failed (only applies to type CUSTOM).
|
|
784
|
+
* @param {OnValidation} settings.onValidation A function(sender, args) invoked when the field is validated. (CUSTOM type validation).
|
|
785
|
+
* @param {OnBeforeValidation} settings.onBeforeValidation A function(sender, args) invoked before the field is validated. (AJAX type validation).
|
|
786
|
+
* @param {OnAfterValidation} settings.onAfterValidation A function function(sender, args) invoked after the field is validated. (AJAX type validation).
|
|
787
|
+
* @param {TypeOption} [type] The type of the validation. May be omitted to apply a global field setting only (feedbackId, autoValidate or live).
|
|
788
|
+
* - 0 REQUIRED
|
|
789
|
+
* - 1 DATATYPE
|
|
790
|
+
* - 2 LENGTH
|
|
791
|
+
* - 3 RANGE
|
|
792
|
+
* - 4 REGEX
|
|
793
|
+
* - 5 COMPARE
|
|
794
|
+
* - 6 CUSTOM
|
|
795
|
+
* - 7 AJAX
|
|
796
|
+
*/
|
|
797
|
+
this.addRule = function (fieldName, settings, type)
|
|
798
|
+
{
|
|
799
|
+
var form = getForm();
|
|
800
|
+
|
|
801
|
+
if (!fieldName)
|
|
802
|
+
return;
|
|
803
|
+
|
|
804
|
+
if (!$lib.isArray(fieldName))
|
|
805
|
+
fieldName = [fieldName];
|
|
806
|
+
|
|
807
|
+
if (!settings)
|
|
808
|
+
settings = {};
|
|
809
|
+
|
|
810
|
+
if (!_fields)
|
|
811
|
+
{
|
|
812
|
+
_fields = {};
|
|
813
|
+
|
|
814
|
+
if (form)
|
|
815
|
+
$lib.on(form, 'submit', validateForm);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (settings.fields && !$lib.isArray(settings.fields))
|
|
819
|
+
settings.fields = [settings.fields];
|
|
820
|
+
|
|
821
|
+
$lib.each(fieldName, function (name)
|
|
822
|
+
{
|
|
823
|
+
let fieldEl = getFieldByName(name);
|
|
824
|
+
|
|
825
|
+
if (!_fields[name])
|
|
826
|
+
{
|
|
827
|
+
_fields[name] =
|
|
828
|
+
{
|
|
829
|
+
feedbackId: settings.feedbackId,
|
|
830
|
+
autoValidate: settings.autoValidate,
|
|
831
|
+
live: settings.live,
|
|
832
|
+
getValue: settings.getValue,
|
|
833
|
+
isValid: false,
|
|
834
|
+
lastValue: null,
|
|
835
|
+
lastMsg: null,
|
|
836
|
+
element: fieldEl || null
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
else
|
|
840
|
+
{
|
|
841
|
+
_fields[name].isValid = false;
|
|
842
|
+
_fields[name].lastValue = null;
|
|
843
|
+
_fields[name].lastMsg = null;
|
|
844
|
+
|
|
845
|
+
if (!$lib.isEmpty(settings.feedbackId))
|
|
846
|
+
_fields[name].feedbackId = settings.feedbackId;
|
|
847
|
+
|
|
848
|
+
if (!$lib.isEmpty(settings.autoValidate))
|
|
849
|
+
_fields[name].autoValidate = settings.autoValidate;
|
|
850
|
+
|
|
851
|
+
if (!$lib.isEmpty(settings.live))
|
|
852
|
+
_fields[name].live = settings.live;
|
|
853
|
+
|
|
854
|
+
if (!$lib.isEmpty(settings.getValue))
|
|
855
|
+
_fields[name].getValue = settings.getValue;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (fieldEl && _fields[name].autoValidate != false)
|
|
859
|
+
{
|
|
860
|
+
let fieldEls = [fieldEl];
|
|
861
|
+
|
|
862
|
+
if (fieldEl.type == 'radio')
|
|
863
|
+
fieldEls = getRadioFields(fieldEl);
|
|
864
|
+
|
|
865
|
+
if (type === _typeOption.COMPARE && settings.fields) // make sure original field gets validated
|
|
866
|
+
{
|
|
867
|
+
settings.fields.forEach(cf =>
|
|
868
|
+
{
|
|
869
|
+
let cfEl = (cf instanceof HTMLElement) ? cf : getFieldByName(cf);
|
|
870
|
+
|
|
871
|
+
if (cfEl && fieldEls.indexOf(cfEl) === -1)
|
|
872
|
+
{
|
|
873
|
+
if (cfEl.type == 'radio')
|
|
874
|
+
fieldEls = fieldEls.concat(getRadioFields(cfEl));
|
|
875
|
+
else
|
|
876
|
+
fieldEls.push(cfEl);
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
$lib.each(fieldEls, function (fld)
|
|
882
|
+
{
|
|
883
|
+
// add unique event handler for field, binding will only be added once per field
|
|
884
|
+
let eventType = (fld.isContentEditable) ? 'blur' : 'change';
|
|
885
|
+
|
|
886
|
+
$lib.on(fld, eventType, validateFieldOnChange, [fld, false], null, true);
|
|
887
|
+
$lib.on(fld, 'focus', hideTooltip, name, null, true);
|
|
888
|
+
|
|
889
|
+
if (fld.nodeName.toLowerCase() != 'select' && (_fields[name].live == true || (_instance.live && _fields[name].live != false)))
|
|
890
|
+
$lib.on(fld, 'input', validateFieldOnInput, [fld, false], null, true);
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
delete settings.feedbackId;
|
|
895
|
+
delete settings.autoValidate;
|
|
896
|
+
delete settings.live;
|
|
897
|
+
delete settings.getValue;
|
|
898
|
+
|
|
899
|
+
if (!$lib.isEmpty(type))
|
|
900
|
+
{
|
|
901
|
+
if (!_fields[name][type])
|
|
902
|
+
_fields[name].isValid = false; // new rule
|
|
903
|
+
|
|
904
|
+
_fields[name][type] = settings;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
var reqSettings = _fields[name][_typeOption.REQUIRED];
|
|
908
|
+
|
|
909
|
+
if ((!_fields[name][_typeOption.REQUIRED] || (reqSettings.when && !isRequired(reqSettings.when, fieldEl))) && $lib.isEmpty(getFieldValue(fieldEl)))
|
|
910
|
+
_fields[name].isValid = true; // not required and no field value
|
|
911
|
+
});
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Resets the field's validation state.
|
|
916
|
+
* @param {String|String[]} fieldName The name of the form field (or array of field names).
|
|
917
|
+
*/
|
|
918
|
+
this.resetState = function (fieldName)
|
|
919
|
+
{
|
|
920
|
+
$lib.each(fieldName, function (name)
|
|
921
|
+
{
|
|
922
|
+
resetState(_fields[name], name);
|
|
923
|
+
});
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Resets the validation state for all fields.
|
|
928
|
+
* @param {HTMLElement} [container] A container element to restrict the state reset to fields within the container.
|
|
929
|
+
*/
|
|
930
|
+
this.resetStates = function (container)
|
|
931
|
+
{
|
|
932
|
+
$lib.each(_fields, function (fld, name)
|
|
933
|
+
{
|
|
934
|
+
if (!container || (container && fld.element && container.contains(fld.element)))
|
|
935
|
+
resetState(fld, name);
|
|
936
|
+
});
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Returns true if there are validation rules for this field, otherwise false.
|
|
941
|
+
* @param {String} fieldName The name of the form field.
|
|
942
|
+
*/
|
|
943
|
+
this.hasRules = function (fieldName)
|
|
944
|
+
{
|
|
945
|
+
return (_fields != null && _fields[fieldName] != undefined);
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Returns true if the validation rule is set for this field, otherwise false.
|
|
950
|
+
* @param {String} fieldName The name of the form field.
|
|
951
|
+
*/
|
|
952
|
+
this.hasRule = function (fieldName, type)
|
|
953
|
+
{
|
|
954
|
+
return (_fields != null && _fields[fieldName] != undefined && _fields[fieldName][type] != undefined);
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Returns the validation rules for the specified field.
|
|
959
|
+
* @param {String} fieldName The name of the form field.
|
|
960
|
+
*/
|
|
961
|
+
this.getRules = function (fieldName)
|
|
962
|
+
{
|
|
963
|
+
if (_fields != null && _fields[fieldName] != undefined)
|
|
964
|
+
return _fields[fieldName];
|
|
965
|
+
else
|
|
966
|
+
return null
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* Removes the validation rule of the specified type for the given field name(s).
|
|
971
|
+
* @param {String|String[]} fieldName The name of the form field (or array of field names).
|
|
972
|
+
* @param {TypeOption} type The type of the validation
|
|
973
|
+
*/
|
|
974
|
+
this.removeRule = function (fieldName, type)
|
|
975
|
+
{
|
|
976
|
+
if (!_fields)
|
|
977
|
+
return;
|
|
978
|
+
|
|
979
|
+
if (!fieldName.constructor || fieldName.constructor.toString().indexOf('Array') == -1)
|
|
980
|
+
fieldName = [fieldName];
|
|
981
|
+
|
|
982
|
+
$lib.each(fieldName, function (name)
|
|
983
|
+
{
|
|
984
|
+
if (_fields[name] && _fields[name][type])
|
|
985
|
+
{
|
|
986
|
+
clearFieldFeedback(name);
|
|
987
|
+
delete _fields[name][type];
|
|
988
|
+
_fields[name].lastValue = null;
|
|
989
|
+
|
|
990
|
+
if (!hasRule(name))
|
|
991
|
+
{
|
|
992
|
+
removeBinding(name);
|
|
993
|
+
delete _fields[name];
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Removes the validation rules for the given field name(s).
|
|
1001
|
+
* @param {String|String[]} fieldName The name of the form field (or array of field names).
|
|
1002
|
+
*/
|
|
1003
|
+
this.removeRules = function (fieldName)
|
|
1004
|
+
{
|
|
1005
|
+
if (!_fields)
|
|
1006
|
+
return;
|
|
1007
|
+
|
|
1008
|
+
fieldName = Array.isArray(fieldName) ? fieldName : [fieldName];
|
|
1009
|
+
|
|
1010
|
+
$lib.each(fieldName, function (name)
|
|
1011
|
+
{
|
|
1012
|
+
if (_fields[name])
|
|
1013
|
+
{
|
|
1014
|
+
clearFieldFeedback(name);
|
|
1015
|
+
delete _fields[name];
|
|
1016
|
+
removeBinding(name);
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Removes the validation rules for all fields.
|
|
1023
|
+
* @param {HTMLElement} [container] A container element to restrict clearance to fields within the container.
|
|
1024
|
+
* @param {Boolean} [keepForRemovedField] A value indicating to keep (instead of clearing) validation rules for field elements removed from the document.
|
|
1025
|
+
*/
|
|
1026
|
+
this.clear = function (container, keepForRemovedField)
|
|
1027
|
+
{
|
|
1028
|
+
var clearFields = [];
|
|
1029
|
+
|
|
1030
|
+
_instance.clearFeedback(container, keepForRemovedField);
|
|
1031
|
+
|
|
1032
|
+
$lib.each(_fields, function (fld, name)
|
|
1033
|
+
{
|
|
1034
|
+
if (container)
|
|
1035
|
+
{
|
|
1036
|
+
if (fld.element && (document.contains(fld.element) || keepForRemovedField) && !container.contains(fld.element))
|
|
1037
|
+
return;
|
|
1038
|
+
else
|
|
1039
|
+
clearFields.push(name);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
removeBinding(name);
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
if (container)
|
|
1046
|
+
{
|
|
1047
|
+
$lib.each(clearFields, function (name)
|
|
1048
|
+
{
|
|
1049
|
+
delete _fields[name];
|
|
1050
|
+
|
|
1051
|
+
if (_ajaxFields[name])
|
|
1052
|
+
delete _ajaxFields[name];
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
else
|
|
1056
|
+
{
|
|
1057
|
+
_fields = null;
|
|
1058
|
+
_ajaxFields = {};
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* Removes the feedback messages for all fields.
|
|
1064
|
+
* @param {HTMLElement} [container] A container element to restrict clearance to fields within the container.
|
|
1065
|
+
* @param {Boolean} [keepForRemovedField] A value indicating to keep (instead of clearing) validation rules for field elements removed from the document.
|
|
1066
|
+
*/
|
|
1067
|
+
this.clearFeedback = function (container, keepForRemovedField)
|
|
1068
|
+
{
|
|
1069
|
+
$lib.each(_fields, function (field, name)
|
|
1070
|
+
{
|
|
1071
|
+
if (container && field.element && (document.contains(field.element) || keepForRemovedField) && !container.contains(field.element))
|
|
1072
|
+
return;
|
|
1073
|
+
|
|
1074
|
+
clearFieldFeedback(name);
|
|
1075
|
+
});
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Removes the feedback messages for the specified field name.
|
|
1080
|
+
*/
|
|
1081
|
+
this.clearFieldFeedback = function (fieldName)
|
|
1082
|
+
{
|
|
1083
|
+
clearFieldFeedback(fieldName);
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Validates all fields or the fields within the specified container element.
|
|
1088
|
+
* @param {HTMLElement} [container] A container element to restrict validation to fields within the container.
|
|
1089
|
+
* @param {Boolean} [filledOnly] A value indicating to only validate filled out fields.
|
|
1090
|
+
*/
|
|
1091
|
+
this.validate = function (container, filledOnly)
|
|
1092
|
+
{
|
|
1093
|
+
return validate(container, filledOnly);
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* Validates a specific field
|
|
1098
|
+
* @param {String} fieldName The name of the form field.
|
|
1099
|
+
* @returns {boolean} A value indicating if the field is valid, works only for simple non async validations.
|
|
1100
|
+
*/
|
|
1101
|
+
this.validateField = function (fieldName)
|
|
1102
|
+
{
|
|
1103
|
+
validateField(getFieldByName(fieldName));
|
|
1104
|
+
return _fields[fieldName].isValid;
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Destroys the component.
|
|
1109
|
+
* @see {@link componyx.UI.base.methods#destroy}
|
|
1110
|
+
*/
|
|
1111
|
+
this.destroy = function (...args)
|
|
1112
|
+
{
|
|
1113
|
+
_instance.clear();
|
|
1114
|
+
$base.methods.destroy.call(this, ...args);
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
function postInit()
|
|
1118
|
+
{
|
|
1119
|
+
if (_instance.rules)
|
|
1120
|
+
initRules();
|
|
1121
|
+
|
|
1122
|
+
if (_instance.detectFields)
|
|
1123
|
+
_instance.detect();
|
|
1124
|
+
|
|
1125
|
+
initFeedback();
|
|
1126
|
+
_instance.events.onPostInit.fire(_instance);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function initRules()
|
|
1130
|
+
{
|
|
1131
|
+
if (typeof _instance.rules == 'string')
|
|
1132
|
+
_instance.rules = window.JSON.parse(_instance.rules);
|
|
1133
|
+
|
|
1134
|
+
$lib.each(_instance.rules, function (rule)
|
|
1135
|
+
{
|
|
1136
|
+
_instance.addRule(rule.fieldName, rule, rule.type);
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function initFeedback()
|
|
1141
|
+
{
|
|
1142
|
+
var fieldName;
|
|
1143
|
+
|
|
1144
|
+
if (_instance.validFields)
|
|
1145
|
+
{
|
|
1146
|
+
for (var index = 0; _instance.validFields[index]; ++index)
|
|
1147
|
+
{
|
|
1148
|
+
fieldName = _instance.validFields[index];
|
|
1149
|
+
_fields[fieldName].isValid = true;
|
|
1150
|
+
addFieldFeedback(getFieldByName(fieldName), _feedbackStatus.VALID, _fields[fieldName].feedbackId, true);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
for (fieldName in _instance.invalidFields)
|
|
1155
|
+
{
|
|
1156
|
+
addFieldFeedback(getFieldByName(fieldName), _feedbackStatus.INVALID, _fields[fieldName].feedbackId, true, _instance.invalidFields[fieldName]);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function getFieldByName(name)
|
|
1161
|
+
{
|
|
1162
|
+
return $lib(function (el)
|
|
1163
|
+
{
|
|
1164
|
+
var fieldName = getFieldName(el);
|
|
1165
|
+
|
|
1166
|
+
if (fieldName && fieldName == name)
|
|
1167
|
+
return true;
|
|
1168
|
+
|
|
1169
|
+
return false;
|
|
1170
|
+
}, _container || getForm(), '*', true);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function getRadioFields(el)
|
|
1174
|
+
{
|
|
1175
|
+
let name = getFieldName(el),
|
|
1176
|
+
root = getForm() || document,
|
|
1177
|
+
radios = root.querySelectorAll('input[type="radio"]'),
|
|
1178
|
+
fields = [];
|
|
1179
|
+
|
|
1180
|
+
$lib.each(radios, function (r)
|
|
1181
|
+
{
|
|
1182
|
+
if (name == getFieldName(r))
|
|
1183
|
+
fields.push(r);
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
return fields;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
function getFieldName(el)
|
|
1190
|
+
{
|
|
1191
|
+
var name = el.getAttribute('name');
|
|
1192
|
+
|
|
1193
|
+
if (name && name.indexOf('$') > -1 && _instance.staticASPNETFieldNames)
|
|
1194
|
+
name = name.substr(name.lastIndexOf('$') + 1);
|
|
1195
|
+
|
|
1196
|
+
return name;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
function getFieldValue(fieldEl)
|
|
1200
|
+
{
|
|
1201
|
+
let value, field = _fields[getFieldName(fieldEl)];
|
|
1202
|
+
|
|
1203
|
+
if (_fieldValueFromCache && field.lastValue != null)
|
|
1204
|
+
return field.lastValue;
|
|
1205
|
+
|
|
1206
|
+
if (' input textarea '.indexOf(' ' + fieldEl.nodeName.toLowerCase() + ' ') > -1)
|
|
1207
|
+
{
|
|
1208
|
+
if (fieldEl.type == 'radio')
|
|
1209
|
+
{
|
|
1210
|
+
let fields = getRadioFields(fieldEl);
|
|
1211
|
+
|
|
1212
|
+
$lib.each(fields, function (el)
|
|
1213
|
+
{
|
|
1214
|
+
if (el.checked)
|
|
1215
|
+
{
|
|
1216
|
+
value = el.value;
|
|
1217
|
+
return false;
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
else
|
|
1222
|
+
value = fieldEl.value;
|
|
1223
|
+
}
|
|
1224
|
+
else if (fieldEl.nodeName.toLowerCase() == 'select')
|
|
1225
|
+
value = fieldEl.options[fieldEl.selectedIndex].value;
|
|
1226
|
+
else // content-editable
|
|
1227
|
+
{
|
|
1228
|
+
let grab = field.getValue;
|
|
1229
|
+
value = (grab) ? getMethod(grab)(fieldEl) : fieldEl.innerHTML;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
return value;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
function getForm()
|
|
1236
|
+
{
|
|
1237
|
+
if (_instance.formId)
|
|
1238
|
+
return $lib('#' + _instance.formId);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
function validateForm(e)
|
|
1242
|
+
{
|
|
1243
|
+
_formSubmit = true;
|
|
1244
|
+
return validate(null, false, e);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
function validate(container, filledOnly, e)
|
|
1248
|
+
{
|
|
1249
|
+
const shouldValidate = (el) =>
|
|
1250
|
+
{
|
|
1251
|
+
if (!isVisible(el))
|
|
1252
|
+
return false;
|
|
1253
|
+
if (el.disabled)
|
|
1254
|
+
return true;
|
|
1255
|
+
if (!filledOnly)
|
|
1256
|
+
return true;
|
|
1257
|
+
if (el.type === 'checkbox' || el.type === 'radio')
|
|
1258
|
+
return el.checked;
|
|
1259
|
+
return !$lib.isEmpty(getFieldValue(el));
|
|
1260
|
+
};
|
|
1261
|
+
|
|
1262
|
+
if ($lib.isEmpty(_fields))
|
|
1263
|
+
return validationComplete(e);
|
|
1264
|
+
|
|
1265
|
+
if (container)
|
|
1266
|
+
{
|
|
1267
|
+
let fieldEls = container.querySelectorAll(_query),
|
|
1268
|
+
validated = {};
|
|
1269
|
+
|
|
1270
|
+
$lib.each(fieldEls, function (el, index)
|
|
1271
|
+
{
|
|
1272
|
+
let name = getFieldName(el);
|
|
1273
|
+
|
|
1274
|
+
if (!validated[name] && _fields[name] && shouldValidate(el))
|
|
1275
|
+
{
|
|
1276
|
+
validated[name] = true;
|
|
1277
|
+
validateField(el, true);
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
else
|
|
1282
|
+
{
|
|
1283
|
+
$lib.each(_fields, function (field, fieldName)
|
|
1284
|
+
{
|
|
1285
|
+
let el = getFieldByName(fieldName);
|
|
1286
|
+
|
|
1287
|
+
if (shouldValidate(el))
|
|
1288
|
+
validateField(getFieldByName(fieldName), true);
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
if (!$lib.isEmpty(_ajaxFields))
|
|
1293
|
+
{
|
|
1294
|
+
let form = getForm();
|
|
1295
|
+
|
|
1296
|
+
if (form)
|
|
1297
|
+
$lib.off(form, 'submit', validateForm);
|
|
1298
|
+
|
|
1299
|
+
if (e)
|
|
1300
|
+
e.preventDefault();
|
|
1301
|
+
|
|
1302
|
+
return false;
|
|
1303
|
+
}
|
|
1304
|
+
else
|
|
1305
|
+
_formSubmit = false;
|
|
1306
|
+
|
|
1307
|
+
return validationComplete(e);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function isVisible(el)
|
|
1311
|
+
{
|
|
1312
|
+
if (el.type === "hidden")
|
|
1313
|
+
return el.parentElement.offsetParent !== null;
|
|
1314
|
+
else
|
|
1315
|
+
return el.offsetParent !== null;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
function asyncValidationComplete()
|
|
1319
|
+
{
|
|
1320
|
+
var form = getForm();
|
|
1321
|
+
var formSubmit = _formSubmit;
|
|
1322
|
+
_formSubmit = false;
|
|
1323
|
+
|
|
1324
|
+
var isValid = validationComplete();
|
|
1325
|
+
|
|
1326
|
+
if (isValid && formSubmit)
|
|
1327
|
+
form.submit();
|
|
1328
|
+
|
|
1329
|
+
if (formSubmit)
|
|
1330
|
+
$lib.on(form, 'submit', validateForm);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
function validationComplete(e)
|
|
1334
|
+
{
|
|
1335
|
+
if (isValid())
|
|
1336
|
+
{
|
|
1337
|
+
var form = getForm();
|
|
1338
|
+
|
|
1339
|
+
if (form)
|
|
1340
|
+
_instance.setPostBackArgs(form);
|
|
1341
|
+
|
|
1342
|
+
_instance.events.onValid.fire(_instance);
|
|
1343
|
+
return true;
|
|
1344
|
+
}
|
|
1345
|
+
else
|
|
1346
|
+
{
|
|
1347
|
+
if (e)
|
|
1348
|
+
e.preventDefault();
|
|
1349
|
+
|
|
1350
|
+
_instance.events.onInvalid.fire(_instance);
|
|
1351
|
+
return false;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
function isValid()
|
|
1356
|
+
{
|
|
1357
|
+
var valid = true;
|
|
1358
|
+
|
|
1359
|
+
if ($lib.isEmpty(_fields))
|
|
1360
|
+
return valid;
|
|
1361
|
+
|
|
1362
|
+
$lib.each(_fields, function (field)
|
|
1363
|
+
{
|
|
1364
|
+
return (valid = field.isValid);
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
return valid;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
function validateFieldOnChange(field, summarise)
|
|
1371
|
+
{
|
|
1372
|
+
validateField(field, summarise);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
function validateFieldOnInput(field, summarise, e)
|
|
1376
|
+
{
|
|
1377
|
+
var keyCode = e.key;
|
|
1378
|
+
|
|
1379
|
+
clearLiveTimer();
|
|
1380
|
+
|
|
1381
|
+
if (keyCode == 'Tab')
|
|
1382
|
+
return;
|
|
1383
|
+
|
|
1384
|
+
_liveTimerId = setTimeout(function () { validateField(field, summarise); }, _instance.liveDelay);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
function clearLiveTimer()
|
|
1388
|
+
{
|
|
1389
|
+
clearTimeout(_liveTimerId);
|
|
1390
|
+
_liveTimerId = null;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function validateField(fieldEl, summarise, visited = new Set())
|
|
1394
|
+
{
|
|
1395
|
+
if (visited.has(fieldEl))
|
|
1396
|
+
return;
|
|
1397
|
+
|
|
1398
|
+
_fieldValueFromCache = false;
|
|
1399
|
+
|
|
1400
|
+
let valid = true,
|
|
1401
|
+
validField = true,
|
|
1402
|
+
fieldName = getFieldName(fieldEl),
|
|
1403
|
+
fieldType = fieldEl.type,
|
|
1404
|
+
fieldValue = getFieldValue(fieldEl) || '',
|
|
1405
|
+
fieldData = _fields[fieldName],
|
|
1406
|
+
showAll = (_instance.messageDisplay == _messageDisplayOption.FEEDBACKALL || _instance.messageDisplay == _messageDisplayOption.FEEDBACKFIRST_TOOLTIPALL || _instance.messageDisplay == _messageDisplayOption.TOOLTIPALL)
|
|
1407
|
+
|| (summarise && (_instance.summaryMessageDisplay == _messageDisplayOption.FEEDBACKALL || _instance.summaryMessageDisplay == _messageDisplayOption.FEEDBACKFIRST_TOOLTIPALL)),
|
|
1408
|
+
msg = [],
|
|
1409
|
+
dataType = (fieldData?.[_typeOption.DATATYPE]) ? fieldData[_typeOption.DATATYPE].dataType : null;
|
|
1410
|
+
|
|
1411
|
+
if (_liveTimerId)
|
|
1412
|
+
clearLiveTimer();
|
|
1413
|
+
|
|
1414
|
+
visited.add(fieldEl);
|
|
1415
|
+
|
|
1416
|
+
if (!fieldData)
|
|
1417
|
+
{
|
|
1418
|
+
validateDependentFields(fieldEl, visited);
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
if (fieldEl.disabled)
|
|
1423
|
+
{
|
|
1424
|
+
clearFieldFeedback(fieldName);
|
|
1425
|
+
fieldData.isValid = true;
|
|
1426
|
+
|
|
1427
|
+
if (!summarise)
|
|
1428
|
+
validationComplete();
|
|
1429
|
+
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
if (_ajaxFields[fieldName])
|
|
1434
|
+
{
|
|
1435
|
+
// cache validation when field is already in async validation
|
|
1436
|
+
let cached = _ajaxFields[fieldName].cached;
|
|
1437
|
+
cached.push(function () { validateField(fieldEl, summarise); });
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
if (!fieldData[_typeOption.REQUIRED] && !fieldData[_typeOption.CUSTOM] && !fieldData[_typeOption.COMPARE] && !summarise && fieldData.lastValue != null && fieldData.lastValue === fieldValue && ' radio checkbox '.indexOf(fieldType) == -1)
|
|
1442
|
+
{
|
|
1443
|
+
if (_instance.messageDisplay >= _messageDisplayOption.TOOLTIPFIRST && fieldData.tooltipTrigger)
|
|
1444
|
+
_tooltipManager.showTooltip(fieldData.tooltipTrigger);
|
|
1445
|
+
|
|
1446
|
+
validationComplete();
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
_fieldValueFromCache = true;
|
|
1451
|
+
fieldData.lastValue = fieldValue;
|
|
1452
|
+
fieldData.isValid = false;
|
|
1453
|
+
|
|
1454
|
+
if (fieldData[_typeOption.REQUIRED])
|
|
1455
|
+
validField = valid = validateRequired(fieldData[_typeOption.REQUIRED], fieldEl, msg);
|
|
1456
|
+
|
|
1457
|
+
if ((showAll || valid) && fieldData[_typeOption.COMPARE])
|
|
1458
|
+
{
|
|
1459
|
+
let dataType = fieldData[_typeOption.DATATYPE]?.dataType;
|
|
1460
|
+
|
|
1461
|
+
valid = validateCompare(fieldData[_typeOption.COMPARE], fieldValue, dataType, msg);
|
|
1462
|
+
|
|
1463
|
+
if (validField)
|
|
1464
|
+
validField = valid;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
if (!$lib.isEmpty(fieldValue))
|
|
1468
|
+
{
|
|
1469
|
+
if ((showAll || valid) && fieldData[_typeOption.DATATYPE])
|
|
1470
|
+
{
|
|
1471
|
+
valid = validateType(fieldData[_typeOption.DATATYPE], fieldValue, msg);
|
|
1472
|
+
|
|
1473
|
+
if (validField)
|
|
1474
|
+
validField = valid;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
if ((showAll || valid) && fieldData[_typeOption.LENGTH])
|
|
1478
|
+
{
|
|
1479
|
+
valid = validateLength(fieldData[_typeOption.LENGTH], fieldValue, msg);
|
|
1480
|
+
|
|
1481
|
+
if (validField)
|
|
1482
|
+
validField = valid;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
if ((showAll || valid) && fieldData[_typeOption.RANGE])
|
|
1486
|
+
{
|
|
1487
|
+
valid = validateRange(fieldData[_typeOption.RANGE], fieldValue, dataType, msg);
|
|
1488
|
+
|
|
1489
|
+
if (validField)
|
|
1490
|
+
validField = valid;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
if ((showAll || valid) && fieldData[_typeOption.REGEX])
|
|
1494
|
+
{
|
|
1495
|
+
valid = validateRegEx(fieldData[_typeOption.REGEX], fieldValue, msg);
|
|
1496
|
+
|
|
1497
|
+
if (validField)
|
|
1498
|
+
validField = valid;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
let settings = fieldData[_typeOption.CUSTOM];
|
|
1502
|
+
|
|
1503
|
+
if (settings && (settings.alwaysCheck || showAll || valid))
|
|
1504
|
+
{
|
|
1505
|
+
valid = validateCustom(settings, { settings: settings, field: fieldEl, fieldName: fieldName, fieldValue: fieldValue, valid: validField }, msg);
|
|
1506
|
+
|
|
1507
|
+
if (validField || settings.alwaysCheck)
|
|
1508
|
+
validField = valid;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
if (valid && fieldData[_typeOption.AJAX] && _instance.ajax.validate && _instance.ajax.validate.isDefined())
|
|
1512
|
+
{
|
|
1513
|
+
validateAjax(fieldData[_typeOption.AJAX], fieldEl, fieldName, fieldValue, summarise);
|
|
1514
|
+
let cached = _ajaxFields[fieldName].cached;
|
|
1515
|
+
cached.push(function () { validateDependentFields(fieldEl, visited); });
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
fieldData.isValid = validField;
|
|
1521
|
+
validateFieldComplete(fieldEl, summarise, msg);
|
|
1522
|
+
fieldData.silent = false;
|
|
1523
|
+
_fieldValueFromCache = false;
|
|
1524
|
+
|
|
1525
|
+
validateDependentFields(fieldEl, visited);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
function validateDependentFields(fieldEl, visited = new Set())
|
|
1529
|
+
{
|
|
1530
|
+
let dependentFields = [],
|
|
1531
|
+
fieldName = getFieldName(fieldEl),
|
|
1532
|
+
fieldData = _fields[fieldName];
|
|
1533
|
+
|
|
1534
|
+
if (!fieldData || !fieldData[_typeOption.COMPARE])
|
|
1535
|
+
dependentFields = getDependentFields(fieldEl);
|
|
1536
|
+
|
|
1537
|
+
dependentFields.forEach((fld) =>
|
|
1538
|
+
{
|
|
1539
|
+
let fieldName = getFieldName(fld),
|
|
1540
|
+
fieldData = _fields[fieldName];
|
|
1541
|
+
|
|
1542
|
+
if (fieldData)
|
|
1543
|
+
{
|
|
1544
|
+
fieldData.silent = true;
|
|
1545
|
+
validateField(fld, false, visited);
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
function getDependentFields(fieldEl)
|
|
1551
|
+
{
|
|
1552
|
+
const dependentFields = [];
|
|
1553
|
+
|
|
1554
|
+
$lib.each(_fields, function (fld, name)
|
|
1555
|
+
{
|
|
1556
|
+
const compare = fld[_typeOption.COMPARE];
|
|
1557
|
+
if (!compare || !compare.fields)
|
|
1558
|
+
return;
|
|
1559
|
+
|
|
1560
|
+
compare.fields.forEach(cf =>
|
|
1561
|
+
{
|
|
1562
|
+
const cfEl = (cf instanceof HTMLElement)
|
|
1563
|
+
? cf
|
|
1564
|
+
: getFieldByName(cf);
|
|
1565
|
+
|
|
1566
|
+
if (cfEl === fieldEl)
|
|
1567
|
+
dependentFields.push(getFieldByName(name));
|
|
1568
|
+
});
|
|
1569
|
+
});
|
|
1570
|
+
|
|
1571
|
+
return dependentFields;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
function validateRequired(settings, fieldEl, msg)
|
|
1575
|
+
{
|
|
1576
|
+
let valid = false;
|
|
1577
|
+
|
|
1578
|
+
if (settings.native && !settings.when)
|
|
1579
|
+
{
|
|
1580
|
+
valid = settings.checkValidity();
|
|
1581
|
+
}
|
|
1582
|
+
else
|
|
1583
|
+
{
|
|
1584
|
+
if (settings.when && !isRequired(settings.when, fieldEl))
|
|
1585
|
+
return true;
|
|
1586
|
+
|
|
1587
|
+
if (fieldEl.type == 'checkbox')
|
|
1588
|
+
valid = fieldEl.checked;
|
|
1589
|
+
else if (fieldEl.type == 'radio')
|
|
1590
|
+
{
|
|
1591
|
+
let fields = getRadioFields(fieldEl);
|
|
1592
|
+
$lib.each(fields, function (fld)
|
|
1593
|
+
{
|
|
1594
|
+
if (fld.checked)
|
|
1595
|
+
{
|
|
1596
|
+
valid = true;
|
|
1597
|
+
return false;
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
else
|
|
1602
|
+
{
|
|
1603
|
+
valid = !$lib.isEmpty(getFieldValue(fieldEl));
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
if (!valid && !$lib.isEmpty(settings.msg))
|
|
1608
|
+
msg.push(settings.msg);
|
|
1609
|
+
|
|
1610
|
+
return valid;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
function isRequired(when, field)
|
|
1614
|
+
{
|
|
1615
|
+
return getMethod(when)(_instance, { field: field });
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
function validateType(settings, value, msg)
|
|
1619
|
+
{
|
|
1620
|
+
var valid = true;
|
|
1621
|
+
|
|
1622
|
+
if (settings.native)
|
|
1623
|
+
{
|
|
1624
|
+
valid = settings.checkValidity();
|
|
1625
|
+
}
|
|
1626
|
+
else
|
|
1627
|
+
{
|
|
1628
|
+
switch (settings.dataType)
|
|
1629
|
+
{
|
|
1630
|
+
case _dataTypeOption.INTEGER:
|
|
1631
|
+
{
|
|
1632
|
+
var intRegEx = new RegExp('^[0-9]*[' + $lib.escapeRegExp(_instance.groupSeparator) + ']*[0-9]+$');
|
|
1633
|
+
valid = intRegEx.test(value);
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
case _dataTypeOption.FLOAT:
|
|
1637
|
+
{
|
|
1638
|
+
var floatRegEx = new RegExp('^[0-9]*[' + $lib.escapeRegExp(_instance.groupSeparator) + ']?[0-9]*[' + $lib.escapeRegExp(_instance.decimalSeparator) + ']?[0-9]+$');
|
|
1639
|
+
valid = floatRegEx.test(value);
|
|
1640
|
+
break;
|
|
1641
|
+
}
|
|
1642
|
+
case _dataTypeOption.DATETIME:
|
|
1643
|
+
{
|
|
1644
|
+
var date = $lib.parseDate(value, _instance.dateFormat.toLowerCase());
|
|
1645
|
+
|
|
1646
|
+
if (!date)
|
|
1647
|
+
valid = false;
|
|
1648
|
+
|
|
1649
|
+
break;
|
|
1650
|
+
}
|
|
1651
|
+
case _dataTypeOption.EMAIL:
|
|
1652
|
+
{
|
|
1653
|
+
valid = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/.test(value);
|
|
1654
|
+
break;
|
|
1655
|
+
}
|
|
1656
|
+
case _dataTypeOption.URL:
|
|
1657
|
+
{
|
|
1658
|
+
valid = /^(https?|ftp):\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]$/.test(value);
|
|
1659
|
+
break;
|
|
1660
|
+
}
|
|
1661
|
+
case _dataTypeOption.SOURCE:
|
|
1662
|
+
{
|
|
1663
|
+
valid = /^(blob:)?(https?|ftp|file):\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]$/.test(value) // absolute URL with allowed protocols, blob: URLs for file inputs
|
|
1664
|
+
|| /^\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]*$/.test(value); // relative URL starting with /
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
return addMsg(settings, msg, valid);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
function validateLength(settings, value, msg)
|
|
1674
|
+
{
|
|
1675
|
+
let valid = true, length = value.length;
|
|
1676
|
+
|
|
1677
|
+
if (settings.native)
|
|
1678
|
+
{
|
|
1679
|
+
valid = settings.checkValidity();
|
|
1680
|
+
}
|
|
1681
|
+
else
|
|
1682
|
+
{
|
|
1683
|
+
if (!$lib.isEmpty(settings.min))
|
|
1684
|
+
valid = (length >= settings.min);
|
|
1685
|
+
|
|
1686
|
+
if (valid && !$lib.isEmpty(settings.max))
|
|
1687
|
+
valid = (length <= settings.max);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
return addMsg(settings, msg, valid);
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
function validateRange(settings, value, dataType, msg)
|
|
1694
|
+
{
|
|
1695
|
+
var valid = true;
|
|
1696
|
+
|
|
1697
|
+
if (settings.native)
|
|
1698
|
+
{
|
|
1699
|
+
valid = settings.checkValidity();
|
|
1700
|
+
}
|
|
1701
|
+
else
|
|
1702
|
+
{
|
|
1703
|
+
if (dataType == _dataTypeOption.DATETIME)
|
|
1704
|
+
{
|
|
1705
|
+
var date = $lib.parseDate(value, _instance.dateFormat.toLowerCase()),
|
|
1706
|
+
minDate = $lib.parseDate(settings.min, _instance.dateFormat.toLowerCase()),
|
|
1707
|
+
maxDate = $lib.parseDate(settings.max, _instance.dateFormat.toLowerCase());
|
|
1708
|
+
|
|
1709
|
+
if (date)
|
|
1710
|
+
{
|
|
1711
|
+
if (minDate)
|
|
1712
|
+
valid = comparableDate(date) >= comparableDate(minDate);
|
|
1713
|
+
|
|
1714
|
+
if (maxDate && valid)
|
|
1715
|
+
valid = comparableDate(date) <= comparableDate(maxDate);
|
|
1716
|
+
}
|
|
1717
|
+
else
|
|
1718
|
+
valid = false;
|
|
1719
|
+
}
|
|
1720
|
+
else if (dataType == _dataTypeOption.INTEGER || dataType == _dataTypeOption.FLOAT)
|
|
1721
|
+
{
|
|
1722
|
+
var number = value.replace(_instance.decimalSeparator, '.');
|
|
1723
|
+
|
|
1724
|
+
valid = (!isNaN(number));
|
|
1725
|
+
|
|
1726
|
+
if (valid)
|
|
1727
|
+
{
|
|
1728
|
+
if (!isNaN(settings.min))
|
|
1729
|
+
valid = parseFloat(number) >= parseFloat(settings.min);
|
|
1730
|
+
|
|
1731
|
+
if (valid && !isNaN(settings.max))
|
|
1732
|
+
valid = parseFloat(number) <= parseFloat(settings.max);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
else
|
|
1736
|
+
{
|
|
1737
|
+
if (!$lib.isEmpty(settings.min))
|
|
1738
|
+
valid = (value >= settings.min);
|
|
1739
|
+
|
|
1740
|
+
if (valid && !$lib.isEmpty(settings.max))
|
|
1741
|
+
valid = (value <= settings.max);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
return addMsg(settings, msg, valid);
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
function validateRegEx(settings, value, msg)
|
|
1749
|
+
{
|
|
1750
|
+
let valid = false;
|
|
1751
|
+
|
|
1752
|
+
if (settings.native)
|
|
1753
|
+
{
|
|
1754
|
+
valid = settings.checkValidity();
|
|
1755
|
+
}
|
|
1756
|
+
else
|
|
1757
|
+
{
|
|
1758
|
+
if (typeof settings.pattern == 'string')
|
|
1759
|
+
settings.pattern = new RegExp(settings.pattern);
|
|
1760
|
+
|
|
1761
|
+
valid = settings.pattern.test(value);
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
return addMsg(settings, msg, valid);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
function validateCompare(settings, value, dataType, msg)
|
|
1768
|
+
{
|
|
1769
|
+
const fields = settings.fields,
|
|
1770
|
+
operators = settings.operators || [];
|
|
1771
|
+
|
|
1772
|
+
let valid = true;
|
|
1773
|
+
|
|
1774
|
+
for (let i = 0; i < fields.length; i++)
|
|
1775
|
+
{
|
|
1776
|
+
const f = fields[i],
|
|
1777
|
+
operator = operators[i] || operators[0] || '==';
|
|
1778
|
+
|
|
1779
|
+
let compareValue;
|
|
1780
|
+
|
|
1781
|
+
if (f instanceof HTMLElement)
|
|
1782
|
+
{
|
|
1783
|
+
compareValue = f.value;
|
|
1784
|
+
}
|
|
1785
|
+
else if (typeof f === 'string')
|
|
1786
|
+
{
|
|
1787
|
+
const el = getFieldByName(f);
|
|
1788
|
+
compareValue = el ? el.value : f; // fallback: treat as literal value
|
|
1789
|
+
} else
|
|
1790
|
+
{
|
|
1791
|
+
compareValue = f; // literal value
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
// convert types
|
|
1795
|
+
if (dataType === _dataTypeOption.INTEGER || dataType === _dataTypeOption.FLOAT)
|
|
1796
|
+
{
|
|
1797
|
+
const v1 = parseFloat(value.replace(_instance.decimalSeparator, '.')),
|
|
1798
|
+
v2 = parseFloat(compareValue.replace(_instance.decimalSeparator, '.'));
|
|
1799
|
+
|
|
1800
|
+
if (isNaN(v1) || isNaN(v2))
|
|
1801
|
+
return addMsg(settings, msg, false);
|
|
1802
|
+
|
|
1803
|
+
value = v1;
|
|
1804
|
+
compareValue = v2;
|
|
1805
|
+
} else if (dataType === _dataTypeOption.DATETIME)
|
|
1806
|
+
{
|
|
1807
|
+
const fmt = _instance.dateFormat.toLowerCase(),
|
|
1808
|
+
d1 = $lib.parseDate(value, fmt),
|
|
1809
|
+
d2 = $lib.parseDate(compareValue, fmt);
|
|
1810
|
+
|
|
1811
|
+
if (!d1 || !d2)
|
|
1812
|
+
return addMsg(settings, msg, false);
|
|
1813
|
+
|
|
1814
|
+
value = comparableDate(d1);
|
|
1815
|
+
compareValue = comparableDate(d2);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
// compare
|
|
1819
|
+
switch (operator)
|
|
1820
|
+
{
|
|
1821
|
+
case '<': valid = value < compareValue; break;
|
|
1822
|
+
case '<=': valid = value <= compareValue; break;
|
|
1823
|
+
case '>=': valid = value >= compareValue; break;
|
|
1824
|
+
case '>': valid = value > compareValue; break;
|
|
1825
|
+
default: valid = value == compareValue;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
if (!valid) break;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
return addMsg(settings, msg, valid);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
|
|
1835
|
+
function validateCustom(settings, obj, msg)
|
|
1836
|
+
{
|
|
1837
|
+
return addMsg(settings, msg, getMethod(settings.onValidation)(_instance, obj));
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
function validateAjax(settings, fieldEl, name, value, summarise)
|
|
1841
|
+
{
|
|
1842
|
+
var data;
|
|
1843
|
+
|
|
1844
|
+
_ajaxFields[name] = { cached: [] };
|
|
1845
|
+
|
|
1846
|
+
if (settings.onBeforeValidation)
|
|
1847
|
+
data = getMethod(settings.onBeforeValidation)(_instance, { field: fieldEl, fieldName: name, fieldValue: value, feedbackId: _fields[name].feedbackId, settings: settings });
|
|
1848
|
+
|
|
1849
|
+
if (settings.data)
|
|
1850
|
+
data = (data) ? $lib.clone(data, settings.data, true) : settings.data;
|
|
1851
|
+
|
|
1852
|
+
if (data && data.validationFields && data.validationFields[0])
|
|
1853
|
+
{
|
|
1854
|
+
if ($lib.isEmpty(data.validationFields[0].fieldName))
|
|
1855
|
+
data.validationFields[0].fieldName = name;
|
|
1856
|
+
|
|
1857
|
+
if ($lib.isEmpty(data.validationFields[0].value))
|
|
1858
|
+
data.validationFields[0].value = value;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
_validatingTimerId = setTimeout(function () { addFieldFeedback(fieldEl, _feedbackStatus.VALIDATING, _fields[name].feedbackId, false); }, _instance.validatingFieldMessageDelay);
|
|
1862
|
+
|
|
1863
|
+
_instance.ajaxCall('validate', data,
|
|
1864
|
+
{
|
|
1865
|
+
onSuccess: function (ajaxArgs)
|
|
1866
|
+
{
|
|
1867
|
+
validateAjaxFieldComplete(fieldEl, name, value, settings, summarise, ajaxArgs);
|
|
1868
|
+
},
|
|
1869
|
+
onError: function (ajaxArgs)
|
|
1870
|
+
{
|
|
1871
|
+
validateAjaxFieldComplete(fieldEl, name, value, settings, summarise, ajaxArgs, true);
|
|
1872
|
+
}
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
function addMsg(settings, msg, valid)
|
|
1877
|
+
{
|
|
1878
|
+
if (!valid && !$lib.isEmpty(settings.msg) && msg)
|
|
1879
|
+
msg.push(settings.msg);
|
|
1880
|
+
|
|
1881
|
+
return valid;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
function validateAjaxFieldComplete(field, fieldName, fieldValue, settings, summarise, ajaxArgs, ajaxError)
|
|
1885
|
+
{
|
|
1886
|
+
var msg,
|
|
1887
|
+
fieldData = _fields[fieldName],
|
|
1888
|
+
cached = _ajaxFields[fieldName].cached,
|
|
1889
|
+
data = settings.data;
|
|
1890
|
+
|
|
1891
|
+
if (_validatingTimerId)
|
|
1892
|
+
clearTimeout(_validatingTimerId);
|
|
1893
|
+
|
|
1894
|
+
_validatingTimerId = null;
|
|
1895
|
+
fieldData.lastValue = fieldData.lastMsg = null;
|
|
1896
|
+
fieldData.isValid = false;
|
|
1897
|
+
fieldData.silent = false;
|
|
1898
|
+
msg = _instance.ajaxErrorMessage;
|
|
1899
|
+
|
|
1900
|
+
if (data && data.validationFields)
|
|
1901
|
+
{
|
|
1902
|
+
$lib.each(data.validationFields, function (field)
|
|
1903
|
+
{
|
|
1904
|
+
field.fieldName = field.value = null;
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
if (!ajaxError)
|
|
1909
|
+
{
|
|
1910
|
+
msg = (!$lib.isEmpty(ajaxArgs.data.msg)) ? ajaxArgs.data.msg : settings.msg;
|
|
1911
|
+
|
|
1912
|
+
fieldData.lastValue = fieldValue;
|
|
1913
|
+
fieldData.lastMsg = msg;
|
|
1914
|
+
fieldData.isValid = ajaxArgs.data.isValid;
|
|
1915
|
+
|
|
1916
|
+
if (!ajaxArgs.data.isValid && (!msg.constructor || msg.constructor.toString().indexOf('Array') == -1))
|
|
1917
|
+
msg = [msg];
|
|
1918
|
+
|
|
1919
|
+
if (settings.onAfterValidation)
|
|
1920
|
+
getMethod(settings.onAfterValidation)(_instance, { field: field, fieldName: fieldName, fieldValue: fieldValue });
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
validateFieldComplete(field, summarise, msg, true);
|
|
1924
|
+
delete _ajaxFields[fieldName];
|
|
1925
|
+
|
|
1926
|
+
if (cached && cached.length)
|
|
1927
|
+
cached.forEach((callback) => callback());
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
function validateFieldComplete(fieldEL, summarise, msg, async)
|
|
1931
|
+
{
|
|
1932
|
+
var fieldName = getFieldName(fieldEL),
|
|
1933
|
+
fieldValue = getFieldValue(fieldEL),
|
|
1934
|
+
fieldData = _fields[fieldName],
|
|
1935
|
+
showValid = _instance.showValidFields,
|
|
1936
|
+
args = eventArgs(fieldEL, fieldName, fieldValue, fieldData, msg);
|
|
1937
|
+
|
|
1938
|
+
clearCss(fieldEL);
|
|
1939
|
+
|
|
1940
|
+
// fire event
|
|
1941
|
+
(fieldData.isValid) ? _instance.events.onValidField.fire(_instance, args) : _instance.events.onInvalidField.fire(_instance, args);
|
|
1942
|
+
|
|
1943
|
+
if (fieldData.isValid && (showValid == _showValidFieldsOption.ALL
|
|
1944
|
+
|| (showValid == _showValidFieldsOption.FILLED && !$lib.isEmpty(fieldValue))
|
|
1945
|
+
|| (showValid == _showValidFieldsOption.REQUIRED && fieldData[_typeOption.REQUIRED])))
|
|
1946
|
+
{
|
|
1947
|
+
clearFieldFeedback(fieldName);
|
|
1948
|
+
addFieldFeedback(fieldEL, _feedbackStatus.VALID, _fields[fieldName].feedbackId, summarise, msg);
|
|
1949
|
+
}
|
|
1950
|
+
else if (!fieldData.isValid)
|
|
1951
|
+
{
|
|
1952
|
+
addFieldFeedback(fieldEL, _feedbackStatus.INVALID, _fields[fieldName].feedbackId, summarise, msg);
|
|
1953
|
+
}
|
|
1954
|
+
else
|
|
1955
|
+
{
|
|
1956
|
+
clearFieldFeedback(fieldName);
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
if (!summarise && !async)
|
|
1960
|
+
validationComplete();
|
|
1961
|
+
else if (async && $lib.isEmpty(_ajaxFields))
|
|
1962
|
+
asyncValidationComplete();
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
function eventArgs()
|
|
1966
|
+
{
|
|
1967
|
+
var args = arguments;
|
|
1968
|
+
return { fieldElement: args[0], fieldName: args[1], fieldValue: args[2], fieldData: args[3], msg: args[4], feedbackElement: args[5], summaryElement: args[6] }
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
function comparableDate(date)
|
|
1972
|
+
{
|
|
1973
|
+
return parseInt(date.getFullYear() + addNil(date.getMonth(), true) + addNil(date.getDate(), true));
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
function addNil(val, leadingZero)
|
|
1977
|
+
{
|
|
1978
|
+
val = val.toString();
|
|
1979
|
+
|
|
1980
|
+
if (!leadingZero)
|
|
1981
|
+
return val;
|
|
1982
|
+
|
|
1983
|
+
return (val.length != 2) ? '0' + val : val;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
function addFieldFeedback(fieldEl, status, feedbackId, summarise, msg)
|
|
1987
|
+
{
|
|
1988
|
+
if (!fieldEl)
|
|
1989
|
+
return;
|
|
1990
|
+
|
|
1991
|
+
var feedbackContainer = (feedbackId) ? $lib('#' + feedbackId) : getFeedbackElement(fieldEl),
|
|
1992
|
+
summaryContainer = (_instance.summaryContainerId) ? $lib('#' + _instance.summaryContainerId) : null,
|
|
1993
|
+
fieldName = getFieldName(fieldEl),
|
|
1994
|
+
fieldValue = getFieldValue(fieldEl),
|
|
1995
|
+
fieldData = _fields[fieldName],
|
|
1996
|
+
silent = fieldData.silent,
|
|
1997
|
+
values = {},
|
|
1998
|
+
summarise = summaryContainer && summarise,
|
|
1999
|
+
triggerClass = _instance.cssClassTooltipTrigger || _classOption.TOOLTIPTRIGGER,
|
|
2000
|
+
invalidCssClass = _instance.cssClassInvalid || _classOption.INVALID,
|
|
2001
|
+
validCssClass = _instance.cssClassValid || _classOption.VALID,
|
|
2002
|
+
validatingCssClass = _instance.cssClassValidating || _classOption.VALIDATING,
|
|
2003
|
+
ulFeedback, ulSummary, ul, li, liSummary;
|
|
2004
|
+
|
|
2005
|
+
if (!silent)
|
|
2006
|
+
{
|
|
2007
|
+
if (feedbackContainer && _instance.messageDisplay > _messageDisplayOption.NONE)
|
|
2008
|
+
{
|
|
2009
|
+
ul = document.createElement('ul');
|
|
2010
|
+
|
|
2011
|
+
if (fieldData.feedbackElement?.isConnected)
|
|
2012
|
+
{
|
|
2013
|
+
ulFeedback = fieldData.feedbackElement;
|
|
2014
|
+
ulFeedback.parentNode.replaceChild(ul, ulFeedback);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
fieldData.feedbackElement = ulFeedback = ul;
|
|
2018
|
+
addValidatorClass(ulFeedback);
|
|
2019
|
+
$lib.addClass(ulFeedback, _instance.cssClassFeedback || _classOption.FEEDBACK);
|
|
2020
|
+
feedbackContainer.appendChild(ulFeedback);
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
if (summarise && ((status == _feedbackStatus.INVALID && _instance.summaryMessageDisplay > _messageDisplayOption.NONE) || (status == _feedbackStatus.VALID && _instance.showValidFieldsInSummary)))
|
|
2024
|
+
{
|
|
2025
|
+
ul = document.createElement('ul');
|
|
2026
|
+
|
|
2027
|
+
if (fieldData.summaryElement?.isConnected)
|
|
2028
|
+
{
|
|
2029
|
+
ulSummary = fieldData.summaryElement;
|
|
2030
|
+
ulSummary.parentNode.replaceChild(ul, ulSummary);
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
fieldData.summaryElement = ulSummary = ul;
|
|
2034
|
+
addValidatorClass(ulSummary);
|
|
2035
|
+
$lib.addClass(ulSummary, _instance.cssClassSummary || _classOption.SUMMARY);
|
|
2036
|
+
summaryContainer.appendChild(ulSummary);
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
values['fieldName'] = _instance.friendlyFieldNames[fieldName] || fieldName;
|
|
2040
|
+
values['icon'] = $lib.format('<span class="{0}"></span>', _instance.cssClassIcon || _classOption.ICON);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
switch (status)
|
|
2044
|
+
{
|
|
2045
|
+
case _feedbackStatus.VALIDATING:
|
|
2046
|
+
{
|
|
2047
|
+
if (_instance.validationCssOnField)
|
|
2048
|
+
$lib.addClass(fieldEl, validatingCssClass);
|
|
2049
|
+
|
|
2050
|
+
if (!ulFeedback)
|
|
2051
|
+
break;
|
|
2052
|
+
|
|
2053
|
+
values['message'] = _instance.validatingFieldMessage;
|
|
2054
|
+
|
|
2055
|
+
if (ulFeedback)
|
|
2056
|
+
createItem(ulFeedback, validatingCssClass, 'ValidatingFieldFeedback', values);
|
|
2057
|
+
|
|
2058
|
+
break;
|
|
2059
|
+
}
|
|
2060
|
+
case _feedbackStatus.VALID:
|
|
2061
|
+
{
|
|
2062
|
+
if (_instance.validationCssOnField)
|
|
2063
|
+
$lib.addClass(fieldEl, validCssClass);
|
|
2064
|
+
|
|
2065
|
+
if (!ulFeedback && !ulSummary)
|
|
2066
|
+
break;
|
|
2067
|
+
|
|
2068
|
+
values['message'] = _instance.validFieldMessage;
|
|
2069
|
+
|
|
2070
|
+
if (ulFeedback)
|
|
2071
|
+
createItem(ulFeedback, validCssClass, 'ValidFieldFeedback', values);
|
|
2072
|
+
|
|
2073
|
+
if (ulSummary)
|
|
2074
|
+
createItem(ulSummary, validCssClass, 'ValidFieldSummary', values);
|
|
2075
|
+
|
|
2076
|
+
break;
|
|
2077
|
+
}
|
|
2078
|
+
case _feedbackStatus.INVALID:
|
|
2079
|
+
{
|
|
2080
|
+
if (_instance.validationCssOnField)
|
|
2081
|
+
$lib.addClass(fieldEl, invalidCssClass);
|
|
2082
|
+
|
|
2083
|
+
if (silent || (!ulFeedback && !ulSummary && _instance.messageDisplay < 3))
|
|
2084
|
+
break;
|
|
2085
|
+
|
|
2086
|
+
let multipleStart = $lib.format('<span class="{0}">', _instance.cssClassMultiple || _classOption.MULTIPLE),
|
|
2087
|
+
tooltipTriggerStart = $lib.format('<span class="{0}">', triggerClass),
|
|
2088
|
+
spanEnd = '</span>',
|
|
2089
|
+
tooltipTriggerKey = 'tooltipTrigger',
|
|
2090
|
+
multipleKey = 'multiple';
|
|
2091
|
+
|
|
2092
|
+
$lib.addClass(ulFeedback, invalidCssClass);
|
|
2093
|
+
values['messageCount'] = msg.length;
|
|
2094
|
+
values['tooltipTrigger'] = values['/tooltipTrigger'] = '';
|
|
2095
|
+
values['multiple'] = '<span style="display:none;">';
|
|
2096
|
+
values['/multiple'] = '</span>';
|
|
2097
|
+
|
|
2098
|
+
if (!msg.length)
|
|
2099
|
+
msg.push(_instance.invalidFieldMessage);
|
|
2100
|
+
|
|
2101
|
+
if (msg.length > 1)
|
|
2102
|
+
{
|
|
2103
|
+
if (_instance.messageDisplay == _messageDisplayOption.FEEDBACKFIRST_TOOLTIPALL)
|
|
2104
|
+
{
|
|
2105
|
+
values[multipleKey] = multipleStart;
|
|
2106
|
+
values[tooltipTriggerKey] = tooltipTriggerStart;
|
|
2107
|
+
values['/' + tooltipTriggerKey] = spanEnd;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
$lib.each(msg, function (msg, index)
|
|
2112
|
+
{
|
|
2113
|
+
values['message'] = msg;
|
|
2114
|
+
|
|
2115
|
+
if (ulFeedback)
|
|
2116
|
+
{
|
|
2117
|
+
if (_instance.messageDisplay < _messageDisplayOption.TOOLTIPFIRST && (index == 0 || _instance.messageDisplay == _messageDisplayOption.FEEDBACKALL))
|
|
2118
|
+
li = createItem(ulFeedback, invalidCssClass, 'InvalidFieldFeedback', values);
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
if (ulSummary)
|
|
2122
|
+
{
|
|
2123
|
+
if (index == 0 || _instance.messageDisplay == _messageDisplayOption.FEEDBACKALL)
|
|
2124
|
+
liSummary = createItem(ulSummary, invalidCssClass, 'InvalidFieldSummary', values);
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
|
|
2128
|
+
if (msg.length > 0)
|
|
2129
|
+
{
|
|
2130
|
+
if (_instance.messageDisplay >= _messageDisplayOption.TOOLTIPFIRST)
|
|
2131
|
+
addTooltip(fieldEl, msg, false, fieldData);
|
|
2132
|
+
else if (ulFeedback && _instance.messageDisplay == _messageDisplayOption.FEEDBACKFIRST_TOOLTIPALL)
|
|
2133
|
+
addTooltip($lib(triggerClass, li, '', true), msg, true, fieldData);
|
|
2134
|
+
|
|
2135
|
+
if (ulSummary && _instance.summaryMessageDisplay == _messageDisplayOption.FEEDBACKFIRST_TOOLTIPALL)
|
|
2136
|
+
addTooltip($lib(triggerClass, liSummary, '', true), msg, true, fieldData, true);
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
break;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
fieldData.lastMsg = msg;
|
|
2144
|
+
|
|
2145
|
+
_instance.events.onFieldFeedback.fire(_instance, eventArgs(fieldEl, fieldName, fieldValue, fieldData, msg, ulFeedback, ulSummary));
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
function createItem(ul, cssClass, template, values)
|
|
2149
|
+
{
|
|
2150
|
+
$lib.addClass(ul, cssClass);
|
|
2151
|
+
var li = ul.appendChild(document.createElement('li'));
|
|
2152
|
+
_instance.applyTemplate(li, template, values);
|
|
2153
|
+
|
|
2154
|
+
return li;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
function clearFieldFeedback(fieldName)
|
|
2158
|
+
{
|
|
2159
|
+
if (!fieldName)
|
|
2160
|
+
return;
|
|
2161
|
+
|
|
2162
|
+
var fieldData = _fields[fieldName];
|
|
2163
|
+
|
|
2164
|
+
if (fieldData.feedbackElement)
|
|
2165
|
+
fieldData.feedbackElement.innerHTML = '';
|
|
2166
|
+
if (fieldData.summaryElement)
|
|
2167
|
+
fieldData.summaryElement.innerHTML = '';
|
|
2168
|
+
|
|
2169
|
+
fieldData.lastMsg = null;
|
|
2170
|
+
|
|
2171
|
+
if (fieldData.tooltipId)
|
|
2172
|
+
{
|
|
2173
|
+
_tooltipManager.hideTooltip(fieldData.tooltipTrigger);
|
|
2174
|
+
_tooltipManager.removeTrigger(fieldData.tooltipTrigger);
|
|
2175
|
+
fieldData.tooltipId = fieldData.tooltipTrigger = null;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
if (fieldData.summaryTooltipId)
|
|
2179
|
+
{
|
|
2180
|
+
_tooltipManager.hideTooltip(fieldData.summaryTooltipTrigger);
|
|
2181
|
+
_tooltipManager.removeTrigger(fieldData.summaryTooltipTrigger);
|
|
2182
|
+
fieldData.summaryTooltipId = fieldData.summaryTooltipTrigger = null;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
if (fieldData.element)
|
|
2186
|
+
clearCss(fieldData.element);
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
function clearCss(element)
|
|
2190
|
+
{
|
|
2191
|
+
if (!element)
|
|
2192
|
+
return;
|
|
2193
|
+
|
|
2194
|
+
var invalidCssClass = _instance.cssClassInvalid || _classOption.INVALID,
|
|
2195
|
+
validCssClass = _instance.cssClassValid || _classOption.VALID,
|
|
2196
|
+
validatingCssClass = _instance.cssClassValidating || _classOption.VALIDATING;
|
|
2197
|
+
|
|
2198
|
+
$lib.removeClass(element, validatingCssClass + ' ' + validCssClass + ' ' + invalidCssClass);
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
function createTooltipManager()
|
|
2202
|
+
{
|
|
2203
|
+
var id = _instance.id + '_TooltipManager';
|
|
2204
|
+
|
|
2205
|
+
_tooltipManager = $UI.createComponent(componyx.UI.TooltipManager, { id: id });
|
|
2206
|
+
_tooltipManager.clone($UI.store[_instance.tooltipManagerId], _instance, true, true, { triggers: '' });
|
|
2207
|
+
_tooltipManager.singleBoxInstance = false;
|
|
2208
|
+
|
|
2209
|
+
if (!_tooltipManager.boxId)
|
|
2210
|
+
{
|
|
2211
|
+
var box = $UI.createComponent(componyx.UI.Box, { id: id + '_TempBox' });
|
|
2212
|
+
box.expandDirection = componyx.UI.Box.ExpandDirectionOption.DOWN;
|
|
2213
|
+
box.autoPosition = componyx.UI.Box.AutoPositionOption.EXPAND;
|
|
2214
|
+
box.autoInvertFit = true;
|
|
2215
|
+
_tooltipManager.boxId = box.id;
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
_tooltipManager.show();
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
function addTooltip(el, msg, isTrigger, fieldData, summary)
|
|
2222
|
+
{
|
|
2223
|
+
if (!el)
|
|
2224
|
+
return;
|
|
2225
|
+
|
|
2226
|
+
var ul = document.createElement('ul'), values = {},
|
|
2227
|
+
id = (summary) ? fieldData.summaryTooltipId : fieldData.tooltipId;
|
|
2228
|
+
|
|
2229
|
+
if (!id)
|
|
2230
|
+
id = $lib.guid();
|
|
2231
|
+
|
|
2232
|
+
if (summary)
|
|
2233
|
+
{
|
|
2234
|
+
fieldData.summaryTooltipId = id;
|
|
2235
|
+
fieldData.summaryTooltipTrigger = el;
|
|
2236
|
+
}
|
|
2237
|
+
else
|
|
2238
|
+
{
|
|
2239
|
+
fieldData.tooltipId = id;
|
|
2240
|
+
fieldData.tooltipTrigger = el;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
addValidatorClass(ul);
|
|
2244
|
+
$lib.addClass(ul, 'tooltip');
|
|
2245
|
+
|
|
2246
|
+
$lib.each(msg, function (msg)
|
|
2247
|
+
{
|
|
2248
|
+
values['message'] = msg;
|
|
2249
|
+
_instance.applyTemplate(ul.appendChild(document.createElement('li')), 'InvalidFieldPopup', values);
|
|
2250
|
+
});
|
|
2251
|
+
|
|
2252
|
+
_tooltipManager.addTooltip(id, ul); // add or update
|
|
2253
|
+
|
|
2254
|
+
if (isTrigger) // trigger element
|
|
2255
|
+
{
|
|
2256
|
+
el.setAttribute('tabindex', _instance.tooltipTriggerTabIndex || '0');
|
|
2257
|
+
_tooltipManager.addTrigger(el, id);
|
|
2258
|
+
}
|
|
2259
|
+
else // field
|
|
2260
|
+
_tooltipManager.addTrigger(el, id, 0, false, false); // show on blur
|
|
2261
|
+
|
|
2262
|
+
if (!isTrigger)
|
|
2263
|
+
_tooltipManager.showTooltip(el);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
function hideTooltip(fieldName)
|
|
2267
|
+
{
|
|
2268
|
+
var fieldData = _fields[fieldName];
|
|
2269
|
+
|
|
2270
|
+
if (fieldData.tooltipTrigger)
|
|
2271
|
+
_tooltipManager.hideTooltip(fieldData.tooltipTrigger);
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
function getFeedbackElement(field)
|
|
2275
|
+
{
|
|
2276
|
+
if (!_instance.feedbackIdentifyingCssClass)
|
|
2277
|
+
return null;
|
|
2278
|
+
|
|
2279
|
+
var feedbackElement = null;
|
|
2280
|
+
|
|
2281
|
+
$lib(function (el, stop)
|
|
2282
|
+
{
|
|
2283
|
+
if (feedbackElement = $lib(_instance.feedbackIdentifyingCssClass, el)[0])
|
|
2284
|
+
{
|
|
2285
|
+
stop();
|
|
2286
|
+
return false;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
return false;
|
|
2290
|
+
|
|
2291
|
+
}, field, '', false, true);
|
|
2292
|
+
|
|
2293
|
+
return feedbackElement || null;
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
function addValidatorClass(el)
|
|
2297
|
+
{
|
|
2298
|
+
$lib.addClass(el, _instance.className || _classOption.VALIDATOR);
|
|
2299
|
+
|
|
2300
|
+
if (_instance.theme > $base.static.ThemeOption.NONE)
|
|
2301
|
+
{
|
|
2302
|
+
$lib.addClass(el, 'theme');
|
|
2303
|
+
$lib.addClass(el, _themes[parseInt(_instance.theme, 10) - 1]);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
function getMethod(method)
|
|
2308
|
+
{
|
|
2309
|
+
return $base.static.getMethod(method);
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
function hasRule(name)
|
|
2313
|
+
{
|
|
2314
|
+
if (!_fields[name])
|
|
2315
|
+
return false;
|
|
2316
|
+
|
|
2317
|
+
for (var key in _typeOption)
|
|
2318
|
+
{
|
|
2319
|
+
if (_fields[name][_typeOption[key]])
|
|
2320
|
+
return true;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
return false;
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
function resetState(field, name)
|
|
2327
|
+
{
|
|
2328
|
+
if (!field)
|
|
2329
|
+
return;
|
|
2330
|
+
|
|
2331
|
+
field.isValid = false;
|
|
2332
|
+
field.lastValue = null;
|
|
2333
|
+
|
|
2334
|
+
if (!field[_typeOption.REQUIRED] && $lib.isEmpty(getFieldValue(field.element || getFieldByName(name)))) // not required and no field value
|
|
2335
|
+
field.isValid = true;
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
function removeBinding(name)
|
|
2339
|
+
{
|
|
2340
|
+
let fieldEl = getFieldByName(name),
|
|
2341
|
+
fieldEls = [fieldEl];
|
|
2342
|
+
|
|
2343
|
+
if (!fieldEl)
|
|
2344
|
+
return;
|
|
2345
|
+
|
|
2346
|
+
if (fieldEl.type == 'radio')
|
|
2347
|
+
fieldEls = getRadioFields(fieldEl);
|
|
2348
|
+
|
|
2349
|
+
$lib.each(fieldEls, function (fld)
|
|
2350
|
+
{
|
|
2351
|
+
$lib.off(fld, 'blur change', validateFieldOnChange);
|
|
2352
|
+
$lib.off(fld, 'focus', hideTooltip);
|
|
2353
|
+
$lib.off(fld, 'input', validateFieldOnInput);
|
|
2354
|
+
});
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
|
|
2358
|
+
/**
|
|
2359
|
+
* @see {@link componyx.UI.base.methods}
|
|
2360
|
+
*/
|
|
2361
|
+
componyx.UI.Validator.prototype = Object.create($base.methods);
|
|
2362
|
+
componyx.UI.Validator.prototype.constructor = componyx.UI.Validator;
|
|
2363
|
+
|
|
2364
|
+
/**
|
|
2365
|
+
* TypeOption
|
|
2366
|
+
* @readonly
|
|
2367
|
+
* @enum {number}
|
|
2368
|
+
* @memberof componyx.UI.Validator
|
|
2369
|
+
*/
|
|
2370
|
+
componyx.UI.Validator.TypeOption =
|
|
2371
|
+
{
|
|
2372
|
+
REQUIRED: 0,
|
|
2373
|
+
DATATYPE: 1,
|
|
2374
|
+
LENGTH: 2,
|
|
2375
|
+
RANGE: 3,
|
|
2376
|
+
REGEX: 4,
|
|
2377
|
+
COMPARE: 5,
|
|
2378
|
+
CUSTOM: 6,
|
|
2379
|
+
AJAX: 7
|
|
2380
|
+
};
|
|
2381
|
+
|
|
2382
|
+
/**
|
|
2383
|
+
* DataTypeOption
|
|
2384
|
+
* @readonly
|
|
2385
|
+
* @enum {number}
|
|
2386
|
+
* @memberof componyx.UI.Validator
|
|
2387
|
+
*/
|
|
2388
|
+
componyx.UI.Validator.DataTypeOption =
|
|
2389
|
+
{
|
|
2390
|
+
INTEGER: 0,
|
|
2391
|
+
FLOAT: 1,
|
|
2392
|
+
DATETIME: 2,
|
|
2393
|
+
EMAIL: 3,
|
|
2394
|
+
URL: 4,
|
|
2395
|
+
SOURCE: 5
|
|
2396
|
+
};
|
|
2397
|
+
|
|
2398
|
+
/**
|
|
2399
|
+
* ShowValidFieldsOption
|
|
2400
|
+
* @readonly
|
|
2401
|
+
* @enum {number}
|
|
2402
|
+
* @memberof componyx.UI.Validator
|
|
2403
|
+
*/
|
|
2404
|
+
componyx.UI.Validator.ShowValidFieldsOption =
|
|
2405
|
+
{
|
|
2406
|
+
NONE: 0,
|
|
2407
|
+
REQUIRED: 1,
|
|
2408
|
+
FILLED: 2,
|
|
2409
|
+
ALL: 3
|
|
2410
|
+
};
|
|
2411
|
+
|
|
2412
|
+
/**
|
|
2413
|
+
* MessageDisplayOption
|
|
2414
|
+
* @readonly
|
|
2415
|
+
* @enum {number}
|
|
2416
|
+
* @memberof componyx.UI.Validator
|
|
2417
|
+
*/
|
|
2418
|
+
componyx.UI.Validator.MessageDisplayOption =
|
|
2419
|
+
{
|
|
2420
|
+
NONE: 0,
|
|
2421
|
+
FEEDBACKFIRST: 1,
|
|
2422
|
+
FEEDBACKALL: 2,
|
|
2423
|
+
FEEDBACKFIRST_TOOLTIPALL: 3,
|
|
2424
|
+
TOOLTIPFIRST: 4,
|
|
2425
|
+
TOOLTIPALL: 5
|
|
2426
|
+
};
|
|
2427
|
+
|
|
2428
|
+
/**
|
|
2429
|
+
* @typedef {object} ValidationData
|
|
2430
|
+
* @memberof componyx.UI.Validator
|
|
2431
|
+
* @property {string} fieldName The name of the field to validate.
|
|
2432
|
+
* @property {string} value The value of the field to validate.
|
|
2433
|
+
*/
|
|
2434
|
+
|
|
2435
|
+
/**
|
|
2436
|
+
* @typedef {object} ValidationArgs
|
|
2437
|
+
* @memberof componyx.UI.Validator
|
|
2438
|
+
* @property {HTMLElement} [field] The field.
|
|
2439
|
+
* @property {string} [fieldName] The field name.
|
|
2440
|
+
* @property {string} [fieldValue] The field value.
|
|
2441
|
+
* @property {string} [feedbackId] The field feedback identifier.
|
|
2442
|
+
* @property {object} [settings] The validation settings of the field.
|
|
2443
|
+
*/
|
|
2444
|
+
|
|
2445
|
+
/**
|
|
2446
|
+
* @callback OnValidation
|
|
2447
|
+
* @memberof componyx.UI.Validator
|
|
2448
|
+
* @param {componyx.UI.Validator} sender
|
|
2449
|
+
* @param {ValidationArgs} args
|
|
2450
|
+
* @returns {boolean} A value indicating if the validation result is valid or invalid.
|
|
2451
|
+
*/
|
|
2452
|
+
/**
|
|
2453
|
+
* @callback OnBeforeValidation
|
|
2454
|
+
* @memberof componyx.UI.Validator
|
|
2455
|
+
* @param {componyx.UI.Validator} sender
|
|
2456
|
+
* @param {ValidationArgs} args
|
|
2457
|
+
* @returns {ValidationData[]} The validation data.
|
|
2458
|
+
*/
|
|
2459
|
+
/**
|
|
2460
|
+
* @callback OnAfterValidation
|
|
2461
|
+
* @memberof componyx.UI.Validator
|
|
2462
|
+
* @param {componyx.UI.Validator} sender
|
|
2463
|
+
* @param {ValidationArgs} args
|
|
2464
|
+
*/
|
|
2465
|
+
|
|
2466
|
+
/**
|
|
2467
|
+
* Creates a Rule object instance.
|
|
2468
|
+
* @class
|
|
2469
|
+
* @property {String} fieldName Gets or sets the field name to which the rule applies.
|
|
2470
|
+
* @property {TypeOption} type Gets or sets the validation type of the rule.
|
|
2471
|
+
* @param {String} feedbackId Gets or sets the identifier of the element used by the Validator to display validation result feedback.
|
|
2472
|
+
* @param {Boolean} autoValidate Gets or sets a value indicating if the field is validated when the field value changes.
|
|
2473
|
+
* @param {Boolean} live Gets or sets a value indicating if the field validation is done while typing.
|
|
2474
|
+
* @param {Function} getValue A custom method to return the value for this field. Passes in the field element and expects the field value as result.
|
|
2475
|
+
* @param {String} msg Gets or sets the message to display if validation fails.
|
|
2476
|
+
* @param {Number|String} min Gets or sets the minimum value. (LENGTH and RANGE type validation).
|
|
2477
|
+
* @param {Number|String} max Gets or sets the maximum value. (LENGTH and RANGE type validation).
|
|
2478
|
+
* @param {RegExp} pattern Gets or sets the Regular expression pattern (REGEX type validation).
|
|
2479
|
+
* @param {HTMLElement|String} field Gets or sets the element or element id (COMPARE type validation).
|
|
2480
|
+
* @param {componyx.UI.DataTypeOption} dataType Gets or sets the data type (DATATYPE type validation).
|
|
2481
|
+
* @param {componyx.UI.OnValidation} onValidation Gets or sets a function invoked when the field is validated (CUSTOM type validation).
|
|
2482
|
+
* @param {componyx.UI.OnBeforeValidation} onBeforeValidation Gets or sets a function invoked before the field is validated (AJAX type validation).
|
|
2483
|
+
* @param {componyx.UI.OnAfterValidation} onAfterValidation Gets or sets a function invoked after the field is validated (AJAX type validation).
|
|
2484
|
+
* @param {Object} data Gets or sets the data query for the AJAX type validation.
|
|
2485
|
+
*/
|
|
2486
|
+
componyx.UI.Validator.Rule = function ()
|
|
2487
|
+
{
|
|
2488
|
+
this.fieldName = null;
|
|
2489
|
+
this.type = null;
|
|
2490
|
+
this.feedbackId = null;
|
|
2491
|
+
this.autoValidate = null;
|
|
2492
|
+
this.getValue = null;
|
|
2493
|
+
this.live = null;
|
|
2494
|
+
this.msg = null;
|
|
2495
|
+
this.min = null;
|
|
2496
|
+
this.max = null;
|
|
2497
|
+
this.pattern = null;
|
|
2498
|
+
this.field = null;
|
|
2499
|
+
this.dataType = null;
|
|
2500
|
+
this.onValidation = null;
|
|
2501
|
+
this.onBeforeValidation = null;
|
|
2502
|
+
this.onAfterValidation = null;
|
|
2503
|
+
};
|
|
2504
|
+
})(window);
|
|
2505
|
+
|
|
2506
|
+
const Validator = componyx.UI.Validator;
|
|
2507
|
+
|
|
2508
|
+
export { Validator };
|