fomantic-ui 2.9.1-beta.3 → 2.9.1-beta.30

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