@ntlab/ntjs-assets 2.0.2 → 2.0.3

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