@northdata/fomantic-ui 2.8.722 → 2.9.401

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/lib/semantic-ui/dist/components/accordion.css +62 -69
  2. package/lib/semantic-ui/dist/components/accordion.js +595 -618
  3. package/lib/semantic-ui/dist/components/accordion.min.css +4 -4
  4. package/lib/semantic-ui/dist/components/accordion.min.js +6 -6
  5. package/lib/semantic-ui/dist/components/api.js +1225 -1177
  6. package/lib/semantic-ui/dist/components/api.min.js +6 -6
  7. package/lib/semantic-ui/dist/components/button.css +291 -634
  8. package/lib/semantic-ui/dist/components/button.min.css +4 -4
  9. package/lib/semantic-ui/dist/components/calendar.css +149 -33
  10. package/lib/semantic-ui/dist/components/calendar.js +2045 -1774
  11. package/lib/semantic-ui/dist/components/calendar.min.css +4 -4
  12. package/lib/semantic-ui/dist/components/calendar.min.js +6 -6
  13. package/lib/semantic-ui/dist/components/card.css +147 -537
  14. package/lib/semantic-ui/dist/components/card.min.css +4 -4
  15. package/lib/semantic-ui/dist/components/checkbox.css +215 -405
  16. package/lib/semantic-ui/dist/components/checkbox.js +888 -876
  17. package/lib/semantic-ui/dist/components/checkbox.min.css +4 -4
  18. package/lib/semantic-ui/dist/components/checkbox.min.js +6 -6
  19. package/lib/semantic-ui/dist/components/container.css +20 -21
  20. package/lib/semantic-ui/dist/components/container.min.css +4 -4
  21. package/lib/semantic-ui/dist/components/dimmer.css +76 -184
  22. package/lib/semantic-ui/dist/components/dimmer.js +732 -753
  23. package/lib/semantic-ui/dist/components/dimmer.min.css +4 -4
  24. package/lib/semantic-ui/dist/components/dimmer.min.js +6 -6
  25. package/lib/semantic-ui/dist/components/divider.css +81 -88
  26. package/lib/semantic-ui/dist/components/divider.min.css +4 -4
  27. package/lib/semantic-ui/dist/components/dropdown.css +431 -450
  28. package/lib/semantic-ui/dist/components/dropdown.js +4432 -4238
  29. package/lib/semantic-ui/dist/components/dropdown.min.css +4 -4
  30. package/lib/semantic-ui/dist/components/dropdown.min.js +6 -6
  31. package/lib/semantic-ui/dist/components/feed.css +130 -87
  32. package/lib/semantic-ui/dist/components/feed.min.css +4 -4
  33. package/lib/semantic-ui/dist/components/flag.css +541 -511
  34. package/lib/semantic-ui/dist/components/flag.min.css +4 -4
  35. package/lib/semantic-ui/dist/components/form.css +437 -476
  36. package/lib/semantic-ui/dist/components/form.js +2199 -2033
  37. package/lib/semantic-ui/dist/components/form.min.css +4 -4
  38. package/lib/semantic-ui/dist/components/form.min.js +6 -6
  39. package/lib/semantic-ui/dist/components/grid.css +151 -306
  40. package/lib/semantic-ui/dist/components/grid.min.css +4 -4
  41. package/lib/semantic-ui/dist/components/header.css +82 -99
  42. package/lib/semantic-ui/dist/components/header.min.css +4 -4
  43. package/lib/semantic-ui/dist/components/icon.css +2744 -2706
  44. package/lib/semantic-ui/dist/components/icon.min.css +4 -4
  45. package/lib/semantic-ui/dist/components/image.css +38 -73
  46. package/lib/semantic-ui/dist/components/image.min.css +4 -4
  47. package/lib/semantic-ui/dist/components/input.css +344 -254
  48. package/lib/semantic-ui/dist/components/input.min.css +4 -4
  49. package/lib/semantic-ui/dist/components/item.css +91 -139
  50. package/lib/semantic-ui/dist/components/item.min.css +4 -4
  51. package/lib/semantic-ui/dist/components/label.css +244 -267
  52. package/lib/semantic-ui/dist/components/label.min.css +4 -4
  53. package/lib/semantic-ui/dist/components/list.css +75 -113
  54. package/lib/semantic-ui/dist/components/list.min.css +4 -4
  55. package/lib/semantic-ui/dist/components/loader.css +142 -601
  56. package/lib/semantic-ui/dist/components/loader.min.css +4 -4
  57. package/lib/semantic-ui/dist/components/menu.css +398 -638
  58. package/lib/semantic-ui/dist/components/menu.min.css +9 -1
  59. package/lib/semantic-ui/dist/components/message.css +116 -332
  60. package/lib/semantic-ui/dist/components/message.min.css +4 -4
  61. package/lib/semantic-ui/dist/components/modal.css +149 -138
  62. package/lib/semantic-ui/dist/components/modal.js +1637 -1209
  63. package/lib/semantic-ui/dist/components/modal.min.css +4 -4
  64. package/lib/semantic-ui/dist/components/modal.min.js +6 -6
  65. package/lib/semantic-ui/dist/components/popup.css +284 -277
  66. package/lib/semantic-ui/dist/components/popup.js +1561 -1541
  67. package/lib/semantic-ui/dist/components/popup.min.css +4 -4
  68. package/lib/semantic-ui/dist/components/popup.min.js +6 -6
  69. package/lib/semantic-ui/dist/components/reset.css +12 -17
  70. package/lib/semantic-ui/dist/components/reset.min.css +4 -4
  71. package/lib/semantic-ui/dist/components/search.css +99 -137
  72. package/lib/semantic-ui/dist/components/search.js +1641 -1565
  73. package/lib/semantic-ui/dist/components/search.min.css +4 -4
  74. package/lib/semantic-ui/dist/components/search.min.js +6 -6
  75. package/lib/semantic-ui/dist/components/segment.css +149 -275
  76. package/lib/semantic-ui/dist/components/segment.min.css +4 -4
  77. package/lib/semantic-ui/dist/components/site.css +40 -48
  78. package/lib/semantic-ui/dist/components/site.js +455 -493
  79. package/lib/semantic-ui/dist/components/site.min.css +4 -4
  80. package/lib/semantic-ui/dist/components/site.min.js +6 -6
  81. package/lib/semantic-ui/dist/components/tab.css +16 -20
  82. package/lib/semantic-ui/dist/components/tab.js +967 -1001
  83. package/lib/semantic-ui/dist/components/tab.min.css +4 -4
  84. package/lib/semantic-ui/dist/components/tab.min.js +6 -6
  85. package/lib/semantic-ui/dist/components/table.css +465 -424
  86. package/lib/semantic-ui/dist/components/table.min.css +4 -4
  87. package/lib/semantic-ui/dist/components/transition.css +264 -1282
  88. package/lib/semantic-ui/dist/components/transition.js +1034 -1109
  89. package/lib/semantic-ui/dist/components/transition.min.css +4 -4
  90. package/lib/semantic-ui/dist/components/transition.min.js +6 -6
  91. package/lib/semantic-ui/dist/semantic-packed.css +5610 -6590
  92. package/lib/semantic-ui/dist/semantic.css +8547 -11697
  93. package/lib/semantic-ui/dist/semantic.full.css +14698 -11227
  94. package/lib/semantic-ui/dist/semantic.full.min.css +6 -262
  95. package/lib/semantic-ui/dist/semantic.js +19428 -19253
  96. package/lib/semantic-ui/dist/semantic.min.css +128 -128
  97. package/lib/semantic-ui/dist/semantic.min.js +6 -6
  98. package/lib/semantic-ui/dist/themes/basic/assets/fonts/LICENSE.txt +91 -0
  99. package/lib/semantic-ui/dist/themes/basic/assets/fonts/icons.woff2 +0 -0
  100. package/lib/semantic-ui/dist/themes/default/assets/fonts/LICENSE_icons.txt +165 -0
  101. package/lib/semantic-ui/dist/themes/default/assets/fonts/brand-icons.woff +0 -0
  102. package/lib/semantic-ui/dist/themes/default/assets/fonts/brand-icons.woff2 +0 -0
  103. package/lib/semantic-ui/dist/themes/default/assets/fonts/icons.woff +0 -0
  104. package/lib/semantic-ui/dist/themes/default/assets/fonts/icons.woff2 +0 -0
  105. package/lib/semantic-ui/dist/themes/default/assets/fonts/outline-icons.woff +0 -0
  106. package/lib/semantic-ui/dist/themes/default/assets/fonts/outline-icons.woff2 +0 -0
  107. package/lib/semantic-ui/types/fomantic-ui-accordion.d.ts +244 -0
  108. package/lib/semantic-ui/types/fomantic-ui-api.d.ts +561 -0
  109. package/lib/semantic-ui/types/fomantic-ui-calendar.d.ts +867 -0
  110. package/lib/semantic-ui/types/fomantic-ui-checkbox.d.ts +342 -0
  111. package/lib/semantic-ui/types/fomantic-ui-dimmer.d.ts +408 -0
  112. package/lib/semantic-ui/types/fomantic-ui-dropdown.d.ts +1221 -0
  113. package/lib/semantic-ui/types/fomantic-ui-embed.d.ts +343 -0
  114. package/lib/semantic-ui/types/fomantic-ui-flyout.d.ts +539 -0
  115. package/lib/semantic-ui/types/fomantic-ui-form.d.ts +759 -0
  116. package/lib/semantic-ui/types/fomantic-ui-modal.d.ts +567 -0
  117. package/lib/semantic-ui/types/fomantic-ui-nag.d.ts +270 -0
  118. package/lib/semantic-ui/types/fomantic-ui-popup.d.ts +549 -0
  119. package/lib/semantic-ui/types/fomantic-ui-progress.d.ts +465 -0
  120. package/lib/semantic-ui/types/fomantic-ui-rating.d.ts +187 -0
  121. package/lib/semantic-ui/types/fomantic-ui-search.d.ts +612 -0
  122. package/lib/semantic-ui/types/fomantic-ui-shape.d.ts +274 -0
  123. package/lib/semantic-ui/types/fomantic-ui-sidebar.d.ts +402 -0
  124. package/lib/semantic-ui/types/fomantic-ui-slider.d.ts +368 -0
  125. package/lib/semantic-ui/types/fomantic-ui-sticky.d.ts +215 -0
  126. package/lib/semantic-ui/types/fomantic-ui-tab.d.ts +361 -0
  127. package/lib/semantic-ui/types/fomantic-ui-toast.d.ts +526 -0
  128. package/lib/semantic-ui/types/fomantic-ui-transition.d.ts +389 -0
  129. package/lib/semantic-ui/types/fomantic-ui-visibility.d.ts +355 -0
  130. package/lib/semantic-ui/types/index.d.ts +63 -0
  131. package/package.json +19 -8
  132. package/lib/semantic-ui/dist/components/shape.css +0 -160
  133. package/lib/semantic-ui/dist/components/shape.js +0 -848
  134. package/lib/semantic-ui/dist/components/shape.min.css +0 -9
  135. package/lib/semantic-ui/dist/components/shape.min.js +0 -11
  136. package/lib/semantic-ui/dist/themes/basic/assets/fonts/icons.eot +0 -0
  137. package/lib/semantic-ui/dist/themes/basic/assets/fonts/icons.svg +0 -450
  138. package/lib/semantic-ui/dist/themes/basic/assets/fonts/icons.ttf +0 -0
  139. package/lib/semantic-ui/dist/themes/default/assets/fonts/brand-icons.eot +0 -0
  140. package/lib/semantic-ui/dist/themes/default/assets/fonts/brand-icons.svg +0 -3570
  141. package/lib/semantic-ui/dist/themes/default/assets/fonts/brand-icons.ttf +0 -0
  142. package/lib/semantic-ui/dist/themes/default/assets/fonts/icons.eot +0 -0
  143. package/lib/semantic-ui/dist/themes/default/assets/fonts/icons.svg +0 -4938
  144. package/lib/semantic-ui/dist/themes/default/assets/fonts/icons.ttf +0 -0
  145. package/lib/semantic-ui/dist/themes/default/assets/fonts/outline-icons.eot +0 -0
  146. package/lib/semantic-ui/dist/themes/default/assets/fonts/outline-icons.svg +0 -803
  147. package/lib/semantic-ui/dist/themes/default/assets/fonts/outline-icons.ttf +0 -0
  148. package/lib/semantic-ui/dist/themes/github/assets/fonts/octicons-local.ttf +0 -0
  149. package/lib/semantic-ui/dist/themes/github/assets/fonts/octicons.svg +0 -200
  150. package/lib/semantic-ui/dist/themes/github/assets/fonts/octicons.ttf +0 -0
  151. package/lib/semantic-ui/dist/themes/github/assets/fonts/octicons.woff +0 -0
  152. package/lib/semantic-ui/dist/themes/material/assets/fonts/icons.eot +0 -0
  153. package/lib/semantic-ui/dist/themes/material/assets/fonts/icons.svg +0 -2373
  154. package/lib/semantic-ui/dist/themes/material/assets/fonts/icons.ttf +0 -0
  155. package/lib/semantic-ui/dist/themes/material/assets/fonts/icons.woff +0 -0
  156. package/lib/semantic-ui/dist/themes/material/assets/fonts/icons.woff2 +0 -0
  157. /package/lib/semantic-ui/dist/themes/{default → famfamfam}/assets/images/flags.png +0 -0
@@ -1,2033 +1,2199 @@
1
- /*!
2
- * # Fomantic-UI - Form Validation
3
- * http://github.com/fomantic/Fomantic-UI/
4
- *
5
- *
6
- * Released under the MIT license
7
- * http://opensource.org/licenses/MIT
8
- *
9
- */
10
-
11
- ;(function ($, window, document, undefined) {
12
-
13
- 'use strict';
14
-
15
- $.isFunction = $.isFunction || function(obj) {
16
- return typeof obj === "function" && typeof obj.nodeType !== "number";
17
- };
18
-
19
- window = (typeof window != 'undefined' && window.Math == Math)
20
- ? window
21
- : (typeof self != 'undefined' && self.Math == Math)
22
- ? self
23
- : Function('return this')()
24
- ;
25
-
26
- $.fn.form = function(parameters) {
27
- var
28
- $allModules = $(this),
29
- moduleSelector = $allModules.selector || '',
30
-
31
- time = new Date().getTime(),
32
- performance = [],
33
-
34
- query = arguments[0],
35
- legacyParameters = arguments[1],
36
- methodInvoked = (typeof query == 'string'),
37
- queryArguments = [].slice.call(arguments, 1),
38
- returnedValue
39
- ;
40
- $allModules
41
- .each(function() {
42
- var
43
- $module = $(this),
44
- element = this,
45
-
46
- formErrors = [],
47
- keyHeldDown = false,
48
-
49
- // set at run-time
50
- $field,
51
- $group,
52
- $message,
53
- $prompt,
54
- $submit,
55
- $clear,
56
- $reset,
57
-
58
- settings,
59
- validation,
60
-
61
- metadata,
62
- selector,
63
- className,
64
- regExp,
65
- error,
66
-
67
- namespace,
68
- moduleNamespace,
69
- eventNamespace,
70
-
71
- submitting = false,
72
- dirty = false,
73
- history = ['clean', 'clean'],
74
-
75
- instance,
76
- module
77
- ;
78
-
79
- module = {
80
-
81
- initialize: function() {
82
-
83
- // settings grabbed at run time
84
- module.get.settings();
85
- if(methodInvoked) {
86
- if(instance === undefined) {
87
- module.instantiate();
88
- }
89
- module.invoke(query);
90
- }
91
- else {
92
- if(instance !== undefined) {
93
- instance.invoke('destroy');
94
- }
95
- module.verbose('Initializing form validation', $module, settings);
96
- module.bindEvents();
97
- module.set.defaults();
98
- if (settings.autoCheckRequired) {
99
- module.set.autoCheck();
100
- }
101
- module.instantiate();
102
- }
103
- },
104
-
105
- instantiate: function() {
106
- module.verbose('Storing instance of module', module);
107
- instance = module;
108
- $module
109
- .data(moduleNamespace, module)
110
- ;
111
- },
112
-
113
- destroy: function() {
114
- module.verbose('Destroying previous module', instance);
115
- module.removeEvents();
116
- $module
117
- .removeData(moduleNamespace)
118
- ;
119
- },
120
-
121
- refresh: function() {
122
- module.verbose('Refreshing selector cache');
123
- $field = $module.find(selector.field);
124
- $group = $module.find(selector.group);
125
- $message = $module.find(selector.message);
126
- $prompt = $module.find(selector.prompt);
127
-
128
- $submit = $module.find(selector.submit);
129
- $clear = $module.find(selector.clear);
130
- $reset = $module.find(selector.reset);
131
- },
132
-
133
- submit: function() {
134
- module.verbose('Submitting form', $module);
135
- submitting = true;
136
- $module.submit();
137
- },
138
-
139
- attachEvents: function(selector, action) {
140
- action = action || 'submit';
141
- $(selector).on('click' + eventNamespace, function(event) {
142
- module[action]();
143
- event.preventDefault();
144
- });
145
- },
146
-
147
- bindEvents: function() {
148
- module.verbose('Attaching form events');
149
- $module
150
- .on('submit' + eventNamespace, module.validate.form)
151
- .on('blur' + eventNamespace, selector.field, module.event.field.blur)
152
- .on('click' + eventNamespace, selector.submit, module.submit)
153
- .on('click' + eventNamespace, selector.reset, module.reset)
154
- .on('click' + eventNamespace, selector.clear, module.clear)
155
- ;
156
- if(settings.keyboardShortcuts) {
157
- $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
158
- }
159
- $field.each(function(index, el) {
160
- var
161
- $input = $(el),
162
- type = $input.prop('type'),
163
- inputEvent = module.get.changeEvent(type, $input)
164
- ;
165
- $input.on(inputEvent + eventNamespace, module.event.field.change);
166
- });
167
-
168
- // Dirty events
169
- if (settings.preventLeaving) {
170
- $(window).on('beforeunload' + eventNamespace, module.event.beforeUnload);
171
- }
172
-
173
- $field.on('change click keyup keydown blur', function(e) {
174
- $(this).triggerHandler(e.type + ".dirty");
175
- });
176
-
177
- $field.on('change.dirty click.dirty keyup.dirty keydown.dirty blur.dirty', module.determine.isDirty);
178
-
179
- $module.on('dirty' + eventNamespace, function(e) {
180
- settings.onDirty.call();
181
- });
182
-
183
- $module.on('clean' + eventNamespace, function(e) {
184
- settings.onClean.call();
185
- })
186
- },
187
-
188
- clear: function() {
189
- $field.each(function (index, el) {
190
- var
191
- $field = $(el),
192
- $element = $field.parent(),
193
- $fieldGroup = $field.closest($group),
194
- $prompt = $fieldGroup.find(selector.prompt),
195
- $calendar = $field.closest(selector.uiCalendar),
196
- defaultValue = $field.data(metadata.defaultValue) || '',
197
- isCheckbox = $element.is(selector.uiCheckbox),
198
- isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
199
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
200
- isErrored = $fieldGroup.hasClass(className.error)
201
- ;
202
- if(isErrored) {
203
- module.verbose('Resetting error on field', $fieldGroup);
204
- $fieldGroup.removeClass(className.error);
205
- $prompt.remove();
206
- }
207
- if(isDropdown) {
208
- module.verbose('Resetting dropdown value', $element, defaultValue);
209
- $element.dropdown('clear', true);
210
- }
211
- else if(isCheckbox) {
212
- $field.prop('checked', false);
213
- }
214
- else if (isCalendar) {
215
- $calendar.calendar('clear');
216
- }
217
- else {
218
- module.verbose('Resetting field value', $field, defaultValue);
219
- $field.val('');
220
- }
221
- });
222
- module.remove.states();
223
- },
224
-
225
- reset: function() {
226
- $field.each(function (index, el) {
227
- var
228
- $field = $(el),
229
- $element = $field.parent(),
230
- $fieldGroup = $field.closest($group),
231
- $calendar = $field.closest(selector.uiCalendar),
232
- $prompt = $fieldGroup.find(selector.prompt),
233
- defaultValue = $field.data(metadata.defaultValue),
234
- isCheckbox = $element.is(selector.uiCheckbox),
235
- isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
236
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
237
- isErrored = $fieldGroup.hasClass(className.error)
238
- ;
239
- if(defaultValue === undefined) {
240
- return;
241
- }
242
- if(isErrored) {
243
- module.verbose('Resetting error on field', $fieldGroup);
244
- $fieldGroup.removeClass(className.error);
245
- $prompt.remove();
246
- }
247
- if(isDropdown) {
248
- module.verbose('Resetting dropdown value', $element, defaultValue);
249
- $element.dropdown('restore defaults', true);
250
- }
251
- else if(isCheckbox) {
252
- module.verbose('Resetting checkbox value', $element, defaultValue);
253
- $field.prop('checked', defaultValue);
254
- }
255
- else if (isCalendar) {
256
- $calendar.calendar('set date', defaultValue);
257
- }
258
- else {
259
- module.verbose('Resetting field value', $field, defaultValue);
260
- $field.val(defaultValue);
261
- }
262
- });
263
- module.remove.states();
264
- },
265
-
266
- determine: {
267
- isValid: function() {
268
- var
269
- allValid = true
270
- ;
271
- $.each(validation, function(fieldName, field) {
272
- if( !( module.validate.field(field, fieldName, true) ) ) {
273
- allValid = false;
274
- }
275
- });
276
- return allValid;
277
- },
278
- isDirty: function(e) {
279
- var formIsDirty = false;
280
-
281
- $field.each(function(index, el) {
282
- var
283
- $el = $(el),
284
- isCheckbox = ($el.filter(selector.checkbox).length > 0),
285
- isDirty
286
- ;
287
-
288
- if (isCheckbox) {
289
- isDirty = module.is.checkboxDirty($el);
290
- } else {
291
- isDirty = module.is.fieldDirty($el);
292
- }
293
-
294
- $el.data(settings.metadata.isDirty, isDirty);
295
-
296
- formIsDirty |= isDirty;
297
- });
298
-
299
- if (formIsDirty) {
300
- module.set.dirty();
301
- } else {
302
- module.set.clean();
303
- }
304
-
305
- if (e && e.namespace === 'dirty') {
306
- e.stopImmediatePropagation();
307
- e.preventDefault();
308
- }
309
- }
310
- },
311
-
312
- is: {
313
- bracketedRule: function(rule) {
314
- return (rule.type && rule.type.match(settings.regExp.bracket));
315
- },
316
- shorthandFields: function(fields) {
317
- var
318
- fieldKeys = Object.keys(fields),
319
- firstRule = fields[fieldKeys[0]]
320
- ;
321
- return module.is.shorthandRules(firstRule);
322
- },
323
- // duck type rule test
324
- shorthandRules: function(rules) {
325
- return (typeof rules == 'string' || Array.isArray(rules));
326
- },
327
- empty: function($field) {
328
- if(!$field || $field.length === 0) {
329
- return true;
330
- }
331
- else if($field.is(selector.checkbox)) {
332
- return !$field.is(':checked');
333
- }
334
- else {
335
- return module.is.blank($field);
336
- }
337
- },
338
- blank: function($field) {
339
- return String($field.val()).trim() === '';
340
- },
341
- valid: function(field, showErrors) {
342
- var
343
- allValid = true
344
- ;
345
- if(field) {
346
- module.verbose('Checking if field is valid', field);
347
- return module.validate.field(validation[field], field, !!showErrors);
348
- }
349
- else {
350
- module.verbose('Checking if form is valid');
351
- $.each(validation, function(fieldName, field) {
352
- if( !module.is.valid(fieldName, showErrors) ) {
353
- allValid = false;
354
- }
355
- });
356
- return allValid;
357
- }
358
- },
359
- dirty: function() {
360
- return dirty;
361
- },
362
- clean: function() {
363
- return !dirty;
364
- },
365
- fieldDirty: function($el) {
366
- var initialValue = $el.data(metadata.defaultValue);
367
- // Explicitly check for null/undefined here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work
368
- if (initialValue == null) { initialValue = ''; }
369
- else if(Array.isArray(initialValue)) {
370
- initialValue = initialValue.toString();
371
- }
372
- var currentValue = $el.val();
373
- if (currentValue == null) { currentValue = ''; }
374
- // multiple select values are returned as arrays which are never equal, so do string conversion first
375
- else if(Array.isArray(currentValue)) {
376
- currentValue = currentValue.toString();
377
- }
378
- // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison
379
- var boolRegex = /^(true|false)$/i;
380
- var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
381
- if (isBoolValue) {
382
- var regex = new RegExp("^" + initialValue + "$", "i");
383
- return !regex.test(currentValue);
384
- }
385
-
386
- return currentValue !== initialValue;
387
- },
388
- checkboxDirty: function($el) {
389
- var initialValue = $el.data(metadata.defaultValue);
390
- var currentValue = $el.is(":checked");
391
-
392
- return initialValue !== currentValue;
393
- },
394
- justDirty: function() {
395
- return (history[0] === 'dirty');
396
- },
397
- justClean: function() {
398
- return (history[0] === 'clean');
399
- }
400
- },
401
-
402
- removeEvents: function() {
403
- $module.off(eventNamespace);
404
- $field.off(eventNamespace);
405
- $submit.off(eventNamespace);
406
- $field.off(eventNamespace);
407
- },
408
-
409
- event: {
410
- field: {
411
- keydown: function(event) {
412
- var
413
- $field = $(this),
414
- key = event.which,
415
- isInput = $field.is(selector.input),
416
- isCheckbox = $field.is(selector.checkbox),
417
- isInDropdown = ($field.closest(selector.uiDropdown).length > 0),
418
- keyCode = {
419
- enter : 13,
420
- escape : 27
421
- }
422
- ;
423
- if( key == keyCode.escape) {
424
- module.verbose('Escape key pressed blurring field');
425
- $field
426
- .blur()
427
- ;
428
- }
429
- if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
430
- if(!keyHeldDown) {
431
- $field.one('keyup' + eventNamespace, module.event.field.keyup);
432
- module.submit();
433
- module.debug('Enter pressed on input submitting form');
434
- }
435
- keyHeldDown = true;
436
- }
437
- },
438
- keyup: function() {
439
- keyHeldDown = false;
440
- },
441
- blur: function(event) {
442
- var
443
- $field = $(this),
444
- $fieldGroup = $field.closest($group),
445
- validationRules = module.get.validation($field)
446
- ;
447
- if( $fieldGroup.hasClass(className.error) ) {
448
- module.debug('Revalidating field', $field, validationRules);
449
- if(validationRules) {
450
- module.validate.field( validationRules );
451
- }
452
- }
453
- else if(settings.on == 'blur') {
454
- if(validationRules) {
455
- module.validate.field( validationRules );
456
- }
457
- }
458
- },
459
- change: function(event) {
460
- var
461
- $field = $(this),
462
- $fieldGroup = $field.closest($group),
463
- validationRules = module.get.validation($field)
464
- ;
465
- if(validationRules && (settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
466
- clearTimeout(module.timer);
467
- module.timer = setTimeout(function() {
468
- module.debug('Revalidating field', $field, module.get.validation($field));
469
- module.validate.field( validationRules );
470
- if(!settings.inline) {
471
- module.validate.form(false,true);
472
- }
473
- }, settings.delay);
474
- }
475
- }
476
- },
477
- beforeUnload: function(event) {
478
- if (module.is.dirty() && !submitting) {
479
- var event = event || window.event;
480
-
481
- // For modern browsers
482
- if (event) {
483
- event.returnValue = settings.text.leavingMessage;
484
- }
485
-
486
- // For olders...
487
- return settings.text.leavingMessage;
488
- }
489
- }
490
-
491
- },
492
-
493
- get: {
494
- ancillaryValue: function(rule) {
495
- if(!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
496
- return false;
497
- }
498
- return (rule.value !== undefined)
499
- ? rule.value
500
- : rule.type.match(settings.regExp.bracket)[1] + ''
501
- ;
502
- },
503
- ruleName: function(rule) {
504
- if( module.is.bracketedRule(rule) ) {
505
- return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
506
- }
507
- return rule.type;
508
- },
509
- changeEvent: function(type, $input) {
510
- if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
511
- return 'change';
512
- }
513
- else {
514
- return module.get.inputEvent();
515
- }
516
- },
517
- inputEvent: function() {
518
- return (document.createElement('input').oninput !== undefined)
519
- ? 'input'
520
- : (document.createElement('input').onpropertychange !== undefined)
521
- ? 'propertychange'
522
- : 'keyup'
523
- ;
524
- },
525
- fieldsFromShorthand: function(fields) {
526
- var
527
- fullFields = {}
528
- ;
529
- $.each(fields, function(name, rules) {
530
- if(typeof rules == 'string') {
531
- rules = [rules];
532
- }
533
- fullFields[name] = {
534
- rules: []
535
- };
536
- $.each(rules, function(index, rule) {
537
- fullFields[name].rules.push({ type: rule });
538
- });
539
- });
540
- return fullFields;
541
- },
542
- prompt: function(rule, field) {
543
- var
544
- ruleName = module.get.ruleName(rule),
545
- ancillary = module.get.ancillaryValue(rule),
546
- $field = module.get.field(field.identifier),
547
- value = $field.val(),
548
- prompt = $.isFunction(rule.prompt)
549
- ? rule.prompt(value)
550
- : rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
551
- requiresValue = (prompt.search('{value}') !== -1),
552
- requiresName = (prompt.search('{name}') !== -1),
553
- $label,
554
- name
555
- ;
556
- if(requiresValue) {
557
- prompt = prompt.replace(/\{value\}/g, $field.val());
558
- }
559
- if(requiresName) {
560
- $label = $field.closest(selector.group).find('label').eq(0);
561
- name = ($label.length == 1)
562
- ? $label.text()
563
- : $field.prop('placeholder') || settings.text.unspecifiedField
564
- ;
565
- prompt = prompt.replace(/\{name\}/g, name);
566
- }
567
- prompt = prompt.replace(/\{identifier\}/g, field.identifier);
568
- prompt = prompt.replace(/\{ruleValue\}/g, ancillary);
569
- if(!rule.prompt) {
570
- module.verbose('Using default validation prompt for type', prompt, ruleName);
571
- }
572
- return prompt;
573
- },
574
- settings: function() {
575
- if($.isPlainObject(parameters)) {
576
- var
577
- keys = Object.keys(parameters),
578
- isLegacySettings = (keys.length > 0)
579
- ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
580
- : false
581
- ;
582
- if(isLegacySettings) {
583
- // 1.x (ducktyped)
584
- settings = $.extend(true, {}, $.fn.form.settings, legacyParameters);
585
- validation = $.extend({}, $.fn.form.settings.defaults, parameters);
586
- module.error(settings.error.oldSyntax, element);
587
- module.verbose('Extending settings from legacy parameters', validation, settings);
588
- }
589
- else {
590
- // 2.x
591
- if(parameters.fields && module.is.shorthandFields(parameters.fields)) {
592
- parameters.fields = module.get.fieldsFromShorthand(parameters.fields);
593
- }
594
- settings = $.extend(true, {}, $.fn.form.settings, parameters);
595
- validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
596
- module.verbose('Extending settings', validation, settings);
597
- }
598
- }
599
- else {
600
- settings = $.fn.form.settings;
601
- validation = $.fn.form.settings.defaults;
602
- module.verbose('Using default form validation', validation, settings);
603
- }
604
-
605
- // shorthand
606
- namespace = settings.namespace;
607
- metadata = settings.metadata;
608
- selector = settings.selector;
609
- className = settings.className;
610
- regExp = settings.regExp;
611
- error = settings.error;
612
- moduleNamespace = 'module-' + namespace;
613
- eventNamespace = '.' + namespace;
614
-
615
- // grab instance
616
- instance = $module.data(moduleNamespace);
617
-
618
- // refresh selector cache
619
- module.refresh();
620
- },
621
- field: function(identifier) {
622
- module.verbose('Finding field with identifier', identifier);
623
- identifier = module.escape.string(identifier);
624
- var t;
625
- if((t=$field.filter('#' + identifier)).length > 0 ) {
626
- return t;
627
- }
628
- if((t=$field.filter('[name="' + identifier +'"]')).length > 0 ) {
629
- return t;
630
- }
631
- if((t=$field.filter('[name="' + identifier +'[]"]')).length > 0 ) {
632
- return t;
633
- }
634
- if((t=$field.filter('[data-' + metadata.validate + '="'+ identifier +'"]')).length > 0 ) {
635
- return t;
636
- }
637
- return $('<input/>');
638
- },
639
- fields: function(fields) {
640
- var
641
- $fields = $()
642
- ;
643
- $.each(fields, function(index, name) {
644
- $fields = $fields.add( module.get.field(name) );
645
- });
646
- return $fields;
647
- },
648
- validation: function($field) {
649
- var
650
- fieldValidation,
651
- identifier
652
- ;
653
- if(!validation) {
654
- return false;
655
- }
656
- $.each(validation, function(fieldName, field) {
657
- identifier = field.identifier || fieldName;
658
- $.each(module.get.field(identifier), function(index, groupField) {
659
- if(groupField == $field[0]) {
660
- field.identifier = identifier;
661
- fieldValidation = field;
662
- return false;
663
- }
664
- });
665
- });
666
- return fieldValidation || false;
667
- },
668
- value: function (field) {
669
- var
670
- fields = [],
671
- results
672
- ;
673
- fields.push(field);
674
- results = module.get.values.call(element, fields);
675
- return results[field];
676
- },
677
- values: function (fields) {
678
- var
679
- $fields = Array.isArray(fields)
680
- ? module.get.fields(fields)
681
- : $field,
682
- values = {}
683
- ;
684
- $fields.each(function(index, field) {
685
- var
686
- $field = $(field),
687
- $calendar = $field.closest(selector.uiCalendar),
688
- name = $field.prop('name'),
689
- value = $field.val(),
690
- isCheckbox = $field.is(selector.checkbox),
691
- isRadio = $field.is(selector.radio),
692
- isMultiple = (name.indexOf('[]') !== -1),
693
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
694
- isChecked = (isCheckbox)
695
- ? $field.is(':checked')
696
- : false
697
- ;
698
- if(name) {
699
- if(isMultiple) {
700
- name = name.replace('[]', '');
701
- if(!values[name]) {
702
- values[name] = [];
703
- }
704
- if(isCheckbox) {
705
- if(isChecked) {
706
- values[name].push(value || true);
707
- }
708
- else {
709
- values[name].push(false);
710
- }
711
- }
712
- else {
713
- values[name].push(value);
714
- }
715
- }
716
- else {
717
- if(isRadio) {
718
- if(values[name] === undefined || values[name] === false) {
719
- values[name] = (isChecked)
720
- ? value || true
721
- : false
722
- ;
723
- }
724
- }
725
- else if(isCheckbox) {
726
- if(isChecked) {
727
- values[name] = value || true;
728
- }
729
- else {
730
- values[name] = false;
731
- }
732
- }
733
- else if(isCalendar) {
734
- var date = $calendar.calendar('get date');
735
-
736
- if (date !== null) {
737
- if (settings.dateHandling == 'date') {
738
- values[name] = date;
739
- } else if(settings.dateHandling == 'input') {
740
- values[name] = $calendar.calendar('get input date')
741
- } else if (settings.dateHandling == 'formatter') {
742
- var type = $calendar.calendar('setting', 'type');
743
-
744
- switch(type) {
745
- case 'date':
746
- values[name] = settings.formatter.date(date);
747
- break;
748
-
749
- case 'datetime':
750
- values[name] = settings.formatter.datetime(date);
751
- break;
752
-
753
- case 'time':
754
- values[name] = settings.formatter.time(date);
755
- break;
756
-
757
- case 'month':
758
- values[name] = settings.formatter.month(date);
759
- break;
760
-
761
- case 'year':
762
- values[name] = settings.formatter.year(date);
763
- break;
764
-
765
- default:
766
- module.debug('Wrong calendar mode', $calendar, type);
767
- values[name] = '';
768
- }
769
- }
770
- } else {
771
- values[name] = '';
772
- }
773
- } else {
774
- values[name] = value;
775
- }
776
- }
777
- }
778
- });
779
- return values;
780
- },
781
- dirtyFields: function() {
782
- return $field.filter(function(index, e) {
783
- return $(e).data(metadata.isDirty);
784
- });
785
- }
786
- },
787
-
788
- has: {
789
-
790
- field: function(identifier) {
791
- module.verbose('Checking for existence of a field with identifier', identifier);
792
- identifier = module.escape.string(identifier);
793
- if(typeof identifier !== 'string') {
794
- module.error(error.identifier, identifier);
795
- }
796
- if($field.filter('#' + identifier).length > 0 ) {
797
- return true;
798
- }
799
- else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
800
- return true;
801
- }
802
- else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
803
- return true;
804
- }
805
- return false;
806
- }
807
-
808
- },
809
-
810
- can: {
811
- useElement: function(element){
812
- if ($.fn[element] !== undefined) {
813
- return true;
814
- }
815
- module.error(error.noElement.replace('{element}',element));
816
- return false;
817
- }
818
- },
819
-
820
- escape: {
821
- string: function(text) {
822
- text = String(text);
823
- return text.replace(regExp.escape, '\\$&');
824
- }
825
- },
826
-
827
- add: {
828
- // alias
829
- rule: function(name, rules) {
830
- module.add.field(name, rules);
831
- },
832
- field: function(name, rules) {
833
- // Validation should have at least a standard format
834
- if(validation[name] === undefined || validation[name].rules === undefined) {
835
- validation[name] = {
836
- rules: []
837
- };
838
- }
839
- var
840
- newValidation = {
841
- rules: []
842
- }
843
- ;
844
- if(module.is.shorthandRules(rules)) {
845
- rules = Array.isArray(rules)
846
- ? rules
847
- : [rules]
848
- ;
849
- $.each(rules, function(_index, rule) {
850
- newValidation.rules.push({ type: rule });
851
- });
852
- }
853
- else {
854
- newValidation.rules = rules.rules;
855
- }
856
- // For each new rule, check if there's not already one with the same type
857
- $.each(newValidation.rules, function (_index, rule) {
858
- if ($.grep(validation[name].rules, function(item){ return item.type == rule.type; }).length == 0) {
859
- validation[name].rules.push(rule);
860
- }
861
- });
862
- module.debug('Adding rules', newValidation.rules, validation);
863
- },
864
- fields: function(fields) {
865
- var
866
- newValidation
867
- ;
868
- if(fields && module.is.shorthandFields(fields)) {
869
- newValidation = module.get.fieldsFromShorthand(fields);
870
- }
871
- else {
872
- newValidation = fields;
873
- }
874
- validation = $.extend({}, validation, newValidation);
875
- },
876
- prompt: function(identifier, errors, internal) {
877
- var
878
- $field = module.get.field(identifier),
879
- $fieldGroup = $field.closest($group),
880
- $prompt = $fieldGroup.children(selector.prompt),
881
- promptExists = ($prompt.length !== 0)
882
- ;
883
- errors = (typeof errors == 'string')
884
- ? [errors]
885
- : errors
886
- ;
887
- module.verbose('Adding field error state', identifier);
888
- if(!internal) {
889
- $fieldGroup
890
- .addClass(className.error)
891
- ;
892
- }
893
- if(settings.inline) {
894
- if(!promptExists) {
895
- $prompt = settings.templates.prompt(errors, className.label);
896
- $prompt
897
- .appendTo($fieldGroup)
898
- ;
899
- }
900
- $prompt
901
- .html(errors[0])
902
- ;
903
- if(!promptExists) {
904
- if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
905
- module.verbose('Displaying error with css transition', settings.transition);
906
- $prompt.transition(settings.transition + ' in', settings.duration);
907
- }
908
- else {
909
- module.verbose('Displaying error with fallback javascript animation');
910
- $prompt
911
- .fadeIn(settings.duration)
912
- ;
913
- }
914
- }
915
- else {
916
- module.verbose('Inline errors are disabled, no inline error added', identifier);
917
- }
918
- }
919
- },
920
- errors: function(errors) {
921
- module.debug('Adding form error messages', errors);
922
- module.set.error();
923
- $message
924
- .html( settings.templates.error(errors) )
925
- ;
926
- }
927
- },
928
-
929
- remove: {
930
- errors: function() {
931
- module.debug('Removing form error messages');
932
- $message.empty();
933
- },
934
- states: function() {
935
- $module.removeClass(className.error).removeClass(className.success);
936
- if(!settings.inline) {
937
- module.remove.errors();
938
- }
939
- module.determine.isDirty();
940
- },
941
- rule: function(field, rule) {
942
- var
943
- rules = Array.isArray(rule)
944
- ? rule
945
- : [rule]
946
- ;
947
- if(validation[field] === undefined || !Array.isArray(validation[field].rules)) {
948
- return;
949
- }
950
- if(rule === undefined) {
951
- module.debug('Removed all rules');
952
- validation[field].rules = [];
953
- return;
954
- }
955
- $.each(validation[field].rules, function(index, rule) {
956
- if(rule && rules.indexOf(rule.type) !== -1) {
957
- module.debug('Removed rule', rule.type);
958
- validation[field].rules.splice(index, 1);
959
- }
960
- });
961
- },
962
- field: function(field) {
963
- var
964
- fields = Array.isArray(field)
965
- ? field
966
- : [field]
967
- ;
968
- $.each(fields, function(index, field) {
969
- module.remove.rule(field);
970
- });
971
- },
972
- // alias
973
- rules: function(field, rules) {
974
- if(Array.isArray(field)) {
975
- $.each(field, function(index, field) {
976
- module.remove.rule(field, rules);
977
- });
978
- }
979
- else {
980
- module.remove.rule(field, rules);
981
- }
982
- },
983
- fields: function(fields) {
984
- module.remove.field(fields);
985
- },
986
- prompt: function(identifier) {
987
- var
988
- $field = module.get.field(identifier),
989
- $fieldGroup = $field.closest($group),
990
- $prompt = $fieldGroup.children(selector.prompt)
991
- ;
992
- $fieldGroup
993
- .removeClass(className.error)
994
- ;
995
- if(settings.inline && $prompt.is(':visible')) {
996
- module.verbose('Removing prompt for field', identifier);
997
- if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
998
- $prompt.transition(settings.transition + ' out', settings.duration, function() {
999
- $prompt.remove();
1000
- });
1001
- }
1002
- else {
1003
- $prompt
1004
- .fadeOut(settings.duration, function(){
1005
- $prompt.remove();
1006
- })
1007
- ;
1008
- }
1009
- }
1010
- }
1011
- },
1012
-
1013
- set: {
1014
- success: function() {
1015
- $module
1016
- .removeClass(className.error)
1017
- .addClass(className.success)
1018
- ;
1019
- },
1020
- defaults: function () {
1021
- $field.each(function (index, el) {
1022
- var
1023
- $el = $(el),
1024
- $parent = $el.parent(),
1025
- isCheckbox = ($el.filter(selector.checkbox).length > 0),
1026
- isDropdown = $parent.is(selector.uiDropdown) && module.can.useElement('dropdown'),
1027
- $calendar = $el.closest(selector.uiCalendar),
1028
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
1029
- value = (isCheckbox)
1030
- ? $el.is(':checked')
1031
- : $el.val()
1032
- ;
1033
- if (isDropdown) {
1034
- $parent.dropdown('save defaults');
1035
- }
1036
- else if (isCalendar) {
1037
- $calendar.calendar('refresh');
1038
- }
1039
- $el.data(metadata.defaultValue, value);
1040
- $el.data(metadata.isDirty, false);
1041
- });
1042
- },
1043
- error: function() {
1044
- $module
1045
- .removeClass(className.success)
1046
- .addClass(className.error)
1047
- ;
1048
- },
1049
- value: function (field, value) {
1050
- var
1051
- fields = {}
1052
- ;
1053
- fields[field] = value;
1054
- return module.set.values.call(element, fields);
1055
- },
1056
- values: function (fields) {
1057
- if($.isEmptyObject(fields)) {
1058
- return;
1059
- }
1060
- $.each(fields, function(key, value) {
1061
- var
1062
- $field = module.get.field(key),
1063
- $element = $field.parent(),
1064
- $calendar = $field.closest(selector.uiCalendar),
1065
- isMultiple = Array.isArray(value),
1066
- isCheckbox = $element.is(selector.uiCheckbox) && module.can.useElement('checkbox'),
1067
- isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
1068
- isRadio = ($field.is(selector.radio) && isCheckbox),
1069
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
1070
- fieldExists = ($field.length > 0),
1071
- $multipleField
1072
- ;
1073
- if(fieldExists) {
1074
- if(isMultiple && isCheckbox) {
1075
- module.verbose('Selecting multiple', value, $field);
1076
- $element.checkbox('uncheck');
1077
- $.each(value, function(index, value) {
1078
- $multipleField = $field.filter('[value="' + value + '"]');
1079
- $element = $multipleField.parent();
1080
- if($multipleField.length > 0) {
1081
- $element.checkbox('check');
1082
- }
1083
- });
1084
- }
1085
- else if(isRadio) {
1086
- module.verbose('Selecting radio value', value, $field);
1087
- $field.filter('[value="' + value + '"]')
1088
- .parent(selector.uiCheckbox)
1089
- .checkbox('check')
1090
- ;
1091
- }
1092
- else if(isCheckbox) {
1093
- module.verbose('Setting checkbox value', value, $element);
1094
- if(value === true || value === 1) {
1095
- $element.checkbox('check');
1096
- }
1097
- else {
1098
- $element.checkbox('uncheck');
1099
- }
1100
- }
1101
- else if(isDropdown) {
1102
- module.verbose('Setting dropdown value', value, $element);
1103
- $element.dropdown('set selected', value);
1104
- }
1105
- else if (isCalendar) {
1106
- $calendar.calendar('set date',value);
1107
- }
1108
- else {
1109
- module.verbose('Setting field value', value, $field);
1110
- $field.val(value);
1111
- }
1112
- }
1113
- });
1114
- },
1115
- dirty: function() {
1116
- module.verbose('Setting state dirty');
1117
- dirty = true;
1118
- history[0] = history[1];
1119
- history[1] = 'dirty';
1120
-
1121
- if (module.is.justClean()) {
1122
- $module.trigger('dirty');
1123
- }
1124
- },
1125
- clean: function() {
1126
- module.verbose('Setting state clean');
1127
- dirty = false;
1128
- history[0] = history[1];
1129
- history[1] = 'clean';
1130
-
1131
- if (module.is.justDirty()) {
1132
- $module.trigger('clean');
1133
- }
1134
- },
1135
- asClean: function() {
1136
- module.set.defaults();
1137
- module.set.clean();
1138
- },
1139
- asDirty: function() {
1140
- module.set.defaults();
1141
- module.set.dirty();
1142
- },
1143
- autoCheck: function() {
1144
- module.debug('Enabling auto check on required fields');
1145
- $field.each(function (_index, el) {
1146
- var
1147
- $el = $(el),
1148
- $elGroup = $(el).closest($group),
1149
- isCheckbox = ($el.filter(selector.checkbox).length > 0),
1150
- isRequired = $el.prop('required') || $elGroup.hasClass(className.required) || $elGroup.parent().hasClass(className.required),
1151
- isDisabled = $el.is(':disabled') || $elGroup.hasClass(className.disabled) || $elGroup.parent().hasClass(className.disabled),
1152
- validation = module.get.validation($el),
1153
- hasEmptyRule = validation
1154
- ? $.grep(validation.rules, function(rule) { return rule.type == "empty" }) !== 0
1155
- : false,
1156
- identifier = validation.identifier || $el.attr('id') || $el.attr('name') || $el.data(metadata.validate)
1157
- ;
1158
- if (isRequired && !isDisabled && !hasEmptyRule && identifier !== undefined) {
1159
- if (isCheckbox) {
1160
- module.verbose("Adding 'checked' rule on field", identifier);
1161
- module.add.rule(identifier, "checked");
1162
- } else {
1163
- module.verbose("Adding 'empty' rule on field", identifier);
1164
- module.add.rule(identifier, "empty");
1165
- }
1166
- }
1167
- });
1168
- }
1169
- },
1170
-
1171
- validate: {
1172
-
1173
- form: function(event, ignoreCallbacks) {
1174
- var values = module.get.values();
1175
-
1176
- // input keydown event will fire submit repeatedly by browser default
1177
- if(keyHeldDown) {
1178
- return false;
1179
- }
1180
-
1181
- // reset errors
1182
- formErrors = [];
1183
- if( module.determine.isValid() ) {
1184
- module.debug('Form has no validation errors, submitting');
1185
- module.set.success();
1186
- if(!settings.inline) {
1187
- module.remove.errors();
1188
- }
1189
- if(ignoreCallbacks !== true) {
1190
- return settings.onSuccess.call(element, event, values);
1191
- }
1192
- }
1193
- else {
1194
- module.debug('Form has errors');
1195
- submitting = false;
1196
- module.set.error();
1197
- if(!settings.inline) {
1198
- module.add.errors(formErrors);
1199
- }
1200
- // prevent ajax submit
1201
- if(event && $module.data('moduleApi') !== undefined) {
1202
- event.stopImmediatePropagation();
1203
- }
1204
- if(ignoreCallbacks !== true) {
1205
- return settings.onFailure.call(element, formErrors, values);
1206
- }
1207
- }
1208
- },
1209
-
1210
- // takes a validation object and returns whether field passes validation
1211
- field: function(field, fieldName, showErrors) {
1212
- showErrors = (showErrors !== undefined)
1213
- ? showErrors
1214
- : true
1215
- ;
1216
- if(typeof field == 'string') {
1217
- module.verbose('Validating field', field);
1218
- fieldName = field;
1219
- field = validation[field];
1220
- }
1221
- var
1222
- identifier = field.identifier || fieldName,
1223
- $field = module.get.field(identifier),
1224
- $dependsField = (field.depends)
1225
- ? module.get.field(field.depends)
1226
- : false,
1227
- fieldValid = true,
1228
- fieldErrors = []
1229
- ;
1230
- if(!field.identifier) {
1231
- module.debug('Using field name as identifier', identifier);
1232
- field.identifier = identifier;
1233
- }
1234
- var isDisabled = !$field.filter(':not(:disabled)').length;
1235
- if(isDisabled) {
1236
- module.debug('Field is disabled. Skipping', identifier);
1237
- }
1238
- else if(field.optional && module.is.blank($field)){
1239
- module.debug('Field is optional and blank. Skipping', identifier);
1240
- }
1241
- else if(field.depends && module.is.empty($dependsField)) {
1242
- module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
1243
- }
1244
- else if(field.rules !== undefined) {
1245
- if(showErrors) {
1246
- $field.closest($group).removeClass(className.error);
1247
- }
1248
- $.each(field.rules, function(index, rule) {
1249
- if( module.has.field(identifier)) {
1250
- var invalidFields = module.validate.rule(field, rule,true) || [];
1251
- if (invalidFields.length>0){
1252
- module.debug('Field is invalid', identifier, rule.type);
1253
- fieldErrors.push(module.get.prompt(rule, field));
1254
- fieldValid = false;
1255
- if(showErrors){
1256
- $(invalidFields).closest($group).addClass(className.error);
1257
- }
1258
- }
1259
- }
1260
- });
1261
- }
1262
- if(fieldValid) {
1263
- if(showErrors) {
1264
- module.remove.prompt(identifier, fieldErrors);
1265
- settings.onValid.call($field);
1266
- }
1267
- }
1268
- else {
1269
- if(showErrors) {
1270
- formErrors = formErrors.concat(fieldErrors);
1271
- module.add.prompt(identifier, fieldErrors, true);
1272
- settings.onInvalid.call($field, fieldErrors);
1273
- }
1274
- return false;
1275
- }
1276
- return true;
1277
- },
1278
-
1279
- // takes validation rule and returns whether field passes rule
1280
- rule: function(field, rule, internal) {
1281
- var
1282
- $field = module.get.field(field.identifier),
1283
- ancillary = module.get.ancillaryValue(rule),
1284
- ruleName = module.get.ruleName(rule),
1285
- ruleFunction = settings.rules[ruleName],
1286
- invalidFields = [],
1287
- isCheckbox = $field.is(selector.checkbox),
1288
- isValid = function(field){
1289
- var value = (isCheckbox ? $(field).filter(':checked').val() : $(field).val());
1290
- // cast to string avoiding encoding special values
1291
- value = (value === undefined || value === '' || value === null)
1292
- ? ''
1293
- : (settings.shouldTrim) ? String(value + '').trim() : String(value + '')
1294
- ;
1295
- return ruleFunction.call(field, value, ancillary, $module);
1296
- }
1297
- ;
1298
- if( !$.isFunction(ruleFunction) ) {
1299
- module.error(error.noRule, ruleName);
1300
- return;
1301
- }
1302
- if(isCheckbox) {
1303
- if (!isValid($field)) {
1304
- invalidFields = $field;
1305
- }
1306
- } else {
1307
- $.each($field, function (index, field) {
1308
- if (!isValid(field)) {
1309
- invalidFields.push(field);
1310
- }
1311
- });
1312
- }
1313
- return internal ? invalidFields : !(invalidFields.length>0);
1314
- }
1315
- },
1316
-
1317
- setting: function(name, value) {
1318
- if( $.isPlainObject(name) ) {
1319
- $.extend(true, settings, name);
1320
- }
1321
- else if(value !== undefined) {
1322
- settings[name] = value;
1323
- }
1324
- else {
1325
- return settings[name];
1326
- }
1327
- },
1328
- internal: function(name, value) {
1329
- if( $.isPlainObject(name) ) {
1330
- $.extend(true, module, name);
1331
- }
1332
- else if(value !== undefined) {
1333
- module[name] = value;
1334
- }
1335
- else {
1336
- return module[name];
1337
- }
1338
- },
1339
- debug: function() {
1340
- if(!settings.silent && settings.debug) {
1341
- if(settings.performance) {
1342
- module.performance.log(arguments);
1343
- }
1344
- else {
1345
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
1346
- module.debug.apply(console, arguments);
1347
- }
1348
- }
1349
- },
1350
- verbose: function() {
1351
- if(!settings.silent && settings.verbose && settings.debug) {
1352
- if(settings.performance) {
1353
- module.performance.log(arguments);
1354
- }
1355
- else {
1356
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
1357
- module.verbose.apply(console, arguments);
1358
- }
1359
- }
1360
- },
1361
- error: function() {
1362
- if(!settings.silent) {
1363
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
1364
- module.error.apply(console, arguments);
1365
- }
1366
- },
1367
- performance: {
1368
- log: function(message) {
1369
- var
1370
- currentTime,
1371
- executionTime,
1372
- previousTime
1373
- ;
1374
- if(settings.performance) {
1375
- currentTime = new Date().getTime();
1376
- previousTime = time || currentTime;
1377
- executionTime = currentTime - previousTime;
1378
- time = currentTime;
1379
- performance.push({
1380
- 'Name' : message[0],
1381
- 'Arguments' : [].slice.call(message, 1) || '',
1382
- 'Element' : element,
1383
- 'Execution Time' : executionTime
1384
- });
1385
- }
1386
- clearTimeout(module.performance.timer);
1387
- module.performance.timer = setTimeout(module.performance.display, 500);
1388
- },
1389
- display: function() {
1390
- var
1391
- title = settings.name + ':',
1392
- totalTime = 0
1393
- ;
1394
- time = false;
1395
- clearTimeout(module.performance.timer);
1396
- $.each(performance, function(index, data) {
1397
- totalTime += data['Execution Time'];
1398
- });
1399
- title += ' ' + totalTime + 'ms';
1400
- if(moduleSelector) {
1401
- title += ' \'' + moduleSelector + '\'';
1402
- }
1403
- if($allModules.length > 1) {
1404
- title += ' ' + '(' + $allModules.length + ')';
1405
- }
1406
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
1407
- console.groupCollapsed(title);
1408
- if(console.table) {
1409
- console.table(performance);
1410
- }
1411
- else {
1412
- $.each(performance, function(index, data) {
1413
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
1414
- });
1415
- }
1416
- console.groupEnd();
1417
- }
1418
- performance = [];
1419
- }
1420
- },
1421
- invoke: function(query, passedArguments, context) {
1422
- var
1423
- object = instance,
1424
- maxDepth,
1425
- found,
1426
- response
1427
- ;
1428
- passedArguments = passedArguments || queryArguments;
1429
- context = element || context;
1430
- if(typeof query == 'string' && object !== undefined) {
1431
- query = query.split(/[\. ]/);
1432
- maxDepth = query.length - 1;
1433
- $.each(query, function(depth, value) {
1434
- var camelCaseValue = (depth != maxDepth)
1435
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1436
- : query
1437
- ;
1438
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
1439
- object = object[camelCaseValue];
1440
- }
1441
- else if( object[camelCaseValue] !== undefined ) {
1442
- found = object[camelCaseValue];
1443
- return false;
1444
- }
1445
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
1446
- object = object[value];
1447
- }
1448
- else if( object[value] !== undefined ) {
1449
- found = object[value];
1450
- return false;
1451
- }
1452
- else {
1453
- return false;
1454
- }
1455
- });
1456
- }
1457
- if( $.isFunction( found ) ) {
1458
- response = found.apply(context, passedArguments);
1459
- }
1460
- else if(found !== undefined) {
1461
- response = found;
1462
- }
1463
- if(Array.isArray(returnedValue)) {
1464
- returnedValue.push(response);
1465
- }
1466
- else if(returnedValue !== undefined) {
1467
- returnedValue = [returnedValue, response];
1468
- }
1469
- else if(response !== undefined) {
1470
- returnedValue = response;
1471
- }
1472
- return found;
1473
- }
1474
- };
1475
- module.initialize();
1476
- })
1477
- ;
1478
-
1479
- return (returnedValue !== undefined)
1480
- ? returnedValue
1481
- : this
1482
- ;
1483
- };
1484
-
1485
- $.fn.form.settings = {
1486
-
1487
- name : 'Form',
1488
- namespace : 'form',
1489
-
1490
- debug : false,
1491
- verbose : false,
1492
- performance : true,
1493
-
1494
- fields : false,
1495
-
1496
- keyboardShortcuts : true,
1497
- on : 'submit',
1498
- inline : false,
1499
-
1500
- delay : 200,
1501
- revalidate : true,
1502
- shouldTrim : true,
1503
-
1504
- transition : 'scale',
1505
- duration : 200,
1506
-
1507
- autoCheckRequired : false,
1508
- preventLeaving : false,
1509
- dateHandling : 'date', // 'date', 'input', 'formatter'
1510
-
1511
- onValid : function() {},
1512
- onInvalid : function() {},
1513
- onSuccess : function() { return true; },
1514
- onFailure : function() { return false; },
1515
- onDirty : function() {},
1516
- onClean : function() {},
1517
-
1518
- metadata : {
1519
- defaultValue : 'default',
1520
- validate : 'validate',
1521
- isDirty : 'isDirty'
1522
- },
1523
-
1524
- regExp: {
1525
- htmlID : /^[a-zA-Z][\w:.-]*$/g,
1526
- bracket : /\[(.*)\]/i,
1527
- decimal : /^\d+\.?\d*$/,
1528
- email : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
1529
- escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|:,=@]/g,
1530
- flags : /^\/(.*)\/(.*)?/,
1531
- integer : /^\-?\d+$/,
1532
- number : /^\-?\d*(\.\d+)?$/,
1533
- url : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
1534
- },
1535
-
1536
- text: {
1537
- unspecifiedRule : 'Please enter a valid value',
1538
- unspecifiedField : 'This field',
1539
- leavingMessage : 'There are unsaved changes on this page which will be discarded if you continue.'
1540
- },
1541
-
1542
- prompt: {
1543
- empty : '{name} must have a value',
1544
- checked : '{name} must be checked',
1545
- email : '{name} must be a valid e-mail',
1546
- url : '{name} must be a valid url',
1547
- regExp : '{name} is not formatted correctly',
1548
- integer : '{name} must be an integer',
1549
- decimal : '{name} must be a decimal number',
1550
- number : '{name} must be set to a number',
1551
- is : '{name} must be "{ruleValue}"',
1552
- isExactly : '{name} must be exactly "{ruleValue}"',
1553
- not : '{name} cannot be set to "{ruleValue}"',
1554
- notExactly : '{name} cannot be set to exactly "{ruleValue}"',
1555
- contain : '{name} must contain "{ruleValue}"',
1556
- containExactly : '{name} must contain exactly "{ruleValue}"',
1557
- doesntContain : '{name} cannot contain "{ruleValue}"',
1558
- doesntContainExactly : '{name} cannot contain exactly "{ruleValue}"',
1559
- minLength : '{name} must be at least {ruleValue} characters',
1560
- length : '{name} must be at least {ruleValue} characters',
1561
- exactLength : '{name} must be exactly {ruleValue} characters',
1562
- maxLength : '{name} cannot be longer than {ruleValue} characters',
1563
- match : '{name} must match {ruleValue} field',
1564
- different : '{name} must have a different value than {ruleValue} field',
1565
- creditCard : '{name} must be a valid credit card number',
1566
- minCount : '{name} must have at least {ruleValue} choices',
1567
- exactCount : '{name} must have exactly {ruleValue} choices',
1568
- maxCount : '{name} must have {ruleValue} or less choices'
1569
- },
1570
-
1571
- selector : {
1572
- checkbox : 'input[type="checkbox"], input[type="radio"]',
1573
- clear : '.clear',
1574
- field : 'input:not(.search), textarea, select',
1575
- group : '.field',
1576
- input : 'input',
1577
- message : '.error.message',
1578
- prompt : '.prompt.label',
1579
- radio : 'input[type="radio"]',
1580
- reset : '.reset:not([type="reset"])',
1581
- submit : '.submit:not([type="submit"])',
1582
- uiCheckbox : '.ui.checkbox',
1583
- uiDropdown : '.ui.dropdown',
1584
- uiCalendar : '.ui.calendar'
1585
- },
1586
-
1587
- className : {
1588
- error : 'error',
1589
- label : 'ui basic red pointing prompt label',
1590
- pressed : 'down',
1591
- success : 'success',
1592
- required : 'required',
1593
- disabled : 'disabled'
1594
- },
1595
-
1596
- error: {
1597
- identifier : 'You must specify a string identifier for each field',
1598
- method : 'The method you called is not defined.',
1599
- noRule : 'There is no rule matching the one you specified',
1600
- oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
1601
- noElement : 'This module requires ui {element}'
1602
- },
1603
-
1604
- templates: {
1605
-
1606
- // template that produces error message
1607
- error: function(errors) {
1608
- var
1609
- html = '<ul class="list">'
1610
- ;
1611
- $.each(errors, function(index, value) {
1612
- html += '<li>' + value + '</li>';
1613
- });
1614
- html += '</ul>';
1615
- return $(html);
1616
- },
1617
-
1618
- // template that produces label
1619
- prompt: function(errors, labelClasses) {
1620
- return $('<div/>')
1621
- .addClass(labelClasses)
1622
- .html(errors[0])
1623
- ;
1624
- }
1625
- },
1626
-
1627
- formatter: {
1628
- date: function(date) {
1629
- return Intl.DateTimeFormat('en-GB').format(date);
1630
- },
1631
- datetime: function(date) {
1632
- return Intl.DateTimeFormat('en-GB', {
1633
- year: "numeric",
1634
- month: "2-digit",
1635
- day: "2-digit",
1636
- hour: '2-digit',
1637
- minute: '2-digit',
1638
- second: '2-digit'
1639
- }).format(date);
1640
- },
1641
- time: function(date) {
1642
- return Intl.DateTimeFormat('en-GB', {
1643
- hour: '2-digit',
1644
- minute: '2-digit',
1645
- second: '2-digit'
1646
- }).format(date);
1647
- },
1648
- month: function(date) {
1649
- return Intl.DateTimeFormat('en-GB', {
1650
- month: '2-digit',
1651
- year: 'numeric'
1652
- }).format(date);
1653
- },
1654
- year: function(date) {
1655
- return Intl.DateTimeFormat('en-GB', {
1656
- year: 'numeric'
1657
- }).format(date);
1658
- }
1659
- },
1660
-
1661
- rules: {
1662
-
1663
- // is not empty or blank string
1664
- empty: function(value) {
1665
- return !(value === undefined || '' === value || Array.isArray(value) && value.length === 0);
1666
- },
1667
-
1668
- // checkbox checked
1669
- checked: function() {
1670
- return ($(this).filter(':checked').length > 0);
1671
- },
1672
-
1673
- // is most likely an email
1674
- email: function(value){
1675
- return $.fn.form.settings.regExp.email.test(value);
1676
- },
1677
-
1678
- // value is most likely url
1679
- url: function(value) {
1680
- return $.fn.form.settings.regExp.url.test(value);
1681
- },
1682
-
1683
- // matches specified regExp
1684
- regExp: function(value, regExp) {
1685
- if(regExp instanceof RegExp) {
1686
- return value.match(regExp);
1687
- }
1688
- var
1689
- regExpParts = regExp.match($.fn.form.settings.regExp.flags),
1690
- flags
1691
- ;
1692
- // regular expression specified as /baz/gi (flags)
1693
- if(regExpParts) {
1694
- regExp = (regExpParts.length >= 2)
1695
- ? regExpParts[1]
1696
- : regExp
1697
- ;
1698
- flags = (regExpParts.length >= 3)
1699
- ? regExpParts[2]
1700
- : ''
1701
- ;
1702
- }
1703
- return value.match( new RegExp(regExp, flags) );
1704
- },
1705
-
1706
- // is valid integer or matches range
1707
- integer: function(value, range) {
1708
- var
1709
- intRegExp = $.fn.form.settings.regExp.integer,
1710
- min,
1711
- max,
1712
- parts
1713
- ;
1714
- if( !range || ['', '..'].indexOf(range) !== -1) {
1715
- // do nothing
1716
- }
1717
- else if(range.indexOf('..') == -1) {
1718
- if(intRegExp.test(range)) {
1719
- min = max = range - 0;
1720
- }
1721
- }
1722
- else {
1723
- parts = range.split('..', 2);
1724
- if(intRegExp.test(parts[0])) {
1725
- min = parts[0] - 0;
1726
- }
1727
- if(intRegExp.test(parts[1])) {
1728
- max = parts[1] - 0;
1729
- }
1730
- }
1731
- return (
1732
- intRegExp.test(value) &&
1733
- (min === undefined || value >= min) &&
1734
- (max === undefined || value <= max)
1735
- );
1736
- },
1737
-
1738
- // is valid number (with decimal)
1739
- decimal: function(value) {
1740
- return $.fn.form.settings.regExp.decimal.test(value);
1741
- },
1742
-
1743
- // is valid number
1744
- number: function(value) {
1745
- return $.fn.form.settings.regExp.number.test(value);
1746
- },
1747
-
1748
- // is value (case insensitive)
1749
- is: function(value, text) {
1750
- text = (typeof text == 'string')
1751
- ? text.toLowerCase()
1752
- : text
1753
- ;
1754
- value = (typeof value == 'string')
1755
- ? value.toLowerCase()
1756
- : value
1757
- ;
1758
- return (value == text);
1759
- },
1760
-
1761
- // is value
1762
- isExactly: function(value, text) {
1763
- return (value == text);
1764
- },
1765
-
1766
- // value is not another value (case insensitive)
1767
- not: function(value, notValue) {
1768
- value = (typeof value == 'string')
1769
- ? value.toLowerCase()
1770
- : value
1771
- ;
1772
- notValue = (typeof notValue == 'string')
1773
- ? notValue.toLowerCase()
1774
- : notValue
1775
- ;
1776
- return (value != notValue);
1777
- },
1778
-
1779
- // value is not another value (case sensitive)
1780
- notExactly: function(value, notValue) {
1781
- return (value != notValue);
1782
- },
1783
-
1784
- // value contains text (insensitive)
1785
- contains: function(value, text) {
1786
- // escape regex characters
1787
- text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
1788
- return (value.search( new RegExp(text, 'i') ) !== -1);
1789
- },
1790
-
1791
- // value contains text (case sensitive)
1792
- containsExactly: function(value, text) {
1793
- // escape regex characters
1794
- text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
1795
- return (value.search( new RegExp(text) ) !== -1);
1796
- },
1797
-
1798
- // value contains text (insensitive)
1799
- doesntContain: function(value, text) {
1800
- // escape regex characters
1801
- text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
1802
- return (value.search( new RegExp(text, 'i') ) === -1);
1803
- },
1804
-
1805
- // value contains text (case sensitive)
1806
- doesntContainExactly: function(value, text) {
1807
- // escape regex characters
1808
- text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
1809
- return (value.search( new RegExp(text) ) === -1);
1810
- },
1811
-
1812
- // is at least string length
1813
- minLength: function(value, requiredLength) {
1814
- return (value !== undefined)
1815
- ? (value.length >= requiredLength)
1816
- : false
1817
- ;
1818
- },
1819
-
1820
- // see rls notes for 2.0.6 (this is a duplicate of minLength)
1821
- length: function(value, requiredLength) {
1822
- return (value !== undefined)
1823
- ? (value.length >= requiredLength)
1824
- : false
1825
- ;
1826
- },
1827
-
1828
- // is exactly length
1829
- exactLength: function(value, requiredLength) {
1830
- return (value !== undefined)
1831
- ? (value.length == requiredLength)
1832
- : false
1833
- ;
1834
- },
1835
-
1836
- // is less than length
1837
- maxLength: function(value, maxLength) {
1838
- return (value !== undefined)
1839
- ? (value.length <= maxLength)
1840
- : false
1841
- ;
1842
- },
1843
-
1844
- // matches another field
1845
- match: function(value, identifier, $module) {
1846
- var
1847
- matchingValue,
1848
- matchingElement
1849
- ;
1850
- if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
1851
- matchingValue = matchingElement.val();
1852
- }
1853
- else if((matchingElement = $module.find('#' + identifier)).length > 0) {
1854
- matchingValue = matchingElement.val();
1855
- }
1856
- else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
1857
- matchingValue = matchingElement.val();
1858
- }
1859
- else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
1860
- matchingValue = matchingElement;
1861
- }
1862
- return (matchingValue !== undefined)
1863
- ? ( value.toString() == matchingValue.toString() )
1864
- : false
1865
- ;
1866
- },
1867
-
1868
- // different than another field
1869
- different: function(value, identifier, $module) {
1870
- // use either id or name of field
1871
- var
1872
- matchingValue,
1873
- matchingElement
1874
- ;
1875
- if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
1876
- matchingValue = matchingElement.val();
1877
- }
1878
- else if((matchingElement = $module.find('#' + identifier)).length > 0) {
1879
- matchingValue = matchingElement.val();
1880
- }
1881
- else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
1882
- matchingValue = matchingElement.val();
1883
- }
1884
- else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
1885
- matchingValue = matchingElement;
1886
- }
1887
- return (matchingValue !== undefined)
1888
- ? ( value.toString() !== matchingValue.toString() )
1889
- : false
1890
- ;
1891
- },
1892
-
1893
- creditCard: function(cardNumber, cardTypes) {
1894
- var
1895
- cards = {
1896
- visa: {
1897
- pattern : /^4/,
1898
- length : [16]
1899
- },
1900
- amex: {
1901
- pattern : /^3[47]/,
1902
- length : [15]
1903
- },
1904
- mastercard: {
1905
- pattern : /^5[1-5]/,
1906
- length : [16]
1907
- },
1908
- discover: {
1909
- pattern : /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,
1910
- length : [16]
1911
- },
1912
- unionPay: {
1913
- pattern : /^(62|88)/,
1914
- length : [16, 17, 18, 19]
1915
- },
1916
- jcb: {
1917
- pattern : /^35(2[89]|[3-8][0-9])/,
1918
- length : [16]
1919
- },
1920
- maestro: {
1921
- pattern : /^(5018|5020|5038|6304|6759|676[1-3])/,
1922
- length : [12, 13, 14, 15, 16, 17, 18, 19]
1923
- },
1924
- dinersClub: {
1925
- pattern : /^(30[0-5]|^36)/,
1926
- length : [14]
1927
- },
1928
- laser: {
1929
- pattern : /^(6304|670[69]|6771)/,
1930
- length : [16, 17, 18, 19]
1931
- },
1932
- visaElectron: {
1933
- pattern : /^(4026|417500|4508|4844|491(3|7))/,
1934
- length : [16]
1935
- }
1936
- },
1937
- valid = {},
1938
- validCard = false,
1939
- requiredTypes = (typeof cardTypes == 'string')
1940
- ? cardTypes.split(',')
1941
- : false,
1942
- unionPay,
1943
- validation
1944
- ;
1945
-
1946
- if(typeof cardNumber !== 'string' || cardNumber.length === 0) {
1947
- return;
1948
- }
1949
-
1950
- // allow dashes in card
1951
- cardNumber = cardNumber.replace(/[\-]/g, '');
1952
-
1953
- // verify card types
1954
- if(requiredTypes) {
1955
- $.each(requiredTypes, function(index, type){
1956
- // verify each card type
1957
- validation = cards[type];
1958
- if(validation) {
1959
- valid = {
1960
- length : ($.inArray(cardNumber.length, validation.length) !== -1),
1961
- pattern : (cardNumber.search(validation.pattern) !== -1)
1962
- };
1963
- if(valid.length && valid.pattern) {
1964
- validCard = true;
1965
- }
1966
- }
1967
- });
1968
-
1969
- if(!validCard) {
1970
- return false;
1971
- }
1972
- }
1973
-
1974
- // skip luhn for UnionPay
1975
- unionPay = {
1976
- number : ($.inArray(cardNumber.length, cards.unionPay.length) !== -1),
1977
- pattern : (cardNumber.search(cards.unionPay.pattern) !== -1)
1978
- };
1979
- if(unionPay.number && unionPay.pattern) {
1980
- return true;
1981
- }
1982
-
1983
- // verify luhn, adapted from <https://gist.github.com/2134376>
1984
- var
1985
- length = cardNumber.length,
1986
- multiple = 0,
1987
- producedValue = [
1988
- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
1989
- [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
1990
- ],
1991
- sum = 0
1992
- ;
1993
- while (length--) {
1994
- sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
1995
- multiple ^= 1;
1996
- }
1997
- return (sum % 10 === 0 && sum > 0);
1998
- },
1999
-
2000
- minCount: function(value, minCount) {
2001
- if(minCount == 0) {
2002
- return true;
2003
- }
2004
- if(minCount == 1) {
2005
- return (value !== '');
2006
- }
2007
- return (value.split(',').length >= minCount);
2008
- },
2009
-
2010
- exactCount: function(value, exactCount) {
2011
- if(exactCount == 0) {
2012
- return (value === '');
2013
- }
2014
- if(exactCount == 1) {
2015
- return (value !== '' && value.search(',') === -1);
2016
- }
2017
- return (value.split(',').length == exactCount);
2018
- },
2019
-
2020
- maxCount: function(value, maxCount) {
2021
- if(maxCount == 0) {
2022
- return false;
2023
- }
2024
- if(maxCount == 1) {
2025
- return (value.search(',') === -1);
2026
- }
2027
- return (value.split(',').length <= maxCount);
2028
- }
2029
- }
2030
-
2031
- };
2032
-
2033
- })( jQuery, window, document );
1
+ /*!
2
+ * # Fomantic-UI 2.9.4 - Form Validation
3
+ * https://github.com/fomantic/Fomantic-UI/
4
+ *
5
+ *
6
+ * Released under the MIT license
7
+ * https://opensource.org/licenses/MIT
8
+ *
9
+ */
10
+
11
+ (function ($, window, document) {
12
+ 'use strict';
13
+
14
+ function isFunction(obj) {
15
+ return typeof obj === 'function' && typeof obj.nodeType !== 'number';
16
+ }
17
+
18
+ window = window !== undefined && window.Math === Math
19
+ ? window
20
+ : globalThis;
21
+
22
+ $.fn.form = function (parameters) {
23
+ var
24
+ $allModules = $(this),
25
+ $window = $(window),
26
+
27
+ time = Date.now(),
28
+ performance = [],
29
+
30
+ query = arguments[0],
31
+ methodInvoked = typeof query === 'string',
32
+ queryArguments = [].slice.call(arguments, 1),
33
+ returnedValue
34
+ ;
35
+ $allModules.each(function () {
36
+ var
37
+ $module = $(this),
38
+ element = this,
39
+
40
+ formErrors = [],
41
+ formErrorsTracker = {},
42
+ keyHeldDown = false,
43
+
44
+ // set at run-time
45
+ $field,
46
+ $group,
47
+ $message,
48
+ $prompt,
49
+ $submit,
50
+ $clear,
51
+ $reset,
52
+
53
+ settings,
54
+ validation,
55
+
56
+ metadata,
57
+ selector,
58
+ className,
59
+ regExp,
60
+ error,
61
+
62
+ namespace,
63
+ moduleNamespace,
64
+ eventNamespace,
65
+ attachEventsSelector,
66
+ attachEventsAction,
67
+
68
+ submitting = false,
69
+ dirty = false,
70
+ history = ['clean', 'clean'],
71
+
72
+ instance,
73
+ module
74
+ ;
75
+
76
+ module = {
77
+
78
+ initialize: function () {
79
+ // settings grabbed at run time
80
+ module.get.settings();
81
+ $module.addClass(className.initial);
82
+ if (methodInvoked) {
83
+ if (instance === undefined) {
84
+ module.instantiate();
85
+ }
86
+ module.invoke(query);
87
+ } else {
88
+ if (instance !== undefined) {
89
+ instance.invoke('destroy');
90
+ module.refresh();
91
+ }
92
+ module.verbose('Initializing form validation', $module, settings);
93
+ module.bindEvents();
94
+ module.set.defaults();
95
+ if (settings.autoCheckRequired) {
96
+ module.set.autoCheck();
97
+ }
98
+ module.instantiate();
99
+ }
100
+ },
101
+
102
+ instantiate: function () {
103
+ module.verbose('Storing instance of module', module);
104
+ instance = module;
105
+ $module
106
+ .data(moduleNamespace, module)
107
+ ;
108
+ },
109
+
110
+ destroy: function () {
111
+ module.verbose('Destroying previous module', instance);
112
+ module.removeEvents();
113
+ $module
114
+ .removeData(moduleNamespace)
115
+ ;
116
+ },
117
+
118
+ refresh: function () {
119
+ module.verbose('Refreshing selector cache');
120
+ $field = $module.find(selector.field);
121
+ $group = $module.find(selector.group);
122
+ $message = $module.find(selector.message);
123
+ $prompt = $module.find(selector.prompt);
124
+
125
+ $submit = $module.find(selector.submit);
126
+ $clear = $module.find(selector.clear);
127
+ $reset = $module.find(selector.reset);
128
+ },
129
+
130
+ refreshEvents: function () {
131
+ module.removeEvents();
132
+ module.bindEvents();
133
+ },
134
+
135
+ submit: function (event) {
136
+ module.verbose('Submitting form', $module);
137
+ submitting = true;
138
+ $module.trigger('submit');
139
+ if (event) {
140
+ event.preventDefault();
141
+ }
142
+ },
143
+
144
+ attachEvents: function (selector, action) {
145
+ if (!action) {
146
+ action = 'submit';
147
+ }
148
+
149
+ $(selector).on('click' + eventNamespace, function (event) {
150
+ module[action]();
151
+ event.preventDefault();
152
+ });
153
+
154
+ attachEventsSelector = selector;
155
+ attachEventsAction = action;
156
+ },
157
+
158
+ bindEvents: function () {
159
+ module.verbose('Attaching form events');
160
+ $module
161
+ .on('submit' + eventNamespace, module.validate.form)
162
+ .on('blur' + eventNamespace, selector.field, module.event.field.blur)
163
+ .on('click' + eventNamespace, selector.submit, module.submit)
164
+ .on('click' + eventNamespace, selector.reset, module.reset)
165
+ .on('click' + eventNamespace, selector.clear, module.clear)
166
+ ;
167
+ $field.on('invalid' + eventNamespace, module.event.field.invalid);
168
+ if (settings.keyboardShortcuts) {
169
+ $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
170
+ }
171
+ $field.each(function (index, el) {
172
+ var
173
+ $input = $(el),
174
+ type = $input.prop('type'),
175
+ inputEvent = module.get.changeEvent(type, $input)
176
+ ;
177
+ $input.on(inputEvent + eventNamespace, module.event.field.change);
178
+ });
179
+
180
+ // Dirty events
181
+ if (settings.preventLeaving) {
182
+ $window.on('beforeunload' + eventNamespace, module.event.beforeUnload);
183
+ }
184
+
185
+ $field.on('change' + eventNamespace
186
+ + ' click' + eventNamespace
187
+ + ' keyup' + eventNamespace
188
+ + ' keydown' + eventNamespace
189
+ + ' blur' + eventNamespace, function (e) {
190
+ module.determine.isDirty();
191
+ });
192
+
193
+ $module.on('dirty' + eventNamespace, function (e) {
194
+ settings.onDirty.call();
195
+ });
196
+
197
+ $module.on('clean' + eventNamespace, function (e) {
198
+ settings.onClean.call();
199
+ });
200
+ if (attachEventsSelector) {
201
+ module.attachEvents(attachEventsSelector, attachEventsAction);
202
+ }
203
+ },
204
+
205
+ clear: function () {
206
+ $field.each(function (index, el) {
207
+ var
208
+ $field = $(el),
209
+ $element = $field.parent(),
210
+ $fieldGroup = $field.closest($group),
211
+ $prompt = $fieldGroup.find(selector.prompt),
212
+ $calendar = $field.closest(selector.uiCalendar),
213
+ defaultValue = $field.data(metadata.defaultValue) || '',
214
+ isCheckbox = $field.is(selector.checkbox),
215
+ isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
216
+ isCalendar = $calendar.length > 0 && module.can.useElement('calendar'),
217
+ isErrored = $fieldGroup.hasClass(className.error)
218
+ ;
219
+ if (isErrored) {
220
+ module.verbose('Resetting error on field', $fieldGroup);
221
+ $fieldGroup.removeClass(className.error);
222
+ $prompt.remove();
223
+ }
224
+ if (isDropdown) {
225
+ module.verbose('Resetting dropdown value', $element, defaultValue);
226
+ $element.dropdown('clear', true);
227
+ } else if (isCheckbox) {
228
+ $field.prop('checked', false);
229
+ } else if (isCalendar) {
230
+ $calendar.calendar('clear');
231
+ } else {
232
+ module.verbose('Resetting field value', $field, defaultValue);
233
+ $field.val('');
234
+ }
235
+ });
236
+ module.remove.states();
237
+ },
238
+
239
+ reset: function () {
240
+ $field.each(function (index, el) {
241
+ var
242
+ $field = $(el),
243
+ $element = $field.parent(),
244
+ $fieldGroup = $field.closest($group),
245
+ $calendar = $field.closest(selector.uiCalendar),
246
+ $prompt = $fieldGroup.find(selector.prompt),
247
+ defaultValue = $field.data(metadata.defaultValue),
248
+ isCheckbox = $field.is(selector.checkbox),
249
+ isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
250
+ isCalendar = $calendar.length > 0 && module.can.useElement('calendar'),
251
+ isFile = $field.is(selector.file),
252
+ isErrored = $fieldGroup.hasClass(className.error)
253
+ ;
254
+ if (defaultValue === undefined) {
255
+ return;
256
+ }
257
+ if (isErrored) {
258
+ module.verbose('Resetting error on field', $fieldGroup);
259
+ $fieldGroup.removeClass(className.error);
260
+ $prompt.remove();
261
+ }
262
+ if (isDropdown) {
263
+ module.verbose('Resetting dropdown value', $element, defaultValue);
264
+ $element.dropdown('restore defaults', true);
265
+ } else if (isCheckbox) {
266
+ module.verbose('Resetting checkbox value', $field, defaultValue);
267
+ $field.prop('checked', defaultValue);
268
+ } else if (isCalendar) {
269
+ $calendar.calendar('set date', defaultValue);
270
+ } else {
271
+ module.verbose('Resetting field value', $field, defaultValue);
272
+ $field.val(isFile ? '' : defaultValue);
273
+ }
274
+ });
275
+ module.remove.states();
276
+ },
277
+
278
+ determine: {
279
+ isValid: function () {
280
+ var
281
+ allValid = true
282
+ ;
283
+ $field.each(function (index, el) {
284
+ var $el = $(el),
285
+ validation = module.get.validation($el) || {},
286
+ identifier = module.get.identifier(validation, $el)
287
+ ;
288
+ if (!module.validate.field(validation, identifier, true)) {
289
+ allValid = false;
290
+ }
291
+ });
292
+
293
+ return allValid;
294
+ },
295
+ isDirty: function (e) {
296
+ var formIsDirty = false;
297
+
298
+ $field.each(function (index, el) {
299
+ var
300
+ $el = $(el),
301
+ isCheckbox = $el.filter(selector.checkbox).length > 0,
302
+ isDirty
303
+ ;
304
+
305
+ isDirty = isCheckbox
306
+ ? module.is.checkboxDirty($el)
307
+ : module.is.fieldDirty($el);
308
+
309
+ $el.data(settings.metadata.isDirty, isDirty);
310
+
311
+ formIsDirty = formIsDirty || isDirty;
312
+ });
313
+
314
+ if (formIsDirty) {
315
+ module.set.dirty();
316
+ } else {
317
+ module.set.clean();
318
+ }
319
+ },
320
+ },
321
+
322
+ is: {
323
+ bracketedRule: function (rule) {
324
+ return rule.type && rule.type.match(settings.regExp.bracket);
325
+ },
326
+ // duck type rule test
327
+ shorthandRules: function (rules) {
328
+ return typeof rules === 'string' || Array.isArray(rules);
329
+ },
330
+ empty: function ($field) {
331
+ if (!$field || $field.length === 0) {
332
+ return true;
333
+ }
334
+ if ($field.is(selector.checkbox)) {
335
+ return !$field.is(':checked');
336
+ }
337
+
338
+ return module.is.blank($field);
339
+ },
340
+ blank: function ($field) {
341
+ return String($field.val()).trim() === '';
342
+ },
343
+ valid: function (field, showErrors) {
344
+ var
345
+ allValid = true
346
+ ;
347
+ if (field) {
348
+ module.verbose('Checking if field is valid', field);
349
+
350
+ return module.validate.field(validation[field], field, !!showErrors);
351
+ }
352
+
353
+ module.verbose('Checking if form is valid');
354
+ $.each(validation, function (fieldName, field) {
355
+ if (!module.is.valid(fieldName, showErrors)) {
356
+ allValid = false;
357
+ }
358
+ });
359
+
360
+ return allValid;
361
+ },
362
+ dirty: function () {
363
+ return dirty;
364
+ },
365
+ clean: function () {
366
+ return !dirty;
367
+ },
368
+ fieldDirty: function ($el) {
369
+ var initialValue = $el.data(metadata.defaultValue);
370
+ // Explicitly check for undefined/null here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work
371
+ if (initialValue === undefined || initialValue === null) {
372
+ initialValue = '';
373
+ } else if (Array.isArray(initialValue)) {
374
+ initialValue = initialValue.toString();
375
+ }
376
+ var currentValue = $el.val();
377
+ if (currentValue === undefined || currentValue === null) {
378
+ currentValue = '';
379
+ } else if (Array.isArray(currentValue)) {
380
+ // multiple select values are returned as arrays which are never equal, so do string conversion first
381
+ currentValue = currentValue.toString();
382
+ }
383
+ // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison
384
+ var boolRegex = /^(true|false)$/i;
385
+ var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
386
+ if (isBoolValue) {
387
+ var regex = new RegExp('^' + initialValue + '$', 'i');
388
+
389
+ return !regex.test(currentValue);
390
+ }
391
+
392
+ return currentValue !== initialValue;
393
+ },
394
+ checkboxDirty: function ($el) {
395
+ var initialValue = $el.data(metadata.defaultValue);
396
+ var currentValue = $el.is(':checked');
397
+
398
+ return initialValue !== currentValue;
399
+ },
400
+ justDirty: function () {
401
+ return history[0] === 'dirty';
402
+ },
403
+ justClean: function () {
404
+ return history[0] === 'clean';
405
+ },
406
+ },
407
+
408
+ removeEvents: function () {
409
+ $module.off(eventNamespace);
410
+ $field.off(eventNamespace);
411
+ $submit.off(eventNamespace);
412
+ if (settings.preventLeaving) {
413
+ $window.off(eventNamespace);
414
+ }
415
+ if (attachEventsSelector) {
416
+ $(attachEventsSelector).off(eventNamespace);
417
+ attachEventsSelector = undefined;
418
+ }
419
+ },
420
+
421
+ event: {
422
+ field: {
423
+ keydown: function (event) {
424
+ var
425
+ $field = $(this),
426
+ key = event.which,
427
+ isInput = $field.is(selector.input),
428
+ isCheckbox = $field.is(selector.checkbox),
429
+ isInDropdown = $field.closest(selector.uiDropdown).length > 0,
430
+ keyCode = {
431
+ enter: 13,
432
+ escape: 27,
433
+ }
434
+ ;
435
+ if (key === keyCode.escape) {
436
+ module.verbose('Escape key pressed blurring field');
437
+ $field[0]
438
+ .blur()
439
+ ;
440
+ }
441
+ if (!event.ctrlKey && key === keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
442
+ if (!keyHeldDown) {
443
+ $field.one('keyup' + eventNamespace, module.event.field.keyup);
444
+ module.submit(event);
445
+ module.debug('Enter pressed on input submitting form');
446
+ }
447
+ keyHeldDown = true;
448
+ }
449
+ },
450
+ keyup: function () {
451
+ keyHeldDown = false;
452
+ },
453
+ invalid: function (event) {
454
+ event.preventDefault();
455
+ },
456
+ blur: function (event) {
457
+ var
458
+ $field = $(this),
459
+ validationRules = module.get.validation($field) || {},
460
+ identifier = module.get.identifier(validationRules, $field)
461
+ ;
462
+ if (settings.on === 'blur' || (!$module.hasClass(className.initial) && settings.revalidate)) {
463
+ module.debug('Revalidating field', $field, validationRules);
464
+ module.validate.field(validationRules, identifier);
465
+ if (!settings.inline) {
466
+ module.validate.form(false, true);
467
+ }
468
+ }
469
+ },
470
+ change: function (event) {
471
+ var
472
+ $field = $(this),
473
+ validationRules = module.get.validation($field) || {},
474
+ identifier = module.get.identifier(validationRules, $field)
475
+ ;
476
+ if (settings.on === 'change' || (!$module.hasClass(className.initial) && settings.revalidate)) {
477
+ clearTimeout(module.timer);
478
+ module.timer = setTimeout(function () {
479
+ module.debug('Revalidating field', $field, validationRules);
480
+ module.validate.field(validationRules, identifier);
481
+ if (!settings.inline) {
482
+ module.validate.form(false, true);
483
+ }
484
+ }, settings.delay);
485
+ }
486
+ },
487
+ },
488
+ beforeUnload: function (event) {
489
+ if (module.is.dirty() && !submitting) {
490
+ event = event || window.event;
491
+
492
+ // For modern browsers
493
+ if (event) {
494
+ event.returnValue = settings.text.leavingMessage;
495
+ }
496
+
497
+ // For olders...
498
+ return settings.text.leavingMessage;
499
+ }
500
+ },
501
+
502
+ },
503
+
504
+ get: {
505
+ ancillaryValue: function (rule) {
506
+ if (!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
507
+ return false;
508
+ }
509
+
510
+ return rule.value !== undefined
511
+ ? rule.value
512
+ : rule.type.match(settings.regExp.bracket)[1] + '';
513
+ },
514
+ ruleName: function (rule) {
515
+ if (module.is.bracketedRule(rule)) {
516
+ return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
517
+ }
518
+
519
+ return rule.type;
520
+ },
521
+ changeEvent: function (type, $input) {
522
+ return ['file', 'checkbox', 'radio', 'hidden'].indexOf(type) >= 0 || $input.is('select') ? 'change' : 'input';
523
+ },
524
+ fieldsFromShorthand: function (fields) {
525
+ var
526
+ fullFields = {}
527
+ ;
528
+ $.each(fields, function (name, rules) {
529
+ if (!Array.isArray(rules) && typeof rules === 'object') {
530
+ fullFields[name] = rules;
531
+ } else {
532
+ if (typeof rules === 'string') {
533
+ rules = [rules];
534
+ }
535
+ fullFields[name] = {
536
+ rules: [],
537
+ };
538
+ $.each(rules, function (index, rule) {
539
+ fullFields[name].rules.push({ type: rule });
540
+ });
541
+ }
542
+
543
+ $.each(fullFields[name].rules, function (index, rule) {
544
+ var ruleName = module.get.ruleName(rule);
545
+ if (ruleName === 'empty') {
546
+ module.warn('*** DEPRECATED *** : Rule "empty" for field "' + name + '" will be removed in a future version. -> Use "notEmpty" rule instead.');
547
+ }
548
+ });
549
+ });
550
+
551
+ return fullFields;
552
+ },
553
+ identifier: function (validation, $el) {
554
+ return validation.identifier || $el.attr('id') || $el.attr('name') || $el.data(metadata.validate);
555
+ },
556
+ prompt: function (rule, field) {
557
+ var
558
+ ruleName = module.get.ruleName(rule),
559
+ ancillary = module.get.ancillaryValue(rule),
560
+ $field = module.get.field(field.identifier),
561
+ value = $field.val(),
562
+ promptCheck = rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
563
+ prompt = String(isFunction(promptCheck)
564
+ ? promptCheck.call($field[0], value)
565
+ : promptCheck),
566
+ requiresValue = prompt.search('{value}') !== -1,
567
+ requiresName = prompt.search('{name}') !== -1,
568
+ parts,
569
+ suffixPrompt
570
+ ;
571
+ if (ancillary && ['integer', 'decimal', 'number', 'size'].indexOf(ruleName) >= 0 && ancillary.indexOf('..') >= 0) {
572
+ parts = ancillary.split('..', 2);
573
+ if (!rule.prompt && ruleName !== 'size') {
574
+ suffixPrompt = parts[0] === ''
575
+ ? settings.prompt.maxValue.replace(/{ruleValue}/g, '{max}')
576
+ : (parts[1] === ''
577
+ ? settings.prompt.minValue.replace(/{ruleValue}/g, '{min}')
578
+ : settings.prompt.range);
579
+ prompt += suffixPrompt.replace(/{name}/g, ' ' + settings.text.and);
580
+ }
581
+ prompt = prompt.replace(/{min}/g, parts[0]);
582
+ prompt = prompt.replace(/{max}/g, parts[1]);
583
+ }
584
+ if (ancillary && ['match', 'different'].indexOf(ruleName) >= 0) {
585
+ prompt = prompt.replace(/{ruleValue}/g, module.get.fieldLabel(ancillary, true));
586
+ }
587
+ if (requiresValue) {
588
+ prompt = prompt.replace(/{value}/g, $field.val());
589
+ }
590
+ if (requiresName) {
591
+ prompt = prompt.replace(/{name}/g, module.get.fieldLabel($field));
592
+ }
593
+ prompt = prompt.replace(/{identifier}/g, field.identifier);
594
+ prompt = prompt.replace(/{ruleValue}/g, ancillary);
595
+ if (!rule.prompt) {
596
+ module.verbose('Using default validation prompt for type', prompt, ruleName);
597
+ }
598
+
599
+ return prompt;
600
+ },
601
+ settings: function () {
602
+ if ($.isPlainObject(parameters)) {
603
+ settings = $.extend(true, {}, $.fn.form.settings, parameters);
604
+ if (settings.fields) {
605
+ settings.fields = module.get.fieldsFromShorthand(settings.fields);
606
+ }
607
+ validation = $.extend(true, {}, $.fn.form.settings.defaults, settings.fields);
608
+ module.verbose('Extending settings', validation, settings);
609
+ } else {
610
+ settings = $.extend(true, {}, $.fn.form.settings);
611
+ validation = $.extend(true, {}, $.fn.form.settings.defaults);
612
+ module.verbose('Using default form validation', validation, settings);
613
+ }
614
+
615
+ // shorthand
616
+ namespace = settings.namespace;
617
+ metadata = settings.metadata;
618
+ selector = settings.selector;
619
+ className = settings.className;
620
+ regExp = settings.regExp;
621
+ error = settings.error;
622
+ moduleNamespace = 'module-' + namespace;
623
+ eventNamespace = '.' + namespace;
624
+
625
+ // grab instance
626
+ instance = $module.data(moduleNamespace);
627
+
628
+ // refresh selector cache
629
+ (instance || module).refresh();
630
+ },
631
+ field: function (identifier, strict, ignoreMissing) {
632
+ module.verbose('Finding field with identifier', identifier);
633
+ identifier = module.escape.string(identifier);
634
+ var t;
635
+ t = $field.filter('#' + identifier);
636
+ if (t.length > 0) {
637
+ return t;
638
+ }
639
+ t = $field.filter('[name="' + identifier + '"]');
640
+ if (t.length > 0) {
641
+ return t;
642
+ }
643
+ t = $field.filter('[name="' + identifier + '[]"]');
644
+ if (t.length > 0) {
645
+ return t;
646
+ }
647
+ t = $field.filter('[data-' + metadata.validate + '="' + identifier + '"]');
648
+ if (t.length > 0) {
649
+ return t;
650
+ }
651
+ if (!ignoreMissing) {
652
+ module.error(error.noField.replace('{identifier}', identifier));
653
+ }
654
+
655
+ return strict ? $() : $('<input/>');
656
+ },
657
+ fields: function (fields, strict) {
658
+ var
659
+ $fields = $()
660
+ ;
661
+ $.each(fields, function (index, name) {
662
+ $fields = $fields.add(module.get.field(name, strict));
663
+ });
664
+
665
+ return $fields;
666
+ },
667
+ fieldLabel: function (identifier, useIdAsFallback) {
668
+ var $field = typeof identifier === 'string'
669
+ ? module.get.field(identifier)
670
+ : identifier,
671
+ $label = $field.closest(selector.group).find('label:not(:empty)').eq(0)
672
+ ;
673
+
674
+ return $label.length === 1
675
+ ? $label.text()
676
+ : $field.prop('placeholder') || (useIdAsFallback ? identifier : settings.text.unspecifiedField);
677
+ },
678
+ validation: function ($field) {
679
+ var
680
+ fieldValidation,
681
+ identifier
682
+ ;
683
+ if (!validation) {
684
+ return false;
685
+ }
686
+ $.each(validation, function (fieldName, field) {
687
+ identifier = field.identifier || fieldName;
688
+ $.each(module.get.field(identifier), function (index, groupField) {
689
+ if (groupField == $field[0]) {
690
+ field.identifier = identifier;
691
+ fieldValidation = field;
692
+
693
+ return false;
694
+ }
695
+ });
696
+ });
697
+
698
+ return fieldValidation || false;
699
+ },
700
+ value: function (field, strict) {
701
+ var
702
+ fields = [],
703
+ results,
704
+ resultKeys
705
+ ;
706
+ fields.push(field);
707
+ results = module.get.values.call(element, fields, strict);
708
+ resultKeys = Object.keys(results);
709
+
710
+ return resultKeys.length > 0 ? results[resultKeys[0]] : undefined;
711
+ },
712
+ values: function (fields, strict) {
713
+ var
714
+ $fields = Array.isArray(fields) && fields.length > 0
715
+ ? module.get.fields(fields, strict)
716
+ : $field,
717
+ values = {}
718
+ ;
719
+ $fields.each(function (index, field) {
720
+ var
721
+ $field = $(field),
722
+ $calendar = $field.closest(selector.uiCalendar),
723
+ name = $field.prop('name') || $field.prop('id'),
724
+ value = $field.val(),
725
+ isCheckbox = $field.is(selector.checkbox),
726
+ isRadio = $field.is(selector.radio),
727
+ isMultiple = name.indexOf('[]') !== -1,
728
+ isCalendar = $calendar.length > 0 && module.can.useElement('calendar'),
729
+ isChecked = isCheckbox
730
+ ? $field.is(':checked')
731
+ : false
732
+ ;
733
+ if (name) {
734
+ if (isMultiple) {
735
+ name = name.replace('[]', '');
736
+ if (!values[name]) {
737
+ values[name] = [];
738
+ }
739
+ if (isCheckbox) {
740
+ if (isChecked) {
741
+ values[name].push(value || true);
742
+ } else {
743
+ values[name].push(false);
744
+ }
745
+ } else {
746
+ values[name].push(value);
747
+ }
748
+ } else {
749
+ if (isRadio) {
750
+ if (values[name] === undefined || values[name] === false) {
751
+ values[name] = isChecked
752
+ ? value || true
753
+ : false;
754
+ }
755
+ } else if (isCheckbox) {
756
+ values[name] = isChecked ? value || true : false;
757
+ } else if (isCalendar) {
758
+ var date = $calendar.calendar('get date');
759
+
760
+ if (date !== null) {
761
+ switch (settings.dateHandling) {
762
+ case 'date': {
763
+ values[name] = date;
764
+
765
+ break;
766
+ }
767
+ case 'input': {
768
+ values[name] = $calendar.calendar('get input date');
769
+
770
+ break;
771
+ }
772
+ case 'formatter': {
773
+ var type = $calendar.calendar('setting', 'type');
774
+
775
+ switch (type) {
776
+ case 'date': {
777
+ values[name] = settings.formatter.date(date);
778
+
779
+ break;
780
+ }
781
+ case 'datetime': {
782
+ values[name] = settings.formatter.datetime(date);
783
+
784
+ break;
785
+ }
786
+ case 'time': {
787
+ values[name] = settings.formatter.time(date);
788
+
789
+ break;
790
+ }
791
+ case 'month': {
792
+ values[name] = settings.formatter.month(date);
793
+
794
+ break;
795
+ }
796
+ case 'year': {
797
+ values[name] = settings.formatter.year(date);
798
+
799
+ break;
800
+ }
801
+ default: {
802
+ module.debug('Wrong calendar mode', $calendar, type);
803
+ values[name] = '';
804
+ }
805
+ }
806
+
807
+ break;
808
+ }
809
+ }
810
+ } else {
811
+ values[name] = '';
812
+ }
813
+ } else {
814
+ values[name] = value;
815
+ }
816
+ }
817
+ }
818
+ });
819
+
820
+ return values;
821
+ },
822
+ dirtyFields: function () {
823
+ return $field.filter(function (index, e) {
824
+ return $(e).data(metadata.isDirty);
825
+ });
826
+ },
827
+ },
828
+
829
+ has: {
830
+
831
+ field: function (identifier, ignoreMissing) {
832
+ module.verbose('Checking for existence of a field with identifier', identifier);
833
+
834
+ return module.get.field(identifier, true, ignoreMissing).length > 0;
835
+ },
836
+
837
+ },
838
+
839
+ can: {
840
+ useElement: function (element) {
841
+ if ($.fn[element] !== undefined) {
842
+ return true;
843
+ }
844
+ module.error(error.noElement.replace('{element}', element));
845
+
846
+ return false;
847
+ },
848
+ },
849
+
850
+ escape: {
851
+ string: function (text) {
852
+ text = String(text);
853
+
854
+ return text.replace(regExp.escape, '\\$&');
855
+ },
856
+ },
857
+
858
+ checkErrors: function (errors, internal) {
859
+ if (!errors || errors.length === 0) {
860
+ if (!internal) {
861
+ module.error(settings.error.noErrorMessage);
862
+ }
863
+
864
+ return false;
865
+ }
866
+ if (!internal) {
867
+ errors = typeof errors === 'string'
868
+ ? [errors]
869
+ : errors;
870
+ }
871
+
872
+ return errors;
873
+ },
874
+ add: {
875
+ // alias
876
+ rule: function (name, rules) {
877
+ module.add.field(name, rules);
878
+ },
879
+ field: function (name, rules) {
880
+ // Validation should have at least a standard format
881
+ if (validation[name] === undefined || validation[name].rules === undefined) {
882
+ validation[name] = {
883
+ rules: [],
884
+ };
885
+ }
886
+ var
887
+ newValidation = {
888
+ rules: [],
889
+ }
890
+ ;
891
+ if (module.is.shorthandRules(rules)) {
892
+ rules = Array.isArray(rules)
893
+ ? rules
894
+ : [rules];
895
+ $.each(rules, function (_index, rule) {
896
+ newValidation.rules.push({ type: rule });
897
+ });
898
+ } else {
899
+ newValidation.rules = rules.rules;
900
+ }
901
+ // For each new rule, check if there's not already one with the same type
902
+ $.each(newValidation.rules, function (_index, rule) {
903
+ if ($.grep(validation[name].rules, function (item) {
904
+ return item.type === rule.type;
905
+ }).length === 0) {
906
+ validation[name].rules.push(rule);
907
+ }
908
+ });
909
+ module.debug('Adding rules', newValidation.rules, validation);
910
+ module.refreshEvents();
911
+ },
912
+ fields: function (fields) {
913
+ validation = $.extend(true, {}, validation, module.get.fieldsFromShorthand(fields));
914
+ module.refreshEvents();
915
+ },
916
+ prompt: function (identifier, errors, internal) {
917
+ errors = module.checkErrors(errors);
918
+ if (errors === false) {
919
+ return;
920
+ }
921
+ var
922
+ $field = module.get.field(identifier),
923
+ $fieldGroup = $field.closest($group),
924
+ $prompt = $fieldGroup.children(selector.prompt),
925
+ promptExists = $prompt.length > 0,
926
+ canTransition = settings.transition && module.can.useElement('transition')
927
+ ;
928
+ module.verbose('Adding field error state', identifier);
929
+ if (!internal) {
930
+ $fieldGroup
931
+ .addClass(className.error)
932
+ ;
933
+ }
934
+ if (settings.inline) {
935
+ if (promptExists) {
936
+ if (canTransition) {
937
+ if ($prompt.transition('is animating')) {
938
+ $prompt.transition('stop all');
939
+ }
940
+ } else if ($prompt.is(':animated')) {
941
+ $prompt.stop(true, true);
942
+ }
943
+ $prompt = $fieldGroup.children(selector.prompt);
944
+ promptExists = $prompt.length > 0;
945
+ }
946
+ if (!promptExists) {
947
+ $prompt = $('<div/>').addClass(className.label);
948
+ if (!canTransition) {
949
+ $prompt.css('display', 'none');
950
+ }
951
+ $prompt
952
+ .appendTo($fieldGroup.filter('.' + className.error))
953
+ ;
954
+ }
955
+ $prompt
956
+ .html(settings.templates.prompt(errors))
957
+ ;
958
+ if (!promptExists) {
959
+ if (canTransition) {
960
+ module.verbose('Displaying error with css transition', settings.transition);
961
+ $prompt.transition(settings.transition + ' in', settings.duration);
962
+ } else {
963
+ module.verbose('Displaying error with fallback javascript animation');
964
+ $prompt
965
+ .fadeIn(settings.duration)
966
+ ;
967
+ }
968
+ }
969
+ } else {
970
+ module.verbose('Inline errors are disabled, no inline error added', identifier);
971
+ }
972
+ },
973
+ errors: function (errors) {
974
+ errors = module.checkErrors(errors);
975
+ if (errors === false) {
976
+ return;
977
+ }
978
+ module.debug('Adding form error messages', errors);
979
+ module.set.error();
980
+ var customErrors = [],
981
+ tempErrors
982
+ ;
983
+ if ($.isPlainObject(errors)) {
984
+ $.each(Object.keys(errors), function (i, id) {
985
+ if (module.checkErrors(errors[id], true) !== false) {
986
+ if (settings.inline) {
987
+ module.add.prompt(id, errors[id]);
988
+ } else {
989
+ tempErrors = module.checkErrors(errors[id]);
990
+ if (tempErrors !== false) {
991
+ $.each(tempErrors, function (index, tempError) {
992
+ customErrors.push(settings.prompt.addErrors
993
+ .replace(/{name}/g, module.get.fieldLabel(id))
994
+ .replace(/{error}/g, tempError));
995
+ });
996
+ }
997
+ }
998
+ }
999
+ });
1000
+ } else {
1001
+ customErrors = errors;
1002
+ }
1003
+ if (customErrors.length > 0) {
1004
+ $message
1005
+ .html(settings.templates.error(customErrors))
1006
+ ;
1007
+ }
1008
+ },
1009
+ },
1010
+
1011
+ remove: {
1012
+ errors: function () {
1013
+ module.debug('Removing form error messages');
1014
+ $message.empty();
1015
+ },
1016
+ states: function () {
1017
+ $module.removeClass(className.error).removeClass(className.success).addClass(className.initial);
1018
+ if (!settings.inline) {
1019
+ module.remove.errors();
1020
+ }
1021
+ module.determine.isDirty();
1022
+ },
1023
+ rule: function (field, rule) {
1024
+ var
1025
+ rules = Array.isArray(rule)
1026
+ ? rule
1027
+ : [rule]
1028
+ ;
1029
+ if (validation[field] === undefined || !Array.isArray(validation[field].rules)) {
1030
+ return;
1031
+ }
1032
+ if (rule === undefined) {
1033
+ module.debug('Removed all rules');
1034
+ if (module.has.field(field, true)) {
1035
+ validation[field].rules = [];
1036
+ } else {
1037
+ delete validation[field];
1038
+ }
1039
+
1040
+ return;
1041
+ }
1042
+ $.each(validation[field].rules, function (index, rule) {
1043
+ if (rule && rules.indexOf(rule.type) !== -1) {
1044
+ module.debug('Removed rule', rule.type);
1045
+ validation[field].rules.splice(index, 1);
1046
+ }
1047
+ });
1048
+ },
1049
+ field: function (field) {
1050
+ var
1051
+ fields = Array.isArray(field)
1052
+ ? field
1053
+ : [field]
1054
+ ;
1055
+ $.each(fields, function (index, field) {
1056
+ module.remove.rule(field);
1057
+ });
1058
+ module.refreshEvents();
1059
+ },
1060
+ // alias
1061
+ rules: function (field, rules) {
1062
+ if (Array.isArray(field)) {
1063
+ $.each(field, function (index, field) {
1064
+ module.remove.rule(field, rules);
1065
+ });
1066
+ } else {
1067
+ module.remove.rule(field, rules);
1068
+ }
1069
+ },
1070
+ fields: function (fields) {
1071
+ module.remove.field(fields);
1072
+ },
1073
+ prompt: function (identifier) {
1074
+ var
1075
+ $field = module.get.field(identifier),
1076
+ $fieldGroup = $field.closest($group),
1077
+ $prompt = $fieldGroup.children(selector.prompt)
1078
+ ;
1079
+ $fieldGroup
1080
+ .removeClass(className.error)
1081
+ ;
1082
+ if (settings.inline && $prompt.is(':visible')) {
1083
+ module.verbose('Removing prompt for field', identifier);
1084
+ if (settings.transition && module.can.useElement('transition')) {
1085
+ $prompt.transition(settings.transition + ' out', settings.duration, function () {
1086
+ $prompt.remove();
1087
+ });
1088
+ } else {
1089
+ $prompt
1090
+ .fadeOut(settings.duration, function () {
1091
+ $prompt.remove();
1092
+ })
1093
+ ;
1094
+ }
1095
+ }
1096
+ },
1097
+ },
1098
+
1099
+ set: {
1100
+ success: function () {
1101
+ $module
1102
+ .removeClass(className.error)
1103
+ .addClass(className.success)
1104
+ ;
1105
+ },
1106
+ defaults: function () {
1107
+ $field.each(function (index, el) {
1108
+ var
1109
+ $el = $(el),
1110
+ $parent = $el.parent(),
1111
+ isCheckbox = $el.filter(selector.checkbox).length > 0,
1112
+ isDropdown = ($parent.is(selector.uiDropdown) || $el.is(selector.uiDropdown)) && module.can.useElement('dropdown'),
1113
+ $calendar = $el.closest(selector.uiCalendar),
1114
+ isCalendar = $calendar.length > 0 && module.can.useElement('calendar'),
1115
+ value = isCheckbox
1116
+ ? $el.is(':checked')
1117
+ : $el.val()
1118
+ ;
1119
+ if (isDropdown) {
1120
+ if ($parent.is(selector.uiDropdown)) {
1121
+ $parent.dropdown('save defaults');
1122
+ } else {
1123
+ $el.dropdown('save defaults');
1124
+ }
1125
+ } else if (isCalendar) {
1126
+ $calendar.calendar('refresh');
1127
+ }
1128
+ $el.data(metadata.defaultValue, value);
1129
+ $el.data(metadata.isDirty, false);
1130
+ });
1131
+ },
1132
+ error: function () {
1133
+ $module
1134
+ .removeClass(className.success)
1135
+ .addClass(className.error)
1136
+ ;
1137
+ },
1138
+ value: function (field, value) {
1139
+ var
1140
+ fields = {}
1141
+ ;
1142
+ fields[field] = value;
1143
+
1144
+ return module.set.values.call(element, fields);
1145
+ },
1146
+ values: function (fields) {
1147
+ if ($.isEmptyObject(fields)) {
1148
+ return;
1149
+ }
1150
+ $.each(fields, function (key, value) {
1151
+ var
1152
+ $field = module.get.field(key),
1153
+ $element = $field.parent(),
1154
+ $calendar = $field.closest(selector.uiCalendar),
1155
+ isFile = $field.is(selector.file),
1156
+ isMultiple = Array.isArray(value),
1157
+ isCheckbox = $element.is(selector.uiCheckbox) && module.can.useElement('checkbox'),
1158
+ isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
1159
+ isRadio = $field.is(selector.radio) && isCheckbox,
1160
+ isCalendar = $calendar.length > 0 && module.can.useElement('calendar'),
1161
+ fieldExists = $field.length > 0,
1162
+ $multipleField
1163
+ ;
1164
+ if (fieldExists) {
1165
+ if (isMultiple && isCheckbox) {
1166
+ module.verbose('Selecting multiple', value, $field);
1167
+ $element.checkbox('uncheck');
1168
+ $.each(value, function (index, value) {
1169
+ $multipleField = $field.filter('[value="' + value + '"]');
1170
+ $element = $multipleField.parent();
1171
+ if ($multipleField.length > 0) {
1172
+ $element.checkbox('check');
1173
+ }
1174
+ });
1175
+ } else if (isRadio) {
1176
+ module.verbose('Selecting radio value', value, $field);
1177
+ $field.filter('[value="' + value + '"]')
1178
+ .parent(selector.uiCheckbox)
1179
+ .checkbox('check')
1180
+ ;
1181
+ } else if (isCheckbox) {
1182
+ module.verbose('Setting checkbox value', value, $element);
1183
+ if (value === true || value === 1 || value === 'on') {
1184
+ $element.checkbox('check');
1185
+ } else {
1186
+ $element.checkbox('uncheck');
1187
+ }
1188
+ if (typeof value === 'string') {
1189
+ $field.val(value);
1190
+ }
1191
+ } else if (isDropdown) {
1192
+ module.verbose('Setting dropdown value', value, $element);
1193
+ $element.dropdown('set selected', value);
1194
+ } else if (isCalendar) {
1195
+ $calendar.calendar('set date', value);
1196
+ } else {
1197
+ module.verbose('Setting field value', value, $field);
1198
+ $field.val(isFile ? '' : value);
1199
+ }
1200
+ }
1201
+ });
1202
+ },
1203
+ dirty: function () {
1204
+ module.verbose('Setting state dirty');
1205
+ dirty = true;
1206
+ history[0] = history[1];
1207
+ history[1] = 'dirty';
1208
+
1209
+ if (module.is.justClean()) {
1210
+ $module.trigger('dirty');
1211
+ }
1212
+ },
1213
+ clean: function () {
1214
+ module.verbose('Setting state clean');
1215
+ dirty = false;
1216
+ history[0] = history[1];
1217
+ history[1] = 'clean';
1218
+
1219
+ if (module.is.justDirty()) {
1220
+ $module.trigger('clean');
1221
+ }
1222
+ },
1223
+ asClean: function () {
1224
+ module.set.defaults();
1225
+ module.set.clean();
1226
+ },
1227
+ asDirty: function () {
1228
+ module.set.defaults();
1229
+ module.set.dirty();
1230
+ },
1231
+ autoCheck: function () {
1232
+ module.debug('Enabling auto check on required fields');
1233
+ if (validation) {
1234
+ $.each(validation, function (fieldName) {
1235
+ if (!module.has.field(fieldName, true)) {
1236
+ module.verbose('Field not found, removing from validation', fieldName);
1237
+ module.remove.field(fieldName);
1238
+ }
1239
+ });
1240
+ }
1241
+ $field.each(function (_index, el) {
1242
+ var
1243
+ $el = $(el),
1244
+ $elGroup = $el.closest($group),
1245
+ isCheckbox = $el.filter(selector.checkbox).length > 0,
1246
+ isRequired = $el.prop('required') || $elGroup.hasClass(className.required) || $elGroup.parent().hasClass(className.required),
1247
+ isDisabled = $el.is(':disabled') || $elGroup.hasClass(className.disabled) || $elGroup.parent().hasClass(className.disabled),
1248
+ validation = module.get.validation($el),
1249
+ hasNotEmptyRule = validation
1250
+ ? $.grep(validation.rules, function (rule) {
1251
+ return ['notEmpty', 'checked', 'empty'].indexOf(rule.type) >= 0;
1252
+ }).length > 0
1253
+ : false,
1254
+ identifier = module.get.identifier(validation, $el)
1255
+ ;
1256
+ if (isRequired && !isDisabled && !hasNotEmptyRule && identifier !== undefined) {
1257
+ if (isCheckbox) {
1258
+ module.verbose("Adding 'checked' rule on field", identifier);
1259
+ module.add.rule(identifier, 'checked');
1260
+ } else {
1261
+ module.verbose("Adding 'notEmpty' rule on field", identifier);
1262
+ module.add.rule(identifier, 'notEmpty');
1263
+ }
1264
+ }
1265
+ });
1266
+ },
1267
+ optional: function (identifier, bool) {
1268
+ bool = bool !== false;
1269
+ $.each(validation, function (fieldName, field) {
1270
+ if (identifier === fieldName || identifier === field.identifier) {
1271
+ field.optional = bool;
1272
+ }
1273
+ });
1274
+ },
1275
+ },
1276
+
1277
+ validate: {
1278
+
1279
+ form: function (event, ignoreCallbacks) {
1280
+ var values = module.get.values();
1281
+
1282
+ // input keydown event will fire submit repeatedly by browser default
1283
+ if (keyHeldDown) {
1284
+ return false;
1285
+ }
1286
+ $module.removeClass(className.initial);
1287
+ // reset errors
1288
+ formErrors = [];
1289
+ formErrorsTracker = {};
1290
+ if (module.determine.isValid()) {
1291
+ module.debug('Form has no validation errors, submitting');
1292
+ module.set.success();
1293
+ if (!settings.inline) {
1294
+ module.remove.errors();
1295
+ }
1296
+ if (ignoreCallbacks !== true) {
1297
+ return settings.onSuccess.call(element, event, values);
1298
+ }
1299
+ } else {
1300
+ module.debug('Form has errors');
1301
+ submitting = false;
1302
+ module.set.error();
1303
+ if (!settings.inline) {
1304
+ module.add.errors(formErrors);
1305
+ }
1306
+ // prevent ajax submit
1307
+ if (event && $module.data('moduleApi') !== undefined) {
1308
+ event.stopImmediatePropagation();
1309
+ }
1310
+ if (settings.errorFocus && ignoreCallbacks !== true) {
1311
+ var
1312
+ $focusElement,
1313
+ hasTabIndex = true
1314
+ ;
1315
+ if (typeof settings.errorFocus === 'string') {
1316
+ $focusElement = $(document).find(settings.errorFocus);
1317
+ hasTabIndex = $focusElement.is('[tabindex]');
1318
+ // to be able to focus/scroll into non input elements we need a tabindex
1319
+ if (!hasTabIndex) {
1320
+ $focusElement.attr('tabindex', -1);
1321
+ }
1322
+ } else {
1323
+ $focusElement = $group.filter('.' + className.error).first().find(selector.field);
1324
+ }
1325
+ $focusElement.trigger('focus');
1326
+ // only remove tabindex if it was dynamically created above
1327
+ if (!hasTabIndex) {
1328
+ $focusElement.removeAttr('tabindex');
1329
+ }
1330
+ }
1331
+ if (ignoreCallbacks !== true) {
1332
+ return settings.onFailure.call(element, formErrors, values);
1333
+ }
1334
+ }
1335
+ },
1336
+
1337
+ // takes a validation object and returns whether field passes validation
1338
+ field: function (field, fieldName, showErrors) {
1339
+ showErrors = showErrors !== undefined
1340
+ ? showErrors
1341
+ : true;
1342
+ if (typeof field === 'string') {
1343
+ module.verbose('Validating field', field);
1344
+ fieldName = field;
1345
+ field = validation[field];
1346
+ }
1347
+ if (!field) {
1348
+ module.debug('Unable to find field validation. Skipping', fieldName);
1349
+
1350
+ return true;
1351
+ }
1352
+ var
1353
+ identifier = field.identifier || fieldName,
1354
+ $field = module.get.field(identifier),
1355
+ $fieldGroup = $field.closest($group),
1356
+ $dependsField = field.depends
1357
+ ? module.get.field(field.depends)
1358
+ : false,
1359
+ fieldValid = true,
1360
+ fieldErrors = [],
1361
+ isDisabled = $field.filter(':not(:disabled)').length === 0 || $fieldGroup.hasClass(className.disabled) || $fieldGroup.parent().hasClass(className.disabled),
1362
+ validationMessage = $field[0].validationMessage,
1363
+ noNativeValidation = field.noNativeValidation || settings.noNativeValidation || $field.filter('[formnovalidate],[novalidate]').length > 0 || $module.filter('[novalidate]').length > 0,
1364
+ errorLimit
1365
+ ;
1366
+ if (!field.identifier) {
1367
+ module.debug('Using field name as identifier', identifier);
1368
+ field.identifier = identifier;
1369
+ }
1370
+ if (validationMessage && !noNativeValidation && !isDisabled) {
1371
+ module.debug('Field is natively invalid', identifier);
1372
+ fieldErrors.push(validationMessage);
1373
+ fieldValid = false;
1374
+ if (showErrors) {
1375
+ $fieldGroup.addClass(className.error);
1376
+ }
1377
+ } else if (showErrors) {
1378
+ $fieldGroup.removeClass(className.error);
1379
+ }
1380
+ if (isDisabled) {
1381
+ module.debug('Field is disabled. Skipping', identifier);
1382
+ } else if (field.optional && module.is.blank($field)) {
1383
+ module.debug('Field is optional and blank. Skipping', identifier);
1384
+ } else if (field.depends && module.is.empty($dependsField)) {
1385
+ module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
1386
+ } else if (field.rules !== undefined) {
1387
+ errorLimit = field.errorLimit || settings.errorLimit;
1388
+ $.each(field.rules, function (index, rule) {
1389
+ if (module.has.field(identifier) && (!errorLimit || fieldErrors.length < errorLimit)) {
1390
+ var invalidFields = module.validate.rule(field, rule, true) || [];
1391
+ if (invalidFields.length > 0) {
1392
+ module.debug('Field is invalid', identifier, rule.type);
1393
+ var fieldError = module.get.prompt(rule, field);
1394
+ if (!settings.inline) {
1395
+ if (
1396
+ // Always allow the first error prompt for new field identifiers
1397
+ (!(identifier in formErrorsTracker)
1398
+ // Also allow multiple error prompts per field identifier but make sure each prompt is unique
1399
+ || formErrorsTracker[identifier].indexOf(fieldError) === -1)
1400
+ // Limit the number of unique error prompts for every field identifier if specified
1401
+ && (!errorLimit || (formErrorsTracker[identifier] || []).length < errorLimit)
1402
+ ) {
1403
+ fieldErrors.push(fieldError);
1404
+ (formErrorsTracker[identifier] = formErrorsTracker[identifier] || []).push(fieldError);
1405
+ }
1406
+ } else {
1407
+ fieldErrors.push(fieldError);
1408
+ }
1409
+ fieldValid = false;
1410
+ if (showErrors) {
1411
+ $(invalidFields).closest($group).addClass(className.error);
1412
+ }
1413
+ }
1414
+ }
1415
+ });
1416
+ }
1417
+ if (fieldValid) {
1418
+ if (showErrors) {
1419
+ module.remove.prompt(identifier);
1420
+ settings.onValid.call($field);
1421
+ }
1422
+ } else {
1423
+ if (showErrors && fieldErrors.length > 0) {
1424
+ formErrors = formErrors.concat(fieldErrors);
1425
+ module.add.prompt(identifier, fieldErrors, true);
1426
+ settings.onInvalid.call($field, fieldErrors);
1427
+ }
1428
+
1429
+ return false;
1430
+ }
1431
+
1432
+ return true;
1433
+ },
1434
+
1435
+ // takes validation rule and returns whether field passes rule
1436
+ rule: function (field, rule, internal) {
1437
+ var
1438
+ $field = module.get.field(field.identifier),
1439
+ ancillary = module.get.ancillaryValue(rule),
1440
+ ruleName = module.get.ruleName(rule),
1441
+ ruleFunction = settings.rules[ruleName],
1442
+ invalidFields = [],
1443
+ isCheckbox = $field.is(selector.checkbox),
1444
+ isValid = function (field) {
1445
+ var value = isCheckbox ? $(field).filter(':checked').val() : $(field).val();
1446
+ // cast to string avoiding encoding special values
1447
+ value = value === undefined || value === '' || value === null
1448
+ ? ''
1449
+ : ((settings.shouldTrim && rule.shouldTrim !== false) || rule.shouldTrim
1450
+ ? String(value + '').trim()
1451
+ : String(value + ''));
1452
+
1453
+ return ruleFunction.call(field, value, ancillary, module);
1454
+ }
1455
+ ;
1456
+ if (!isFunction(ruleFunction)) {
1457
+ module.error(error.noRule, ruleName);
1458
+
1459
+ return;
1460
+ }
1461
+ if (isCheckbox) {
1462
+ if (!isValid($field)) {
1463
+ invalidFields = $field;
1464
+ }
1465
+ } else {
1466
+ $.each($field, function (index, field) {
1467
+ if (!isValid(field)) {
1468
+ invalidFields.push(field);
1469
+ }
1470
+ });
1471
+ }
1472
+
1473
+ return internal ? invalidFields : invalidFields.length === 0;
1474
+ },
1475
+ },
1476
+
1477
+ setting: function (name, value) {
1478
+ if ($.isPlainObject(name)) {
1479
+ $.extend(true, settings, name);
1480
+ } else if (value !== undefined) {
1481
+ settings[name] = value;
1482
+ } else {
1483
+ return settings[name];
1484
+ }
1485
+ },
1486
+ internal: function (name, value) {
1487
+ if ($.isPlainObject(name)) {
1488
+ $.extend(true, module, name);
1489
+ } else if (value !== undefined) {
1490
+ module[name] = value;
1491
+ } else {
1492
+ return module[name];
1493
+ }
1494
+ },
1495
+ debug: function () {
1496
+ if (!settings.silent && settings.debug) {
1497
+ if (settings.performance) {
1498
+ module.performance.log(arguments);
1499
+ } else {
1500
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
1501
+ module.debug.apply(console, arguments);
1502
+ }
1503
+ }
1504
+ },
1505
+ verbose: function () {
1506
+ if (!settings.silent && settings.verbose && settings.debug) {
1507
+ if (settings.performance) {
1508
+ module.performance.log(arguments);
1509
+ } else {
1510
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
1511
+ module.verbose.apply(console, arguments);
1512
+ }
1513
+ }
1514
+ },
1515
+ error: function () {
1516
+ if (!settings.silent) {
1517
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
1518
+ module.error.apply(console, arguments);
1519
+ }
1520
+ },
1521
+ warn: function () {
1522
+ if (!settings.silent) {
1523
+ module.warn = Function.prototype.bind.call(console.warn, console, settings.name + ':');
1524
+ module.warn.apply(console, arguments);
1525
+ }
1526
+ },
1527
+ performance: {
1528
+ log: function (message) {
1529
+ var
1530
+ currentTime,
1531
+ executionTime,
1532
+ previousTime
1533
+ ;
1534
+ if (settings.performance) {
1535
+ currentTime = Date.now();
1536
+ previousTime = time || currentTime;
1537
+ executionTime = currentTime - previousTime;
1538
+ time = currentTime;
1539
+ performance.push({
1540
+ Name: message[0],
1541
+ Arguments: [].slice.call(message, 1) || '',
1542
+ Element: element,
1543
+ 'Execution Time': executionTime,
1544
+ });
1545
+ }
1546
+ clearTimeout(module.performance.timer);
1547
+ module.performance.timer = setTimeout(function () {
1548
+ module.performance.display();
1549
+ }, 500);
1550
+ },
1551
+ display: function () {
1552
+ var
1553
+ title = settings.name + ':',
1554
+ totalTime = 0
1555
+ ;
1556
+ time = false;
1557
+ clearTimeout(module.performance.timer);
1558
+ $.each(performance, function (index, data) {
1559
+ totalTime += data['Execution Time'];
1560
+ });
1561
+ title += ' ' + totalTime + 'ms';
1562
+ if ($allModules.length > 1) {
1563
+ title += ' (' + $allModules.length + ')';
1564
+ }
1565
+ if (performance.length > 0) {
1566
+ console.groupCollapsed(title);
1567
+ if (console.table) {
1568
+ console.table(performance);
1569
+ } else {
1570
+ $.each(performance, function (index, data) {
1571
+ console.log(data.Name + ': ' + data['Execution Time'] + 'ms');
1572
+ });
1573
+ }
1574
+ console.groupEnd();
1575
+ }
1576
+ performance = [];
1577
+ },
1578
+ },
1579
+ invoke: function (query, passedArguments, context) {
1580
+ var
1581
+ object = instance,
1582
+ maxDepth,
1583
+ found,
1584
+ response
1585
+ ;
1586
+ passedArguments = passedArguments || queryArguments;
1587
+ context = context || element;
1588
+ if (typeof query === 'string' && object !== undefined) {
1589
+ query = query.split(/[ .]/);
1590
+ maxDepth = query.length - 1;
1591
+ $.each(query, function (depth, value) {
1592
+ var camelCaseValue = depth !== maxDepth
1593
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1594
+ : query;
1595
+ if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) {
1596
+ object = object[camelCaseValue];
1597
+ } else if (object[camelCaseValue] !== undefined) {
1598
+ found = object[camelCaseValue];
1599
+
1600
+ return false;
1601
+ } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) {
1602
+ object = object[value];
1603
+ } else if (object[value] !== undefined) {
1604
+ found = object[value];
1605
+
1606
+ return false;
1607
+ } else {
1608
+ module.error(error.method, query);
1609
+
1610
+ return false;
1611
+ }
1612
+ });
1613
+ }
1614
+ if (isFunction(found)) {
1615
+ response = found.apply(context, passedArguments);
1616
+ } else if (found !== undefined) {
1617
+ response = found;
1618
+ }
1619
+ if (Array.isArray(returnedValue)) {
1620
+ returnedValue.push(response);
1621
+ } else if (returnedValue !== undefined) {
1622
+ returnedValue = [returnedValue, response];
1623
+ } else if (response !== undefined) {
1624
+ returnedValue = response;
1625
+ }
1626
+
1627
+ return found;
1628
+ },
1629
+ };
1630
+ module.initialize();
1631
+ });
1632
+
1633
+ return returnedValue !== undefined
1634
+ ? returnedValue
1635
+ : this;
1636
+ };
1637
+
1638
+ $.fn.form.settings = {
1639
+
1640
+ name: 'Form',
1641
+ namespace: 'form',
1642
+
1643
+ silent: false,
1644
+ debug: false,
1645
+ verbose: false,
1646
+ performance: true,
1647
+
1648
+ fields: false,
1649
+
1650
+ keyboardShortcuts: true,
1651
+ on: 'submit',
1652
+ inline: false,
1653
+
1654
+ delay: 200,
1655
+ revalidate: true,
1656
+ shouldTrim: true,
1657
+
1658
+ transition: 'scale',
1659
+ duration: 200,
1660
+
1661
+ autoCheckRequired: false,
1662
+ preventLeaving: false,
1663
+ errorFocus: true,
1664
+ dateHandling: 'date', // 'date', 'input', 'formatter'
1665
+ errorLimit: 0,
1666
+ noNativeValidation: false,
1667
+
1668
+ onValid: function () {},
1669
+ onInvalid: function () {},
1670
+ onSuccess: function () {
1671
+ return true;
1672
+ },
1673
+ onFailure: function () {
1674
+ return false;
1675
+ },
1676
+ onDirty: function () {},
1677
+ onClean: function () {},
1678
+
1679
+ metadata: {
1680
+ defaultValue: 'default',
1681
+ validate: 'validate',
1682
+ isDirty: 'isDirty',
1683
+ },
1684
+
1685
+ regExp: {
1686
+ htmlID: /^[A-Za-z][\w.:-]*$/g,
1687
+ bracket: /\[(.*)]/i,
1688
+ decimal: /^\d+\.?\d*$/,
1689
+ email: /^[\w!#$%&'*+./=?^`{|}~-]+@[\da-z]([\da-z-]*[\da-z])?(\.[\da-z]([\da-z-]*[\da-z])?)*$/i,
1690
+ escape: /[$()*+,./:=?@[\\\]^{|}-]/g,
1691
+ flags: /^\/(.*)\/(.*)?/,
1692
+ integer: /^-?\d+$/,
1693
+ number: /^-?\d*(\.\d+)?$/,
1694
+ url: /(https?:\/\/(?:www\.|(?!www))[^\s.]+\.\S{2,}|www\.\S+\.\S{2,})/i,
1695
+ },
1696
+
1697
+ text: {
1698
+ and: 'and',
1699
+ unspecifiedRule: 'Please enter a valid value',
1700
+ unspecifiedField: 'This field',
1701
+ leavingMessage: 'There are unsaved changes on this page which will be discarded if you continue.',
1702
+ },
1703
+
1704
+ prompt: {
1705
+ range: '{name} must be in a range from {min} to {max}',
1706
+ maxValue: '{name} must have a maximum value of {ruleValue}',
1707
+ minValue: '{name} must have a minimum value of {ruleValue}',
1708
+ empty: '{name} must have a value',
1709
+ notEmpty: '{name} must have a value',
1710
+ checked: '{name} must be checked',
1711
+ email: '{name} must be a valid e-mail',
1712
+ url: '{name} must be a valid url',
1713
+ regExp: '{name} is not formatted correctly',
1714
+ integer: '{name} must be an integer',
1715
+ decimal: '{name} must be a decimal number',
1716
+ number: '{name} must be set to a number',
1717
+ is: '{name} must be "{ruleValue}"',
1718
+ isExactly: '{name} must be exactly "{ruleValue}"',
1719
+ not: '{name} cannot be set to "{ruleValue}"',
1720
+ notExactly: '{name} cannot be set to exactly "{ruleValue}"',
1721
+ contains: '{name} must contain "{ruleValue}"',
1722
+ containsExactly: '{name} must contain exactly "{ruleValue}"',
1723
+ doesntContain: '{name} cannot contain "{ruleValue}"',
1724
+ doesntContainExactly: '{name} cannot contain exactly "{ruleValue}"',
1725
+ minLength: '{name} must be at least {ruleValue} characters',
1726
+ exactLength: '{name} must be exactly {ruleValue} characters',
1727
+ maxLength: '{name} cannot be longer than {ruleValue} characters',
1728
+ size: '{name} must have a length between {min} and {max} characters',
1729
+ match: '{name} must match {ruleValue} field',
1730
+ different: '{name} must have a different value than {ruleValue} field',
1731
+ creditCard: '{name} must be a valid credit card number',
1732
+ minCount: '{name} must have at least {ruleValue} choices',
1733
+ exactCount: '{name} must have exactly {ruleValue} choices',
1734
+ maxCount: '{name} must have {ruleValue} or less choices',
1735
+ addErrors: '{name}: {error}',
1736
+ },
1737
+
1738
+ selector: {
1739
+ checkbox: 'input[type="checkbox"], input[type="radio"]',
1740
+ clear: '.clear',
1741
+ field: 'input:not(.search):not([type="reset"]):not([type="button"]):not([type="submit"]), textarea, select',
1742
+ file: 'input[type="file"]',
1743
+ group: '.field',
1744
+ input: 'input',
1745
+ message: '.error.message',
1746
+ prompt: '.prompt.label',
1747
+ radio: 'input[type="radio"]',
1748
+ reset: '.reset:not([type="reset"])',
1749
+ submit: '.submit:not([type="submit"])',
1750
+ uiCheckbox: '.ui.checkbox',
1751
+ uiDropdown: '.ui.dropdown',
1752
+ uiCalendar: '.ui.calendar',
1753
+ },
1754
+
1755
+ className: {
1756
+ initial: 'initial',
1757
+ error: 'error',
1758
+ label: 'ui basic red pointing prompt label',
1759
+ pressed: 'down',
1760
+ success: 'success',
1761
+ required: 'required',
1762
+ disabled: 'disabled',
1763
+ },
1764
+
1765
+ error: {
1766
+ method: 'The method you called is not defined.',
1767
+ noRule: 'There is no rule matching the one you specified',
1768
+ noField: 'Field identifier {identifier} not found',
1769
+ noElement: 'This module requires ui {element}',
1770
+ noErrorMessage: 'No error message provided',
1771
+ },
1772
+
1773
+ templates: {
1774
+
1775
+ // template that produces error message
1776
+ error: function (errors) {
1777
+ var
1778
+ html = '<ul class="list">'
1779
+ ;
1780
+ $.each(errors, function (index, value) {
1781
+ html += '<li>' + value + '</li>';
1782
+ });
1783
+ html += '</ul>';
1784
+
1785
+ return html;
1786
+ },
1787
+
1788
+ // template that produces label content
1789
+ prompt: function (errors) {
1790
+ if (errors.length === 1) {
1791
+ return errors[0];
1792
+ }
1793
+ var
1794
+ html = '<ul class="ui list">'
1795
+ ;
1796
+ $.each(errors, function (index, value) {
1797
+ html += '<li>' + value + '</li>';
1798
+ });
1799
+ html += '</ul>';
1800
+
1801
+ return html;
1802
+ },
1803
+ },
1804
+
1805
+ formatter: {
1806
+ date: function (date) {
1807
+ return Intl.DateTimeFormat('en-GB').format(date);
1808
+ },
1809
+ datetime: function (date) {
1810
+ return Intl.DateTimeFormat('en-GB', {
1811
+ year: 'numeric',
1812
+ month: '2-digit',
1813
+ day: '2-digit',
1814
+ hour: '2-digit',
1815
+ minute: '2-digit',
1816
+ second: '2-digit',
1817
+ }).format(date);
1818
+ },
1819
+ time: function (date) {
1820
+ return Intl.DateTimeFormat('en-GB', {
1821
+ hour: '2-digit',
1822
+ minute: '2-digit',
1823
+ second: '2-digit',
1824
+ }).format(date);
1825
+ },
1826
+ month: function (date) {
1827
+ return Intl.DateTimeFormat('en-GB', {
1828
+ month: '2-digit',
1829
+ year: 'numeric',
1830
+ }).format(date);
1831
+ },
1832
+ year: function (date) {
1833
+ return Intl.DateTimeFormat('en-GB', {
1834
+ year: 'numeric',
1835
+ }).format(date);
1836
+ },
1837
+ },
1838
+
1839
+ rules: {
1840
+
1841
+ // is not empty or blank string
1842
+ notEmpty: function (value) {
1843
+ return !(value === undefined || value === '' || (Array.isArray(value) && value.length === 0));
1844
+ },
1845
+
1846
+ /* Deprecated */
1847
+ empty: function (value) {
1848
+ return $.fn.form.settings.rules.notEmpty(value);
1849
+ },
1850
+
1851
+ // checkbox checked
1852
+ checked: function () {
1853
+ return $(this).filter(':checked').length > 0;
1854
+ },
1855
+
1856
+ // is most likely an email
1857
+ email: function (value) {
1858
+ return $.fn.form.settings.regExp.email.test(value);
1859
+ },
1860
+
1861
+ // value is most likely url
1862
+ url: function (value) {
1863
+ return $.fn.form.settings.regExp.url.test(value);
1864
+ },
1865
+
1866
+ // matches specified regExp
1867
+ regExp: function (value, regExp) {
1868
+ if (regExp instanceof RegExp) {
1869
+ return value.match(regExp);
1870
+ }
1871
+ var
1872
+ regExpParts = regExp.match($.fn.form.settings.regExp.flags),
1873
+ flags
1874
+ ;
1875
+ // regular expression specified as /baz/gi (flags)
1876
+ if (regExpParts) {
1877
+ regExp = regExpParts.length >= 2
1878
+ ? regExpParts[1]
1879
+ : regExp;
1880
+ flags = regExpParts.length >= 3
1881
+ ? regExpParts[2]
1882
+ : '';
1883
+ }
1884
+
1885
+ return value.match(new RegExp(regExp, flags));
1886
+ },
1887
+ minValue: function (value, range) {
1888
+ return $.fn.form.settings.rules.range(value, range + '..', 'number');
1889
+ },
1890
+ maxValue: function (value, range) {
1891
+ return $.fn.form.settings.rules.range(value, '..' + range, 'number');
1892
+ },
1893
+ // is valid integer or matches range
1894
+ integer: function (value, range) {
1895
+ return $.fn.form.settings.rules.range(value, range, 'integer');
1896
+ },
1897
+ range: function (value, range, regExp, testLength) {
1898
+ if (typeof regExp === 'string') {
1899
+ regExp = $.fn.form.settings.regExp[regExp];
1900
+ }
1901
+ if (!(regExp instanceof RegExp)) {
1902
+ regExp = $.fn.form.settings.regExp.integer;
1903
+ }
1904
+ var
1905
+ min,
1906
+ max,
1907
+ parts
1908
+ ;
1909
+ if (!range || ['', '..'].indexOf(range) !== -1) {
1910
+
1911
+ // do nothing
1912
+ } else if (range.indexOf('..') === -1) {
1913
+ if (regExp.test(range)) {
1914
+ min = range - 0;
1915
+ max = min;
1916
+ }
1917
+ } else {
1918
+ parts = range.split('..', 2);
1919
+ if (regExp.test(parts[0])) {
1920
+ min = parts[0] - 0;
1921
+ }
1922
+ if (regExp.test(parts[1])) {
1923
+ max = parts[1] - 0;
1924
+ }
1925
+ }
1926
+ if (testLength) {
1927
+ value = value.length;
1928
+ }
1929
+
1930
+ return (
1931
+ regExp.test(value)
1932
+ && (min === undefined || value >= min)
1933
+ && (max === undefined || value <= max)
1934
+ );
1935
+ },
1936
+
1937
+ // is valid number (with decimal)
1938
+ decimal: function (value, range) {
1939
+ return $.fn.form.settings.rules.range(value, range, 'decimal');
1940
+ },
1941
+
1942
+ // is valid number
1943
+ number: function (value, range) {
1944
+ return $.fn.form.settings.rules.range(value, range, 'number');
1945
+ },
1946
+
1947
+ // is value (case insensitive)
1948
+ is: function (value, text) {
1949
+ text = typeof text === 'string'
1950
+ ? text.toLowerCase()
1951
+ : text;
1952
+ value = typeof value === 'string'
1953
+ ? value.toLowerCase()
1954
+ : value;
1955
+
1956
+ return value == text;
1957
+ },
1958
+
1959
+ // is value
1960
+ isExactly: function (value, text) {
1961
+ return value == text;
1962
+ },
1963
+
1964
+ // value is not another value (case insensitive)
1965
+ not: function (value, notValue) {
1966
+ value = typeof value === 'string'
1967
+ ? value.toLowerCase()
1968
+ : value;
1969
+ notValue = typeof notValue === 'string'
1970
+ ? notValue.toLowerCase()
1971
+ : notValue;
1972
+
1973
+ return value != notValue;
1974
+ },
1975
+
1976
+ // value is not another value (case sensitive)
1977
+ notExactly: function (value, notValue) {
1978
+ return value != notValue;
1979
+ },
1980
+
1981
+ // value contains text (insensitive)
1982
+ contains: function (value, text) {
1983
+ // escape regex characters
1984
+ text = text.replace($.fn.form.settings.regExp.escape, '\\$&');
1985
+
1986
+ return value.search(new RegExp(text, 'i')) !== -1;
1987
+ },
1988
+
1989
+ // value contains text (case sensitive)
1990
+ containsExactly: function (value, text) {
1991
+ // escape regex characters
1992
+ text = text.replace($.fn.form.settings.regExp.escape, '\\$&');
1993
+
1994
+ return value.search(new RegExp(text)) !== -1;
1995
+ },
1996
+
1997
+ // value contains text (insensitive)
1998
+ doesntContain: function (value, text) {
1999
+ // escape regex characters
2000
+ text = text.replace($.fn.form.settings.regExp.escape, '\\$&');
2001
+
2002
+ return value.search(new RegExp(text, 'i')) === -1;
2003
+ },
2004
+
2005
+ // value contains text (case sensitive)
2006
+ doesntContainExactly: function (value, text) {
2007
+ // escape regex characters
2008
+ text = text.replace($.fn.form.settings.regExp.escape, '\\$&');
2009
+
2010
+ return value.search(new RegExp(text)) === -1;
2011
+ },
2012
+
2013
+ // is at least string length
2014
+ minLength: function (value, minLength) {
2015
+ return $.fn.form.settings.rules.range(value, minLength + '..', 'integer', true);
2016
+ },
2017
+
2018
+ // is exactly length
2019
+ exactLength: function (value, requiredLength) {
2020
+ return $.fn.form.settings.rules.range(value, requiredLength + '..' + requiredLength, 'integer', true);
2021
+ },
2022
+
2023
+ // is less than length
2024
+ maxLength: function (value, maxLength) {
2025
+ return $.fn.form.settings.rules.range(value, '..' + maxLength, 'integer', true);
2026
+ },
2027
+
2028
+ size: function (value, range) {
2029
+ return $.fn.form.settings.rules.range(value, range, 'integer', true);
2030
+ },
2031
+
2032
+ // matches another field
2033
+ match: function (value, identifier, module) {
2034
+ var matchingValue = module.get.value(identifier, true);
2035
+
2036
+ return matchingValue !== undefined
2037
+ ? value.toString() === matchingValue.toString()
2038
+ : false;
2039
+ },
2040
+
2041
+ // different than another field
2042
+ different: function (value, identifier, module) {
2043
+ var matchingValue = module.get.value(identifier, true);
2044
+
2045
+ return matchingValue !== undefined
2046
+ ? value.toString() !== matchingValue.toString()
2047
+ : false;
2048
+ },
2049
+
2050
+ creditCard: function (cardNumber, cardTypes) {
2051
+ var
2052
+ cards = {
2053
+ visa: {
2054
+ pattern: /^4/,
2055
+ length: [16],
2056
+ },
2057
+ amex: {
2058
+ pattern: /^3[47]/,
2059
+ length: [15],
2060
+ },
2061
+ mastercard: {
2062
+ pattern: /^5[1-5]/,
2063
+ length: [16],
2064
+ },
2065
+ discover: {
2066
+ pattern: /^(6011|622(12[6-9]|1[3-9]\d|[2-8]\d{2}|9[01]\d|92[0-5]|64[4-9])|65)/,
2067
+ length: [16],
2068
+ },
2069
+ unionPay: {
2070
+ pattern: /^(62|88)/,
2071
+ length: [16, 17, 18, 19],
2072
+ },
2073
+ jcb: {
2074
+ pattern: /^35(2[89]|[3-8]\d)/,
2075
+ length: [16],
2076
+ },
2077
+ maestro: {
2078
+ pattern: /^(5018|5020|5038|6304|6759|676[1-3])/,
2079
+ length: [12, 13, 14, 15, 16, 17, 18, 19],
2080
+ },
2081
+ dinersClub: {
2082
+ pattern: /^(30[0-5]|^36)/,
2083
+ length: [14],
2084
+ },
2085
+ laser: {
2086
+ pattern: /^(6304|670[69]|6771)/,
2087
+ length: [16, 17, 18, 19],
2088
+ },
2089
+ visaElectron: {
2090
+ pattern: /^(4026|417500|4508|4844|491(3|7))/,
2091
+ length: [16],
2092
+ },
2093
+ },
2094
+ valid = {},
2095
+ validCard = false,
2096
+ requiredTypes = typeof cardTypes === 'string'
2097
+ ? cardTypes.split(',')
2098
+ : false,
2099
+ unionPay,
2100
+ validation
2101
+ ;
2102
+
2103
+ if (typeof cardNumber !== 'string' || cardNumber.length === 0) {
2104
+ return;
2105
+ }
2106
+
2107
+ // allow dashes and spaces in card
2108
+ cardNumber = cardNumber.replace(/[\s-]/g, '');
2109
+
2110
+ // verify card types
2111
+ if (requiredTypes) {
2112
+ $.each(requiredTypes, function (index, type) {
2113
+ // verify each card type
2114
+ validation = cards[type];
2115
+ if (validation) {
2116
+ valid = {
2117
+ length: $.inArray(cardNumber.length, validation.length) !== -1,
2118
+ pattern: cardNumber.search(validation.pattern) !== -1,
2119
+ };
2120
+ if (valid.length > 0 && valid.pattern) {
2121
+ validCard = true;
2122
+ }
2123
+ }
2124
+ });
2125
+
2126
+ if (!validCard) {
2127
+ return false;
2128
+ }
2129
+ }
2130
+
2131
+ // skip luhn for UnionPay
2132
+ unionPay = {
2133
+ number: $.inArray(cardNumber.length, cards.unionPay.length) !== -1,
2134
+ pattern: cardNumber.search(cards.unionPay.pattern) !== -1,
2135
+ };
2136
+ if (unionPay.number && unionPay.pattern) {
2137
+ return true;
2138
+ }
2139
+
2140
+ // verify luhn, adapted from <https://gist.github.com/2134376>
2141
+ var
2142
+ length = cardNumber.length,
2143
+ multiple = 0,
2144
+ producedValue = [
2145
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
2146
+ [0, 2, 4, 6, 8, 1, 3, 5, 7, 9],
2147
+ ],
2148
+ sum = 0
2149
+ ;
2150
+ while (length--) {
2151
+ sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
2152
+ multiple ^= 1; // eslint-disable-line no-bitwise
2153
+ }
2154
+
2155
+ return sum % 10 === 0 && sum > 0;
2156
+ },
2157
+
2158
+ minCount: function (value, minCount) {
2159
+ minCount = Number(minCount);
2160
+
2161
+ if (minCount === 0) {
2162
+ return true;
2163
+ }
2164
+ if (minCount === 1) {
2165
+ return value !== '';
2166
+ }
2167
+
2168
+ return value.split(',').length >= minCount;
2169
+ },
2170
+
2171
+ exactCount: function (value, exactCount) {
2172
+ exactCount = Number(exactCount);
2173
+
2174
+ if (exactCount === 0) {
2175
+ return value === '';
2176
+ }
2177
+ if (exactCount === 1) {
2178
+ return value !== '' && value.search(',') === -1;
2179
+ }
2180
+
2181
+ return value.split(',').length === exactCount;
2182
+ },
2183
+
2184
+ maxCount: function (value, maxCount) {
2185
+ maxCount = Number(maxCount);
2186
+
2187
+ if (maxCount === 0) {
2188
+ return false;
2189
+ }
2190
+ if (maxCount === 1) {
2191
+ return value.search(',') === -1;
2192
+ }
2193
+
2194
+ return value.split(',').length <= maxCount;
2195
+ },
2196
+ },
2197
+
2198
+ };
2199
+ })(jQuery, window, document);