fomantic-ui-sass 2.9.0 → 2.9.1

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