fomantic-ui-sass 2.8.8.1 → 2.9.1

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