fomantic-ui 2.9.1-beta.8 → 2.9.1

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