fomantic-ui-sass 2.9.0 → 2.9.2

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