rails-active-ui 0.3.0 → 0.3.2

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 (197) hide show
  1. checksums.yaml +4 -4
  2. data/app/javascript/ui/controllers/turbo_modal_controller.js +36 -0
  3. data/config/importmap.rb +1 -0
  4. data/formantic-ui/components/accordion.css +369 -0
  5. data/formantic-ui/components/accordion.js +595 -0
  6. data/formantic-ui/components/accordion.min.css +9 -0
  7. data/formantic-ui/components/accordion.min.js +11 -0
  8. data/formantic-ui/components/ad.css +264 -0
  9. data/formantic-ui/components/ad.min.css +10 -0
  10. data/formantic-ui/components/api.js +1225 -0
  11. data/formantic-ui/components/api.min.js +11 -0
  12. data/formantic-ui/components/breadcrumb.css +135 -0
  13. data/formantic-ui/components/breadcrumb.min.css +9 -0
  14. data/formantic-ui/components/button.css +4058 -0
  15. data/formantic-ui/components/button.min.css +9 -0
  16. data/formantic-ui/components/calendar.css +327 -0
  17. data/formantic-ui/components/calendar.js +2045 -0
  18. data/formantic-ui/components/calendar.min.css +9 -0
  19. data/formantic-ui/components/calendar.min.js +11 -0
  20. data/formantic-ui/components/card.css +1881 -0
  21. data/formantic-ui/components/card.min.css +9 -0
  22. data/formantic-ui/components/checkbox.css +785 -0
  23. data/formantic-ui/components/checkbox.js +888 -0
  24. data/formantic-ui/components/checkbox.min.css +9 -0
  25. data/formantic-ui/components/checkbox.min.js +11 -0
  26. data/formantic-ui/components/comment.css +283 -0
  27. data/formantic-ui/components/comment.min.css +9 -0
  28. data/formantic-ui/components/container.css +300 -0
  29. data/formantic-ui/components/container.min.css +9 -0
  30. data/formantic-ui/components/dimmer.css +367 -0
  31. data/formantic-ui/components/dimmer.js +732 -0
  32. data/formantic-ui/components/dimmer.min.css +9 -0
  33. data/formantic-ui/components/dimmer.min.js +11 -0
  34. data/formantic-ui/components/divider.css +287 -0
  35. data/formantic-ui/components/divider.min.css +9 -0
  36. data/formantic-ui/components/dropdown.css +2087 -0
  37. data/formantic-ui/components/dropdown.js +4432 -0
  38. data/formantic-ui/components/dropdown.min.css +9 -0
  39. data/formantic-ui/components/dropdown.min.js +11 -0
  40. data/formantic-ui/components/embed.css +155 -0
  41. data/formantic-ui/components/embed.js +688 -0
  42. data/formantic-ui/components/embed.min.css +9 -0
  43. data/formantic-ui/components/embed.min.js +11 -0
  44. data/formantic-ui/components/emoji.css +15311 -0
  45. data/formantic-ui/components/emoji.min.css +9 -0
  46. data/formantic-ui/components/feed.css +799 -0
  47. data/formantic-ui/components/feed.min.css +9 -0
  48. data/formantic-ui/components/flag.css +1149 -0
  49. data/formantic-ui/components/flag.min.css +9 -0
  50. data/formantic-ui/components/flyout.css +546 -0
  51. data/formantic-ui/components/flyout.js +1551 -0
  52. data/formantic-ui/components/flyout.min.css +9 -0
  53. data/formantic-ui/components/flyout.min.js +11 -0
  54. data/formantic-ui/components/form.css +1885 -0
  55. data/formantic-ui/components/form.js +2199 -0
  56. data/formantic-ui/components/form.min.css +9 -0
  57. data/formantic-ui/components/form.min.js +11 -0
  58. data/formantic-ui/components/grid.css +1952 -0
  59. data/formantic-ui/components/grid.min.css +9 -0
  60. data/formantic-ui/components/header.css +778 -0
  61. data/formantic-ui/components/header.min.css +9 -0
  62. data/formantic-ui/components/icon.css +7066 -0
  63. data/formantic-ui/components/icon.min.css +9 -0
  64. data/formantic-ui/components/image.css +315 -0
  65. data/formantic-ui/components/image.min.css +9 -0
  66. data/formantic-ui/components/input.css +1566 -0
  67. data/formantic-ui/components/input.min.css +9 -0
  68. data/formantic-ui/components/item.css +534 -0
  69. data/formantic-ui/components/item.min.css +9 -0
  70. data/formantic-ui/components/label.css +2114 -0
  71. data/formantic-ui/components/label.min.css +9 -0
  72. data/formantic-ui/components/list.css +955 -0
  73. data/formantic-ui/components/list.min.css +9 -0
  74. data/formantic-ui/components/loader.css +787 -0
  75. data/formantic-ui/components/loader.min.css +9 -0
  76. data/formantic-ui/components/menu.css +2131 -0
  77. data/formantic-ui/components/menu.min.css +9 -0
  78. data/formantic-ui/components/message.css +619 -0
  79. data/formantic-ui/components/message.min.css +9 -0
  80. data/formantic-ui/components/modal.css +779 -0
  81. data/formantic-ui/components/modal.js +1637 -0
  82. data/formantic-ui/components/modal.min.css +9 -0
  83. data/formantic-ui/components/modal.min.js +11 -0
  84. data/formantic-ui/components/nag.css +290 -0
  85. data/formantic-ui/components/nag.js +566 -0
  86. data/formantic-ui/components/nag.min.css +9 -0
  87. data/formantic-ui/components/nag.min.js +11 -0
  88. data/formantic-ui/components/placeholder.css +228 -0
  89. data/formantic-ui/components/placeholder.min.css +9 -0
  90. data/formantic-ui/components/popup.css +1184 -0
  91. data/formantic-ui/components/popup.js +1561 -0
  92. data/formantic-ui/components/popup.min.css +9 -0
  93. data/formantic-ui/components/popup.min.js +11 -0
  94. data/formantic-ui/components/progress.css +761 -0
  95. data/formantic-ui/components/progress.js +979 -0
  96. data/formantic-ui/components/progress.min.css +9 -0
  97. data/formantic-ui/components/progress.min.js +11 -0
  98. data/formantic-ui/components/rail.css +147 -0
  99. data/formantic-ui/components/rail.min.css +9 -0
  100. data/formantic-ui/components/rating.css +414 -0
  101. data/formantic-ui/components/rating.js +540 -0
  102. data/formantic-ui/components/rating.min.css +9 -0
  103. data/formantic-ui/components/rating.min.js +11 -0
  104. data/formantic-ui/components/reset.css +386 -0
  105. data/formantic-ui/components/reset.min.css +9 -0
  106. data/formantic-ui/components/reveal.css +277 -0
  107. data/formantic-ui/components/reveal.min.css +9 -0
  108. data/formantic-ui/components/search.css +541 -0
  109. data/formantic-ui/components/search.js +1641 -0
  110. data/formantic-ui/components/search.min.css +9 -0
  111. data/formantic-ui/components/search.min.js +11 -0
  112. data/formantic-ui/components/segment.css +1053 -0
  113. data/formantic-ui/components/segment.min.css +9 -0
  114. data/formantic-ui/components/shape.css +144 -0
  115. data/formantic-ui/components/shape.js +797 -0
  116. data/formantic-ui/components/shape.min.css +9 -0
  117. data/formantic-ui/components/shape.min.js +11 -0
  118. data/formantic-ui/components/sidebar.css +539 -0
  119. data/formantic-ui/components/sidebar.js +1054 -0
  120. data/formantic-ui/components/sidebar.min.css +9 -0
  121. data/formantic-ui/components/sidebar.min.js +11 -0
  122. data/formantic-ui/components/site.css +286 -0
  123. data/formantic-ui/components/site.js +455 -0
  124. data/formantic-ui/components/site.min.css +9 -0
  125. data/formantic-ui/components/site.min.js +11 -0
  126. data/formantic-ui/components/slider.css +926 -0
  127. data/formantic-ui/components/slider.js +1546 -0
  128. data/formantic-ui/components/slider.min.css +9 -0
  129. data/formantic-ui/components/slider.min.js +11 -0
  130. data/formantic-ui/components/state.js +697 -0
  131. data/formantic-ui/components/state.min.js +11 -0
  132. data/formantic-ui/components/statistic.css +586 -0
  133. data/formantic-ui/components/statistic.min.css +9 -0
  134. data/formantic-ui/components/step.css +1538 -0
  135. data/formantic-ui/components/step.min.css +9 -0
  136. data/formantic-ui/components/sticky.css +73 -0
  137. data/formantic-ui/components/sticky.js +917 -0
  138. data/formantic-ui/components/sticky.min.css +9 -0
  139. data/formantic-ui/components/sticky.min.js +11 -0
  140. data/formantic-ui/components/tab.css +84 -0
  141. data/formantic-ui/components/tab.js +967 -0
  142. data/formantic-ui/components/tab.min.css +9 -0
  143. data/formantic-ui/components/tab.min.js +11 -0
  144. data/formantic-ui/components/table.css +3473 -0
  145. data/formantic-ui/components/table.min.css +9 -0
  146. data/formantic-ui/components/text.css +155 -0
  147. data/formantic-ui/components/text.min.css +9 -0
  148. data/formantic-ui/components/toast.css +751 -0
  149. data/formantic-ui/components/toast.js +964 -0
  150. data/formantic-ui/components/toast.min.css +9 -0
  151. data/formantic-ui/components/toast.min.js +11 -0
  152. data/formantic-ui/components/transition.css +1148 -0
  153. data/formantic-ui/components/transition.js +1034 -0
  154. data/formantic-ui/components/transition.min.css +9 -0
  155. data/formantic-ui/components/transition.min.js +11 -0
  156. data/formantic-ui/components/visibility.js +1292 -0
  157. data/formantic-ui/components/visibility.min.js +11 -0
  158. data/formantic-ui/semantic.css +78485 -0
  159. data/formantic-ui/semantic.js +31036 -0
  160. data/formantic-ui/semantic.min.css +11 -0
  161. data/formantic-ui/semantic.min.js +11 -0
  162. data/formantic-ui/themes/basic/assets/fonts/LICENSE.txt +91 -0
  163. data/formantic-ui/themes/basic/assets/fonts/icons.woff +0 -0
  164. data/formantic-ui/themes/basic/assets/fonts/icons.woff2 +0 -0
  165. data/formantic-ui/themes/default/assets/fonts/LICENSE_Lato.txt +94 -0
  166. data/formantic-ui/themes/default/assets/fonts/LICENSE_icons.txt +165 -0
  167. data/formantic-ui/themes/default/assets/fonts/Lato-Bold.woff +0 -0
  168. data/formantic-ui/themes/default/assets/fonts/Lato-Bold.woff2 +0 -0
  169. data/formantic-ui/themes/default/assets/fonts/Lato-BoldItalic.woff +0 -0
  170. data/formantic-ui/themes/default/assets/fonts/Lato-BoldItalic.woff2 +0 -0
  171. data/formantic-ui/themes/default/assets/fonts/Lato-Italic.woff +0 -0
  172. data/formantic-ui/themes/default/assets/fonts/Lato-Italic.woff2 +0 -0
  173. data/formantic-ui/themes/default/assets/fonts/Lato-Regular.woff +0 -0
  174. data/formantic-ui/themes/default/assets/fonts/Lato-Regular.woff2 +0 -0
  175. data/formantic-ui/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  176. data/formantic-ui/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  177. data/formantic-ui/themes/default/assets/fonts/LatoLatin-BoldItalic.woff +0 -0
  178. data/formantic-ui/themes/default/assets/fonts/LatoLatin-BoldItalic.woff2 +0 -0
  179. data/formantic-ui/themes/default/assets/fonts/LatoLatin-Italic.woff +0 -0
  180. data/formantic-ui/themes/default/assets/fonts/LatoLatin-Italic.woff2 +0 -0
  181. data/formantic-ui/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  182. data/formantic-ui/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  183. data/formantic-ui/themes/default/assets/fonts/brand-icons.woff +0 -0
  184. data/formantic-ui/themes/default/assets/fonts/brand-icons.woff2 +0 -0
  185. data/formantic-ui/themes/default/assets/fonts/icons.woff +0 -0
  186. data/formantic-ui/themes/default/assets/fonts/icons.woff2 +0 -0
  187. data/formantic-ui/themes/default/assets/fonts/outline-icons.woff +0 -0
  188. data/formantic-ui/themes/default/assets/fonts/outline-icons.woff2 +0 -0
  189. data/formantic-ui/themes/famfamfam/assets/images/flags.png +0 -0
  190. data/formantic-ui/themes/github/assets/fonts/LICENSE.txt +94 -0
  191. data/formantic-ui/themes/github/assets/fonts/octicons.woff +0 -0
  192. data/formantic-ui/themes/github/assets/fonts/octicons.woff2 +0 -0
  193. data/formantic-ui/themes/material/assets/fonts/LICENSE.txt +202 -0
  194. data/formantic-ui/themes/material/assets/fonts/icons.woff +0 -0
  195. data/formantic-ui/themes/material/assets/fonts/icons.woff2 +0 -0
  196. data/lib/ui/version.rb +1 -1
  197. metadata +194 -1
@@ -0,0 +1,2199 @@
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);