fomantic-ui 2.9.1-beta.2 → 2.9.1-beta.20

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 (339) hide show
  1. package/.eslintrc.js +110 -0
  2. package/.github/workflows/ci.yml +14 -4
  3. package/.stylelintrc.js +46 -0
  4. package/dist/components/accordion.css +19 -26
  5. package/dist/components/accordion.js +568 -595
  6. package/dist/components/accordion.min.css +1 -1
  7. package/dist/components/accordion.min.js +2 -2
  8. package/dist/components/ad.css +31 -41
  9. package/dist/components/ad.min.css +1 -1
  10. package/dist/components/api.js +1161 -1179
  11. package/dist/components/api.min.js +2 -2
  12. package/dist/components/breadcrumb.css +1 -1
  13. package/dist/components/breadcrumb.min.css +1 -1
  14. package/dist/components/button.css +71 -106
  15. package/dist/components/button.min.css +1 -1
  16. package/dist/components/calendar.css +18 -16
  17. package/dist/components/calendar.js +1895 -1809
  18. package/dist/components/calendar.min.css +2 -2
  19. package/dist/components/calendar.min.js +2 -2
  20. package/dist/components/card.css +83 -115
  21. package/dist/components/card.min.css +1 -1
  22. package/dist/components/checkbox.css +47 -64
  23. package/dist/components/checkbox.js +842 -841
  24. package/dist/components/checkbox.min.css +1 -1
  25. package/dist/components/checkbox.min.js +2 -2
  26. package/dist/components/comment.css +37 -51
  27. package/dist/components/comment.min.css +1 -1
  28. package/dist/components/container.css +6 -5
  29. package/dist/components/container.min.css +2 -2
  30. package/dist/components/dimmer.css +31 -41
  31. package/dist/components/dimmer.js +708 -737
  32. package/dist/components/dimmer.min.css +1 -1
  33. package/dist/components/dimmer.min.js +2 -2
  34. package/dist/components/divider.css +32 -44
  35. package/dist/components/divider.min.css +1 -1
  36. package/dist/components/dropdown.css +129 -166
  37. package/dist/components/dropdown.js +4161 -4234
  38. package/dist/components/dropdown.min.css +2 -2
  39. package/dist/components/dropdown.min.js +2 -2
  40. package/dist/components/embed.css +13 -19
  41. package/dist/components/embed.js +651 -675
  42. package/dist/components/embed.min.css +1 -1
  43. package/dist/components/embed.min.js +2 -2
  44. package/dist/components/emoji.css +7 -6
  45. package/dist/components/feed.css +24 -32
  46. package/dist/components/feed.min.css +1 -1
  47. package/dist/components/flag.css +5 -3
  48. package/dist/components/flag.min.css +1 -1
  49. package/dist/components/flyout.css +44 -58
  50. package/dist/components/flyout.js +1464 -1465
  51. package/dist/components/flyout.min.css +2 -2
  52. package/dist/components/flyout.min.js +2 -2
  53. package/dist/components/form.css +85 -111
  54. package/dist/components/form.js +1979 -2004
  55. package/dist/components/form.min.css +1 -1
  56. package/dist/components/form.min.js +2 -2
  57. package/dist/components/grid.css +70 -96
  58. package/dist/components/grid.min.css +1 -1
  59. package/dist/components/header.css +47 -65
  60. package/dist/components/header.min.css +1 -1
  61. package/dist/components/icon.css +45 -66
  62. package/dist/components/icon.min.css +2 -2
  63. package/dist/components/image.css +32 -42
  64. package/dist/components/image.min.css +1 -1
  65. package/dist/components/input.css +182 -65
  66. package/dist/components/input.min.css +2 -2
  67. package/dist/components/item.css +55 -77
  68. package/dist/components/item.min.css +1 -1
  69. package/dist/components/label.css +68 -91
  70. package/dist/components/label.min.css +2 -2
  71. package/dist/components/list.css +44 -59
  72. package/dist/components/list.min.css +1 -1
  73. package/dist/components/loader.css +16 -22
  74. package/dist/components/loader.min.css +1 -1
  75. package/dist/components/menu.css +126 -186
  76. package/dist/components/message.css +25 -35
  77. package/dist/components/message.min.css +1 -1
  78. package/dist/components/modal.css +39 -40
  79. package/dist/components/modal.js +1491 -1485
  80. package/dist/components/modal.min.css +2 -2
  81. package/dist/components/modal.min.js +2 -2
  82. package/dist/components/nag.css +21 -28
  83. package/dist/components/nag.js +518 -526
  84. package/dist/components/nag.min.css +2 -2
  85. package/dist/components/nag.min.js +2 -2
  86. package/dist/components/placeholder.css +10 -12
  87. package/dist/components/placeholder.min.css +1 -1
  88. package/dist/components/popup.css +352 -59
  89. package/dist/components/popup.js +1437 -1456
  90. package/dist/components/popup.min.css +2 -2
  91. package/dist/components/popup.min.js +2 -2
  92. package/dist/components/progress.css +29 -39
  93. package/dist/components/progress.js +969 -997
  94. package/dist/components/progress.min.css +1 -1
  95. package/dist/components/progress.min.js +2 -2
  96. package/dist/components/rail.css +15 -20
  97. package/dist/components/rail.min.css +1 -1
  98. package/dist/components/rating.css +9 -13
  99. package/dist/components/rating.js +505 -523
  100. package/dist/components/rating.min.css +1 -1
  101. package/dist/components/rating.min.js +2 -2
  102. package/dist/components/reset.css +1 -1
  103. package/dist/components/reset.min.css +1 -1
  104. package/dist/components/reveal.css +19 -26
  105. package/dist/components/reveal.min.css +1 -1
  106. package/dist/components/search.css +43 -58
  107. package/dist/components/search.js +1498 -1534
  108. package/dist/components/search.min.css +2 -2
  109. package/dist/components/search.min.js +2 -2
  110. package/dist/components/segment.css +64 -83
  111. package/dist/components/segment.min.css +2 -2
  112. package/dist/components/shape.css +10 -14
  113. package/dist/components/shape.js +792 -809
  114. package/dist/components/shape.min.css +1 -1
  115. package/dist/components/shape.min.js +2 -2
  116. package/dist/components/sidebar.css +43 -58
  117. package/dist/components/sidebar.js +1071 -1098
  118. package/dist/components/sidebar.min.css +2 -2
  119. package/dist/components/sidebar.min.js +2 -2
  120. package/dist/components/site.css +5 -5
  121. package/dist/components/site.js +462 -476
  122. package/dist/components/site.min.css +1 -1
  123. package/dist/components/site.min.js +2 -2
  124. package/dist/components/slider.css +27 -37
  125. package/dist/components/slider.js +1287 -1306
  126. package/dist/components/slider.min.js +2 -2
  127. package/dist/components/state.js +639 -657
  128. package/dist/components/state.min.js +2 -2
  129. package/dist/components/statistic.css +32 -41
  130. package/dist/components/statistic.min.css +2 -2
  131. package/dist/components/step.css +26 -35
  132. package/dist/components/step.min.css +1 -1
  133. package/dist/components/sticky.css +1 -1
  134. package/dist/components/sticky.js +857 -902
  135. package/dist/components/sticky.min.css +1 -1
  136. package/dist/components/sticky.min.js +2 -2
  137. package/dist/components/tab.css +6 -8
  138. package/dist/components/tab.js +922 -963
  139. package/dist/components/tab.min.css +1 -1
  140. package/dist/components/tab.min.js +2 -2
  141. package/dist/components/table.css +93 -119
  142. package/dist/components/table.min.css +2 -2
  143. package/dist/components/text.css +1 -1
  144. package/dist/components/text.min.css +1 -1
  145. package/dist/components/toast.css +4 -6
  146. package/dist/components/toast.js +886 -887
  147. package/dist/components/toast.min.css +1 -1
  148. package/dist/components/toast.min.js +2 -2
  149. package/dist/components/transition.css +150 -3
  150. package/dist/components/transition.js +1041 -1077
  151. package/dist/components/transition.min.css +2 -2
  152. package/dist/components/transition.min.js +2 -2
  153. package/dist/components/visibility.js +1220 -1244
  154. package/dist/components/visibility.min.js +2 -2
  155. package/dist/semantic.css +2529 -1778
  156. package/dist/semantic.js +28928 -29383
  157. package/dist/semantic.min.css +2 -2
  158. package/dist/semantic.min.js +2 -2
  159. package/dist/themes/default/assets/fonts/Lato-Bold.woff +0 -0
  160. package/dist/themes/default/assets/fonts/Lato-Bold.woff2 +0 -0
  161. package/dist/themes/default/assets/fonts/Lato-BoldItalic.woff +0 -0
  162. package/dist/themes/default/assets/fonts/Lato-BoldItalic.woff2 +0 -0
  163. package/dist/themes/default/assets/fonts/Lato-Italic.woff +0 -0
  164. package/dist/themes/default/assets/fonts/Lato-Italic.woff2 +0 -0
  165. package/dist/themes/default/assets/fonts/Lato-Regular.woff +0 -0
  166. package/dist/themes/default/assets/fonts/Lato-Regular.woff2 +0 -0
  167. package/dist/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  168. package/dist/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  169. package/dist/themes/default/assets/fonts/LatoLatin-BoldItalic.woff +0 -0
  170. package/dist/themes/default/assets/fonts/LatoLatin-BoldItalic.woff2 +0 -0
  171. package/dist/themes/default/assets/fonts/LatoLatin-Italic.woff +0 -0
  172. package/dist/themes/default/assets/fonts/LatoLatin-Italic.woff2 +0 -0
  173. package/dist/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  174. package/dist/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  175. package/examples/assets/show-examples.js +13 -13
  176. package/gulpfile.js +9 -10
  177. package/package.json +13 -8
  178. package/scripts/nightly-version.js +81 -75
  179. package/src/definitions/behaviors/api.js +1162 -1180
  180. package/src/definitions/behaviors/form.js +1978 -2003
  181. package/src/definitions/behaviors/state.js +645 -663
  182. package/src/definitions/behaviors/visibility.js +1219 -1243
  183. package/src/definitions/collections/breadcrumb.less +41 -44
  184. package/src/definitions/collections/form.less +869 -879
  185. package/src/definitions/collections/grid.less +1690 -1695
  186. package/src/definitions/collections/menu.less +1493 -1503
  187. package/src/definitions/collections/message.less +292 -295
  188. package/src/definitions/collections/table.less +1616 -1620
  189. package/src/definitions/elements/button.less +1721 -1743
  190. package/src/definitions/elements/container.less +209 -209
  191. package/src/definitions/elements/divider.less +205 -206
  192. package/src/definitions/elements/emoji.less +38 -44
  193. package/src/definitions/elements/flag.less +44 -46
  194. package/src/definitions/elements/header.less +337 -345
  195. package/src/definitions/elements/icon.less +516 -443
  196. package/src/definitions/elements/image.less +221 -225
  197. package/src/definitions/elements/input.less +663 -659
  198. package/src/definitions/elements/label.less +803 -793
  199. package/src/definitions/elements/list.less +809 -809
  200. package/src/definitions/elements/loader.less +272 -266
  201. package/src/definitions/elements/placeholder.less +171 -168
  202. package/src/definitions/elements/rail.less +91 -91
  203. package/src/definitions/elements/reveal.less +192 -196
  204. package/src/definitions/elements/segment.less +743 -746
  205. package/src/definitions/elements/step.less +425 -433
  206. package/src/definitions/elements/text.less +32 -34
  207. package/src/definitions/globals/reset.less +9 -6
  208. package/src/definitions/globals/site.js +461 -475
  209. package/src/definitions/globals/site.less +106 -108
  210. package/src/definitions/modules/accordion.js +567 -594
  211. package/src/definitions/modules/accordion.less +242 -246
  212. package/src/definitions/modules/calendar.js +1894 -1808
  213. package/src/definitions/modules/calendar.less +98 -95
  214. package/src/definitions/modules/checkbox.js +841 -840
  215. package/src/definitions/modules/checkbox.less +536 -542
  216. package/src/definitions/modules/dimmer.js +707 -736
  217. package/src/definitions/modules/dimmer.less +300 -297
  218. package/src/definitions/modules/dropdown.js +4160 -4233
  219. package/src/definitions/modules/dropdown.less +1577 -1589
  220. package/src/definitions/modules/embed.js +650 -674
  221. package/src/definitions/modules/embed.less +81 -82
  222. package/src/definitions/modules/flyout.js +1463 -1464
  223. package/src/definitions/modules/flyout.less +445 -451
  224. package/src/definitions/modules/modal.js +1490 -1484
  225. package/src/definitions/modules/modal.less +459 -459
  226. package/src/definitions/modules/nag.js +517 -525
  227. package/src/definitions/modules/nag.less +134 -138
  228. package/src/definitions/modules/popup.js +1436 -1455
  229. package/src/definitions/modules/popup.less +742 -687
  230. package/src/definitions/modules/progress.js +968 -996
  231. package/src/definitions/modules/progress.less +521 -494
  232. package/src/definitions/modules/rating.js +504 -522
  233. package/src/definitions/modules/rating.less +94 -101
  234. package/src/definitions/modules/search.js +1497 -1533
  235. package/src/definitions/modules/search.less +375 -382
  236. package/src/definitions/modules/shape.js +791 -808
  237. package/src/definitions/modules/shape.less +71 -76
  238. package/src/definitions/modules/sidebar.js +1070 -1097
  239. package/src/definitions/modules/sidebar.less +463 -472
  240. package/src/definitions/modules/slider.js +1286 -1305
  241. package/src/definitions/modules/slider.less +307 -308
  242. package/src/definitions/modules/sticky.js +873 -918
  243. package/src/definitions/modules/sticky.less +21 -23
  244. package/src/definitions/modules/tab.js +921 -962
  245. package/src/definitions/modules/tab.less +51 -52
  246. package/src/definitions/modules/toast.js +885 -886
  247. package/src/definitions/modules/toast.less +584 -586
  248. package/src/definitions/modules/transition.js +1040 -1076
  249. package/src/definitions/modules/transition.less +62 -28
  250. package/src/definitions/views/ad.less +206 -206
  251. package/src/definitions/views/card.less +968 -970
  252. package/src/definitions/views/comment.less +190 -198
  253. package/src/definitions/views/feed.less +220 -224
  254. package/src/definitions/views/item.less +436 -446
  255. package/src/definitions/views/statistic.less +271 -277
  256. package/src/theme.less +29 -32
  257. package/src/themes/default/assets/fonts/Lato-Bold.woff +0 -0
  258. package/src/themes/default/assets/fonts/Lato-Bold.woff2 +0 -0
  259. package/src/themes/default/assets/fonts/Lato-BoldItalic.woff +0 -0
  260. package/src/themes/default/assets/fonts/Lato-BoldItalic.woff2 +0 -0
  261. package/src/themes/default/assets/fonts/Lato-Italic.woff +0 -0
  262. package/src/themes/default/assets/fonts/Lato-Italic.woff2 +0 -0
  263. package/src/themes/default/assets/fonts/Lato-Regular.woff +0 -0
  264. package/src/themes/default/assets/fonts/Lato-Regular.woff2 +0 -0
  265. package/src/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  266. package/src/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  267. package/src/themes/default/assets/fonts/LatoLatin-BoldItalic.woff +0 -0
  268. package/src/themes/default/assets/fonts/LatoLatin-BoldItalic.woff2 +0 -0
  269. package/src/themes/default/assets/fonts/LatoLatin-Italic.woff +0 -0
  270. package/src/themes/default/assets/fonts/LatoLatin-Italic.woff2 +0 -0
  271. package/src/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  272. package/src/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  273. package/src/themes/default/elements/container.variables +0 -7
  274. package/src/themes/default/elements/icon.variables +18 -0
  275. package/src/themes/default/elements/segment.variables +0 -7
  276. package/src/themes/default/globals/colors.less +589 -589
  277. package/src/themes/default/globals/site.variables +8 -0
  278. package/src/themes/default/globals/variation.variables +13 -1
  279. package/src/themes/default/modules/calendar.variables +2 -0
  280. package/src/themes/default/modules/nag.variables +1 -1
  281. package/src/themes/default/modules/popup.variables +2 -0
  282. package/src/themes/default/modules/transition.variables +8 -1
  283. package/tasks/admin/components/create.js +274 -276
  284. package/tasks/admin/components/init.js +123 -130
  285. package/tasks/admin/components/update.js +149 -157
  286. package/tasks/admin/distributions/create.js +184 -187
  287. package/tasks/admin/distributions/init.js +123 -130
  288. package/tasks/admin/distributions/update.js +145 -152
  289. package/tasks/admin/publish.js +5 -7
  290. package/tasks/admin/register.js +36 -38
  291. package/tasks/admin/release.js +8 -10
  292. package/tasks/build/assets.js +42 -39
  293. package/tasks/build/css.js +225 -216
  294. package/tasks/build/javascript.js +118 -113
  295. package/tasks/build.js +10 -10
  296. package/tasks/check-install.js +14 -16
  297. package/tasks/clean.js +5 -5
  298. package/tasks/collections/admin.js +34 -36
  299. package/tasks/collections/build.js +18 -20
  300. package/tasks/collections/docs.js +9 -11
  301. package/tasks/collections/install.js +9 -11
  302. package/tasks/collections/rtl.js +9 -11
  303. package/tasks/collections/various.js +8 -10
  304. package/tasks/config/admin/github.js +17 -17
  305. package/tasks/config/admin/oauth.example.js +4 -4
  306. package/tasks/config/admin/release.js +98 -98
  307. package/tasks/config/admin/templates/component-package.js +9 -10
  308. package/tasks/config/admin/templates/css-package.js +18 -20
  309. package/tasks/config/admin/templates/less-package.js +11 -13
  310. package/tasks/config/defaults.js +116 -114
  311. package/tasks/config/docs.js +23 -23
  312. package/tasks/config/npm/gulpfile.js +8 -9
  313. package/tasks/config/project/config.js +127 -134
  314. package/tasks/config/project/install.js +715 -713
  315. package/tasks/config/project/release.js +32 -38
  316. package/tasks/config/tasks.js +165 -156
  317. package/tasks/config/user.js +23 -26
  318. package/tasks/docs/build.js +97 -95
  319. package/tasks/docs/metadata.js +90 -96
  320. package/tasks/docs/serve.js +80 -81
  321. package/tasks/install.js +370 -378
  322. package/tasks/rtl/build.js +2 -2
  323. package/tasks/rtl/watch.js +2 -2
  324. package/tasks/version.js +4 -4
  325. package/tasks/watch.js +28 -30
  326. package/test/meteor/assets.js +10 -13
  327. package/test/meteor/fonts.js +12 -13
  328. package/test/modules/accordion.spec.js +6 -8
  329. package/test/modules/checkbox.spec.js +5 -7
  330. package/test/modules/dropdown.spec.js +5 -7
  331. package/test/modules/modal.spec.js +6 -8
  332. package/test/modules/module.spec.js +158 -178
  333. package/test/modules/popup.spec.js +5 -7
  334. package/test/modules/search.spec.js +5 -7
  335. package/test/modules/shape.spec.js +5 -7
  336. package/test/modules/sidebar.spec.js +5 -7
  337. package/test/modules/tab.spec.js +6 -8
  338. package/test/modules/transition.spec.js +5 -7
  339. package/test/modules/video.spec.js +5 -7
@@ -8,1229 +8,1211 @@
8
8
  *
9
9
  */
10
10
 
11
- ;(function ($, window, document, undefined) {
12
-
13
- 'use strict';
14
-
15
- $.isWindow = $.isWindow || function(obj) {
16
- return obj != null && obj === obj.window;
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
- $.api = $.fn.api = function(parameters) {
27
-
28
- var
29
- // use window context if none specified
30
- $allModules = $.isFunction(this)
31
- ? $(window)
32
- : $(this),
33
- moduleSelector = $allModules.selector || '',
34
- time = new Date().getTime(),
35
- performance = [],
36
-
37
- query = arguments[0],
38
- methodInvoked = (typeof query == 'string'),
39
- queryArguments = [].slice.call(arguments, 1),
40
-
41
- returnedValue
42
- ;
43
-
44
- $allModules
45
- .each(function() {
46
- var
47
- settings = ( $.isPlainObject(parameters) )
48
- ? $.extend(true, {}, $.fn.api.settings, parameters)
49
- : $.extend({}, $.fn.api.settings),
50
-
51
- // internal aliases
52
- namespace = settings.namespace,
53
- metadata = settings.metadata,
54
- selector = settings.selector,
55
- error = settings.error,
56
- className = settings.className,
57
-
58
- // define namespaces for modules
59
- eventNamespace = '.' + namespace,
60
- moduleNamespace = 'module-' + namespace,
61
-
62
- // element that creates request
63
- $module = $(this),
64
- $form = $module.closest(selector.form),
65
-
66
- // context used for state
67
- $context = (settings.stateContext)
68
- ? ([window,document].indexOf(settings.stateContext) < 0 ? $(document).find(settings.stateContext) : $(settings.stateContext))
69
- : $module,
70
-
71
- // request details
72
- ajaxSettings,
73
- requestSettings,
74
- url,
75
- data,
76
- requestStartTime,
77
- originalData,
78
-
79
- // standard module
80
- element = this,
81
- context = $context[0],
82
- instance = $module.data(moduleNamespace),
83
- module
84
- ;
85
-
86
- module = {
87
-
88
- initialize: function() {
89
- if(!methodInvoked) {
90
- originalData = settings.data;
91
- module.bind.events();
92
- }
93
- module.instantiate();
94
- },
11
+ (function ($, window, document, undefined) {
12
+ 'use strict';
13
+
14
+ function isWindow(obj) {
15
+ return obj != null && obj === obj.window;
16
+ }
17
+
18
+ function isFunction(obj) {
19
+ return typeof obj === 'function' && typeof obj.nodeType !== 'number';
20
+ }
21
+
22
+ window = (typeof window != 'undefined' && window.Math == Math)
23
+ ? window
24
+ : globalThis;
25
+
26
+ $.api = $.fn.api = function (parameters) {
27
+ var
28
+ // use window context if none specified
29
+ $allModules = isFunction(this)
30
+ ? $(window)
31
+ : $(this),
32
+ moduleSelector = $allModules.selector || '',
33
+ time = new Date().getTime(),
34
+ performance = [],
35
+
36
+ query = arguments[0],
37
+ methodInvoked = (typeof query == 'string'),
38
+ queryArguments = [].slice.call(arguments, 1),
39
+
40
+ returnedValue
41
+ ;
42
+
43
+ $allModules.each(function () {
44
+ var
45
+ settings = ($.isPlainObject(parameters))
46
+ ? $.extend(true, {}, $.fn.api.settings, parameters)
47
+ : $.extend({}, $.fn.api.settings),
48
+
49
+ // internal aliases
50
+ namespace = settings.namespace,
51
+ metadata = settings.metadata,
52
+ selector = settings.selector,
53
+ error = settings.error,
54
+ className = settings.className,
55
+
56
+ // define namespaces for modules
57
+ eventNamespace = '.' + namespace,
58
+ moduleNamespace = 'module-' + namespace,
59
+
60
+ // element that creates request
61
+ $module = $(this),
62
+ $form = $module.closest(selector.form),
63
+
64
+ // context used for state
65
+ $context = (settings.stateContext)
66
+ ? ([window, document].indexOf(settings.stateContext) < 0 ? $(document).find(settings.stateContext) : $(settings.stateContext))
67
+ : $module,
68
+
69
+ // request details
70
+ ajaxSettings,
71
+ requestSettings,
72
+ url,
73
+ data,
74
+ requestStartTime,
75
+ originalData,
76
+
77
+ // standard module
78
+ element = this,
79
+ context = $context[0],
80
+ instance = $module.data(moduleNamespace),
81
+ module
82
+ ;
95
83
 
96
- instantiate: function() {
97
- module.verbose('Storing instance of module', module);
98
- instance = module;
99
- $module
100
- .data(moduleNamespace, instance)
101
- ;
102
- },
84
+ module = {
103
85
 
104
- destroy: function() {
105
- module.verbose('Destroying previous module for', element);
106
- $module
107
- .removeData(moduleNamespace)
108
- .off(eventNamespace)
109
- ;
110
- },
86
+ initialize: function () {
87
+ if (!methodInvoked) {
88
+ originalData = settings.data;
89
+ module.bind.events();
90
+ }
91
+ module.instantiate();
92
+ },
93
+
94
+ instantiate: function () {
95
+ module.verbose('Storing instance of module', module);
96
+ instance = module;
97
+ $module
98
+ .data(moduleNamespace, instance)
99
+ ;
100
+ },
111
101
 
112
- bind: {
113
- events: function() {
114
- var
115
- triggerEvent = module.get.event()
116
- ;
117
- if( triggerEvent ) {
118
- module.verbose('Attaching API events to element', triggerEvent);
119
- $module
120
- .on(triggerEvent + eventNamespace, module.event.trigger)
121
- ;
122
- }
123
- else if(settings.on == 'now') {
124
- module.debug('Querying API endpoint immediately');
125
- module.query();
126
- }
127
- }
128
- },
102
+ destroy: function () {
103
+ module.verbose('Destroying previous module for', element);
104
+ $module
105
+ .removeData(moduleNamespace)
106
+ .off(eventNamespace)
107
+ ;
108
+ },
109
+
110
+ bind: {
111
+ events: function () {
112
+ var
113
+ triggerEvent = module.get.event()
114
+ ;
115
+ if (triggerEvent) {
116
+ module.verbose('Attaching API events to element', triggerEvent);
117
+ $module
118
+ .on(triggerEvent + eventNamespace, module.event.trigger)
119
+ ;
120
+ } else if (settings.on == 'now') {
121
+ module.debug('Querying API endpoint immediately');
122
+ module.query();
123
+ }
124
+ },
125
+ },
126
+
127
+ decode: {
128
+ json: function (response) {
129
+ if (response !== undefined && typeof response == 'string') {
130
+ try {
131
+ response = JSON.parse(response);
132
+ } catch (e) {
133
+ // isn't json string
134
+ }
135
+ }
136
+
137
+ return response;
138
+ },
139
+ },
140
+
141
+ read: {
142
+ cachedResponse: function (url) {
143
+ var
144
+ response
145
+ ;
146
+ if (window.Storage === undefined) {
147
+ module.error(error.noStorage);
148
+
149
+ return;
150
+ }
151
+ response = sessionStorage.getItem(url + module.get.normalizedData());
152
+ module.debug('Using cached response', url, settings.data, response);
153
+ response = module.decode.json(response);
154
+
155
+ return response;
156
+ },
157
+ },
158
+ write: {
159
+ cachedResponse: function (url, response) {
160
+ if (response && response === '') {
161
+ module.debug('Response empty, not caching', response);
162
+
163
+ return;
164
+ }
165
+ if (window.Storage === undefined) {
166
+ module.error(error.noStorage);
167
+
168
+ return;
169
+ }
170
+ if ($.isPlainObject(response)) {
171
+ response = JSON.stringify(response);
172
+ }
173
+ sessionStorage.setItem(url + module.get.normalizedData(), response);
174
+ module.verbose('Storing cached response for url', url, settings.data, response);
175
+ },
176
+ },
177
+
178
+ query: function () {
179
+ if (module.is.disabled()) {
180
+ module.debug('Element is disabled API request aborted');
181
+
182
+ return;
183
+ }
129
184
 
130
- decode: {
131
- json: function(response) {
132
- if(response !== undefined && typeof response == 'string') {
133
- try {
134
- response = JSON.parse(response);
135
- }
136
- catch(e) {
137
- // isn't json string
138
- }
139
- }
140
- return response;
141
- }
142
- },
185
+ if (module.is.loading()) {
186
+ if (settings.interruptRequests) {
187
+ module.debug('Interrupting previous request');
188
+ module.abort();
189
+ } else {
190
+ module.debug('Cancelling request, previous request is still pending');
143
191
 
144
- read: {
145
- cachedResponse: function(url) {
146
- var
147
- response
148
- ;
149
- if(window.Storage === undefined) {
150
- module.error(error.noStorage);
151
- return;
152
- }
153
- response = sessionStorage.getItem(url + module.get.normalizedData());
154
- module.debug('Using cached response', url, settings.data, response);
155
- response = module.decode.json(response);
156
- return response;
157
- }
158
- },
159
- write: {
160
- cachedResponse: function(url, response) {
161
- if(response && response === '') {
162
- module.debug('Response empty, not caching', response);
163
- return;
164
- }
165
- if(window.Storage === undefined) {
166
- module.error(error.noStorage);
167
- return;
168
- }
169
- if( $.isPlainObject(response) ) {
170
- response = JSON.stringify(response);
171
- }
172
- sessionStorage.setItem(url + module.get.normalizedData(), response);
173
- module.verbose('Storing cached response for url', url, settings.data, response);
174
- }
175
- },
192
+ return;
193
+ }
194
+ }
176
195
 
177
- query: function() {
196
+ // pass element metadata to url (value, text)
197
+ if (settings.defaultData) {
198
+ $.extend(true, settings.urlData, module.get.defaultData());
199
+ }
178
200
 
179
- if(module.is.disabled()) {
180
- module.debug('Element is disabled API request aborted');
181
- return;
182
- }
201
+ // Add form content
202
+ if (settings.serializeForm) {
203
+ settings.data = module.add.formData(originalData || settings.data);
204
+ }
183
205
 
184
- if(module.is.loading()) {
185
- if(settings.interruptRequests) {
186
- module.debug('Interrupting previous request');
187
- module.abort();
188
- }
189
- else {
190
- module.debug('Cancelling request, previous request is still pending');
191
- return;
192
- }
193
- }
194
-
195
- // pass element metadata to url (value, text)
196
- if(settings.defaultData) {
197
- $.extend(true, settings.urlData, module.get.defaultData());
198
- }
199
-
200
- // Add form content
201
- if(settings.serializeForm) {
202
- settings.data = module.add.formData(originalData || settings.data);
203
- }
204
-
205
- // call beforesend and get any settings changes
206
- requestSettings = module.get.settings();
207
-
208
- // check if before send cancelled request
209
- if(requestSettings === false) {
210
- module.cancelled = true;
211
- module.error(error.beforeSend);
212
- return;
213
- }
214
- else {
215
- module.cancelled = false;
216
- }
217
-
218
- // get url
219
- url = module.get.templatedURL();
220
-
221
- if(!url && !module.is.mocked()) {
222
- module.error(error.missingURL);
223
- return;
224
- }
225
-
226
- // replace variables
227
- url = module.add.urlData( url );
228
- // missing url parameters
229
- if( !url && !module.is.mocked()) {
230
- return;
231
- }
232
-
233
- requestSettings.url = settings.base + url;
234
-
235
- // look for jQuery ajax parameters in settings
236
- ajaxSettings = $.extend(true, {}, settings, {
237
- type : settings.method || settings.type,
238
- data : data,
239
- url : settings.base + url,
240
- beforeSend : settings.beforeXHR,
241
- success : function() {},
242
- failure : function() {},
243
- complete : function() {}
244
- });
245
-
246
- module.debug('Querying URL', ajaxSettings.url);
247
- module.verbose('Using AJAX settings', ajaxSettings);
248
- if(settings.cache === 'local' && module.read.cachedResponse(url)) {
249
- module.debug('Response returned from local cache');
250
- module.request = module.create.request();
251
- module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
252
- return;
253
- }
254
-
255
- if( !settings.throttle ) {
256
- module.debug('Sending request', data, ajaxSettings.method);
257
- module.send.request();
258
- }
259
- else {
260
- if(!settings.throttleFirstRequest && !module.timer) {
261
- module.debug('Sending request', data, ajaxSettings.method);
262
- module.send.request();
263
- module.timer = setTimeout(function(){}, settings.throttle);
264
- }
265
- else {
266
- module.debug('Throttling request', settings.throttle);
267
- clearTimeout(module.timer);
268
- module.timer = setTimeout(function() {
269
- if(module.timer) {
270
- delete module.timer;
271
- }
272
- module.debug('Sending throttled request', data, ajaxSettings.method);
273
- module.send.request();
274
- }, settings.throttle);
275
- }
276
- }
206
+ // call beforesend and get any settings changes
207
+ requestSettings = module.get.settings();
277
208
 
278
- },
209
+ // check if before send cancelled request
210
+ if (requestSettings === false) {
211
+ module.cancelled = true;
212
+ module.error(error.beforeSend);
279
213
 
280
- should: {
281
- removeError: function() {
282
- return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
283
- }
284
- },
214
+ return;
215
+ } else {
216
+ module.cancelled = false;
217
+ }
285
218
 
286
- is: {
287
- disabled: function() {
288
- return ($module.filter(selector.disabled).length > 0);
289
- },
290
- expectingJSON: function() {
291
- return settings.dataType === 'json' || settings.dataType === 'jsonp';
292
- },
293
- form: function() {
294
- return $module.is('form') || $context.is('form');
295
- },
296
- mocked: function() {
297
- return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
298
- },
299
- input: function() {
300
- return $module.is('input');
301
- },
302
- loading: function() {
303
- return (module.request)
304
- ? (module.request.state() == 'pending')
305
- : false
306
- ;
307
- },
308
- abortedRequest: function(xhr) {
309
- if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
310
- module.verbose('XHR request determined to be aborted');
311
- return true;
312
- }
313
- else {
314
- module.verbose('XHR request was not aborted');
315
- return false;
316
- }
317
- },
318
- validResponse: function(response) {
319
- if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) {
320
- module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
321
- return true;
322
- }
323
- module.debug('Checking JSON returned success', settings.successTest, response);
324
- if( settings.successTest(response) ) {
325
- module.debug('Response passed success test', response);
326
- return true;
327
- }
328
- else {
329
- module.debug('Response failed success test', response);
330
- return false;
331
- }
332
- }
333
- },
219
+ // get url
220
+ url = module.get.templatedURL();
334
221
 
335
- was: {
336
- cancelled: function() {
337
- return (module.cancelled || false);
338
- },
339
- successful: function() {
340
- return (module.request && module.request.state() == 'resolved');
341
- },
342
- failure: function() {
343
- return (module.request && module.request.state() == 'rejected');
344
- },
345
- complete: function() {
346
- return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
347
- }
348
- },
222
+ if (!url && !module.is.mocked()) {
223
+ module.error(error.missingURL);
349
224
 
350
- add: {
351
- urlData: function(url, urlData) {
352
- var
353
- requiredVariables,
354
- optionalVariables
355
- ;
356
- if(url) {
357
- requiredVariables = url.match(settings.regExp.required);
358
- optionalVariables = url.match(settings.regExp.optional);
359
- urlData = urlData || settings.urlData;
360
- if(requiredVariables) {
361
- module.debug('Looking for required URL variables', requiredVariables);
362
- $.each(requiredVariables, function(index, templatedString) {
363
- var
364
- // allow legacy {$var} style
365
- variable = (templatedString.indexOf('$') !== -1)
366
- ? templatedString.slice(2, -1)
367
- : templatedString.slice(1, -1),
368
- value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
369
- ? urlData[variable]
370
- : ($module.data(variable) !== undefined)
371
- ? $module.data(variable)
372
- : ($context.data(variable) !== undefined)
373
- ? $context.data(variable)
374
- : urlData[variable]
375
- ;
376
- // remove value
377
- if(value === undefined) {
378
- module.error(error.requiredParameter, variable, url);
379
- url = false;
380
- return false;
381
- }
382
- else {
383
- module.verbose('Found required variable', variable, value);
384
- value = (settings.encodeParameters)
385
- ? module.get.urlEncodedValue(value)
386
- : value
225
+ return;
226
+ }
227
+
228
+ // replace variables
229
+ url = module.add.urlData(url);
230
+ // missing url parameters
231
+ if (!url && !module.is.mocked()) {
232
+ return;
233
+ }
234
+
235
+ requestSettings.url = settings.base + url;
236
+
237
+ // look for jQuery ajax parameters in settings
238
+ ajaxSettings = $.extend(true, {}, settings, {
239
+ type: settings.method || settings.type,
240
+ data: data,
241
+ url: settings.base + url,
242
+ beforeSend: settings.beforeXHR,
243
+ success: function () {},
244
+ failure: function () {},
245
+ complete: function () {},
246
+ });
247
+
248
+ module.debug('Querying URL', ajaxSettings.url);
249
+ module.verbose('Using AJAX settings', ajaxSettings);
250
+ if (settings.cache === 'local' && module.read.cachedResponse(url)) {
251
+ module.debug('Response returned from local cache');
252
+ module.request = module.create.request();
253
+ module.request.resolveWith(context, [module.read.cachedResponse(url)]);
254
+
255
+ return;
256
+ }
257
+
258
+ if (!settings.throttle) {
259
+ module.debug('Sending request', data, ajaxSettings.method);
260
+ module.send.request();
261
+ } else {
262
+ if (!settings.throttleFirstRequest && !module.timer) {
263
+ module.debug('Sending request', data, ajaxSettings.method);
264
+ module.send.request();
265
+ module.timer = setTimeout(function () {}, settings.throttle);
266
+ } else {
267
+ module.debug('Throttling request', settings.throttle);
268
+ clearTimeout(module.timer);
269
+ module.timer = setTimeout(function () {
270
+ if (module.timer) {
271
+ delete module.timer;
272
+ }
273
+ module.debug('Sending throttled request', data, ajaxSettings.method);
274
+ module.send.request();
275
+ }, settings.throttle);
276
+ }
277
+ }
278
+ },
279
+
280
+ should: {
281
+ removeError: function () {
282
+ return (settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()));
283
+ },
284
+ },
285
+
286
+ is: {
287
+ disabled: function () {
288
+ return ($module.filter(selector.disabled).length > 0);
289
+ },
290
+ expectingJSON: function () {
291
+ return settings.dataType === 'json' || settings.dataType === 'jsonp';
292
+ },
293
+ form: function () {
294
+ return $module.is('form') || $context.is('form');
295
+ },
296
+ mocked: function () {
297
+ return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
298
+ },
299
+ input: function () {
300
+ return $module.is('input');
301
+ },
302
+ loading: function () {
303
+ return (module.request)
304
+ ? (module.request.state() == 'pending')
305
+ : false;
306
+ },
307
+ abortedRequest: function (xhr) {
308
+ if (xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
309
+ module.verbose('XHR request determined to be aborted');
310
+
311
+ return true;
312
+ } else {
313
+ module.verbose('XHR request was not aborted');
314
+
315
+ return false;
316
+ }
317
+ },
318
+ validResponse: function (response) {
319
+ if ((!module.is.expectingJSON()) || !isFunction(settings.successTest)) {
320
+ module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
321
+
322
+ return true;
323
+ }
324
+ module.debug('Checking JSON returned success', settings.successTest, response);
325
+ if (settings.successTest(response)) {
326
+ module.debug('Response passed success test', response);
327
+
328
+ return true;
329
+ } else {
330
+ module.debug('Response failed success test', response);
331
+
332
+ return false;
333
+ }
334
+ },
335
+ },
336
+
337
+ was: {
338
+ cancelled: function () {
339
+ return (module.cancelled || false);
340
+ },
341
+ successful: function () {
342
+ return (module.request && module.request.state() == 'resolved');
343
+ },
344
+ failure: function () {
345
+ return (module.request && module.request.state() == 'rejected');
346
+ },
347
+ complete: function () {
348
+ return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected'));
349
+ },
350
+ },
351
+
352
+ add: {
353
+ urlData: function (url, urlData) {
354
+ var
355
+ requiredVariables,
356
+ optionalVariables
357
+ ;
358
+ if (url) {
359
+ requiredVariables = url.match(settings.regExp.required);
360
+ optionalVariables = url.match(settings.regExp.optional);
361
+ urlData = urlData || settings.urlData;
362
+ if (requiredVariables) {
363
+ module.debug('Looking for required URL variables', requiredVariables);
364
+ $.each(requiredVariables, function (index, templatedString) {
365
+ var
366
+ // allow legacy {$var} style
367
+ variable = (templatedString.indexOf('$') !== -1)
368
+ ? templatedString.slice(2, -1)
369
+ : templatedString.slice(1, -1),
370
+ value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
371
+ ? urlData[variable]
372
+ : ($module.data(variable) !== undefined)
373
+ ? $module.data(variable)
374
+ : ($context.data(variable) !== undefined)
375
+ ? $context.data(variable)
376
+ : urlData[variable]
377
+ ;
378
+ // remove value
379
+ if (value === undefined) {
380
+ module.error(error.requiredParameter, variable, url);
381
+ url = false;
382
+
383
+ return false;
384
+ } else {
385
+ module.verbose('Found required variable', variable, value);
386
+ value = (settings.encodeParameters)
387
+ ? module.get.urlEncodedValue(value)
388
+ : value;
389
+ url = url.replace(templatedString, value);
390
+ }
391
+ });
392
+ }
393
+ if (optionalVariables) {
394
+ module.debug('Looking for optional URL variables', requiredVariables);
395
+ $.each(optionalVariables, function (index, templatedString) {
396
+ var
397
+ // allow legacy {/$var} style
398
+ variable = (templatedString.indexOf('$') !== -1)
399
+ ? templatedString.slice(3, -1)
400
+ : templatedString.slice(2, -1),
401
+ value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
402
+ ? urlData[variable]
403
+ : ($module.data(variable) !== undefined)
404
+ ? $module.data(variable)
405
+ : ($context.data(variable) !== undefined)
406
+ ? $context.data(variable)
407
+ : urlData[variable]
408
+ ;
409
+ // optional replacement
410
+ if (value !== undefined) {
411
+ module.verbose('Optional variable Found', variable, value);
412
+ url = url.replace(templatedString, value);
413
+ } else {
414
+ module.verbose('Optional variable not found', variable);
415
+ // remove preceding slash if set
416
+ if (url.indexOf('/' + templatedString) !== -1) {
417
+ url = url.replace('/' + templatedString, '');
418
+ } else {
419
+ url = url.replace(templatedString, '');
420
+ }
421
+ }
422
+ });
423
+ }
424
+ }
425
+
426
+ return url;
427
+ },
428
+ formData: function (data) {
429
+ var
430
+ formData = {},
431
+ hasOtherData,
432
+ useFormDataApi = settings.serializeForm === 'formdata'
433
+ ;
434
+ data = data || originalData || settings.data;
435
+ hasOtherData = $.isPlainObject(data);
436
+
437
+ if (useFormDataApi) {
438
+ formData = new FormData($form[0]);
439
+ settings.processData = typeof settings.processData !== 'undefined' ? settings.processData : false;
440
+ settings.contentType = typeof settings.contentType !== 'undefined' ? settings.contentType : false;
441
+ } else {
442
+ var
443
+ formArray = $form.serializeArray(),
444
+ pushes = {},
445
+ pushValues = {},
446
+ build = function (base, key, value) {
447
+ base[key] = value;
448
+
449
+ return base;
450
+ }
451
+ ;
452
+ // add files
453
+ $.each($('input[type="file"]', $form), function (i, tag) {
454
+ $.each($(tag)[0].files, function (j, file) {
455
+ formArray.push({ name: tag.name, value: file });
456
+ });
457
+ });
458
+ $.each(formArray, function (i, el) {
459
+ if (!settings.regExp.validate.test(el.name)) {
460
+ return;
461
+ }
462
+ var
463
+ isCheckbox = $('[name="' + el.name + '"]', $form).attr('type') === 'checkbox',
464
+ floatValue = parseFloat(el.value),
465
+ value = (isCheckbox && el.value === 'on')
466
+ || el.value === 'true'
467
+ || (String(floatValue) === el.value
468
+ ? floatValue
469
+ : (el.value === 'false' ? false : el.value)),
470
+ nameKeys = el.name.match(settings.regExp.key) || [],
471
+ k,
472
+ pushKey = el.name.replace(/\[\]$/, '')
473
+ ;
474
+ if (!(pushKey in pushes)) {
475
+ pushes[pushKey] = 0;
476
+ pushValues[pushKey] = value;
477
+ } else if (Array.isArray(pushValues[pushKey])) {
478
+ pushValues[pushKey].push(value);
479
+ } else {
480
+ pushValues[pushKey] = [pushValues[pushKey], value];
481
+ }
482
+ if (pushKey.indexOf('[]') === -1) {
483
+ value = pushValues[pushKey];
484
+ }
485
+
486
+ while ((k = nameKeys.pop()) !== undefined) {
487
+ if (k == '' && !Array.isArray(value)) { // foo[]
488
+ value = build([], pushes[pushKey]++, value);
489
+ } else if (settings.regExp.fixed.test(k)) { // foo[n]
490
+ value = build([], k, value);
491
+ } else if (settings.regExp.named.test(k)) { // foo; foo[bar]
492
+ value = build({}, k, value);
493
+ }
494
+ }
495
+ formData = $.extend(true, formData, value);
496
+ });
497
+ }
498
+
499
+ if (hasOtherData) {
500
+ module.debug('Extending existing data with form data', data, formData);
501
+ if (useFormDataApi) {
502
+ $.each(Object.keys(data), function (i, el) {
503
+ formData.append(el, data[el]);
504
+ });
505
+ data = formData;
506
+ } else {
507
+ data = $.extend(true, {}, data, formData);
508
+ }
509
+ } else {
510
+ module.debug('Adding form data', formData);
511
+ data = formData;
512
+ }
513
+
514
+ return data;
515
+ },
516
+ },
517
+
518
+ send: {
519
+ request: function () {
520
+ module.set.loading();
521
+ module.request = module.create.request();
522
+ if (module.is.mocked()) {
523
+ module.mockedXHR = module.create.mockedXHR();
524
+ } else {
525
+ module.xhr = module.create.xhr();
526
+ }
527
+ settings.onRequest.call(context, module.request, module.xhr);
528
+ },
529
+ },
530
+
531
+ event: {
532
+ trigger: function (event) {
533
+ module.query();
534
+ if (event.type == 'submit' || event.type == 'click') {
535
+ event.preventDefault();
536
+ }
537
+ },
538
+ xhr: {
539
+ always: function () {
540
+ // nothing special
541
+ },
542
+ done: function (response, textStatus, xhr) {
543
+ var
544
+ context = this,
545
+ elapsedTime = (new Date().getTime() - requestStartTime),
546
+ timeLeft = (settings.loadingDuration - elapsedTime),
547
+ translatedResponse = (isFunction(settings.onResponse))
548
+ ? module.is.expectingJSON() && !settings.rawResponse
549
+ ? settings.onResponse.call(context, $.extend(true, {}, response))
550
+ : settings.onResponse.call(context, response)
551
+ : false
552
+ ;
553
+ timeLeft = (timeLeft > 0)
554
+ ? timeLeft
555
+ : 0;
556
+ if (translatedResponse) {
557
+ module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
558
+ response = translatedResponse;
559
+ }
560
+ if (timeLeft > 0) {
561
+ module.debug('Response completed early delaying state change by', timeLeft);
562
+ }
563
+ setTimeout(function () {
564
+ if (module.is.validResponse(response)) {
565
+ module.request.resolveWith(context, [response, xhr]);
566
+ } else {
567
+ module.request.rejectWith(context, [xhr, 'invalid']);
568
+ }
569
+ }, timeLeft);
570
+ },
571
+ fail: function (xhr, status, httpMessage) {
572
+ var
573
+ context = this,
574
+ elapsedTime = (new Date().getTime() - requestStartTime),
575
+ timeLeft = (settings.loadingDuration - elapsedTime)
576
+ ;
577
+ timeLeft = (timeLeft > 0)
578
+ ? timeLeft
579
+ : 0;
580
+ if (timeLeft > 0) {
581
+ module.debug('Response completed early delaying state change by', timeLeft);
582
+ }
583
+ setTimeout(function () {
584
+ if (module.is.abortedRequest(xhr)) {
585
+ module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
586
+ } else {
587
+ module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
588
+ }
589
+ }, timeLeft);
590
+ },
591
+ },
592
+ request: {
593
+ done: function (response, xhr) {
594
+ module.debug('Successful API Response', response);
595
+ if (settings.cache === 'local' && url) {
596
+ module.write.cachedResponse(url, response);
597
+ module.debug('Saving server response locally', module.cache);
598
+ }
599
+ settings.onSuccess.call(context, response, $module, xhr);
600
+ },
601
+ complete: function (firstParameter, secondParameter) {
602
+ var
603
+ xhr,
604
+ response
605
+ ;
606
+ // have to guess callback parameters based on request success
607
+ if (module.was.successful()) {
608
+ response = firstParameter;
609
+ xhr = secondParameter;
610
+ } else {
611
+ xhr = firstParameter;
612
+ response = module.get.responseFromXHR(xhr);
613
+ }
614
+ module.remove.loading();
615
+ settings.onComplete.call(context, response, $module, xhr);
616
+ },
617
+ fail: function (xhr, status, httpMessage) {
618
+ var
619
+ // pull response from xhr if available
620
+ response = module.get.responseFromXHR(xhr),
621
+ errorMessage = module.get.errorFromRequest(response, status, httpMessage)
622
+ ;
623
+ if (status == 'aborted') {
624
+ module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
625
+ settings.onAbort.call(context, status, $module, xhr);
626
+
627
+ return true;
628
+ } else if (status == 'invalid') {
629
+ module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
630
+ } else if (status == 'error') {
631
+ if (xhr !== undefined) {
632
+ module.debug('XHR produced a server error', status, httpMessage);
633
+ // make sure we have an error to display to console
634
+ if ((xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
635
+ module.error(error.statusMessage + httpMessage, ajaxSettings.url);
636
+ }
637
+ settings.onError.call(context, errorMessage, $module, xhr);
638
+ }
639
+ }
640
+
641
+ if (settings.errorDuration && status !== 'aborted') {
642
+ module.debug('Adding error state');
643
+ module.set.error();
644
+ if (module.should.removeError()) {
645
+ setTimeout(module.remove.error, settings.errorDuration);
646
+ }
647
+ }
648
+ module.debug('API Request failed', errorMessage, xhr);
649
+ settings.onFailure.call(context, response, $module, xhr);
650
+ },
651
+ },
652
+ },
653
+
654
+ create: {
655
+
656
+ request: function () {
657
+ // api request promise
658
+ return $.Deferred()
659
+ .always(module.event.request.complete)
660
+ .done(module.event.request.done)
661
+ .fail(module.event.request.fail)
662
+ ;
663
+ },
664
+
665
+ mockedXHR: function () {
666
+ var
667
+ // xhr does not simulate these properties of xhr but must return them
668
+ textStatus = false,
669
+ status = false,
670
+ httpMessage = false,
671
+ responder = settings.mockResponse || settings.response,
672
+ asyncResponder = settings.mockResponseAsync || settings.responseAsync,
673
+ asyncCallback,
674
+ response,
675
+ mockedXHR
676
+ ;
677
+
678
+ mockedXHR = $.Deferred()
679
+ .always(module.event.xhr.complete)
680
+ .done(module.event.xhr.done)
681
+ .fail(module.event.xhr.fail)
682
+ ;
683
+
684
+ if (responder) {
685
+ if (isFunction(responder)) {
686
+ module.debug('Using specified synchronous callback', responder);
687
+ response = responder.call(context, requestSettings);
688
+ } else {
689
+ module.debug('Using settings specified response', responder);
690
+ response = responder;
691
+ }
692
+ // simulating response
693
+ mockedXHR.resolveWith(context, [response, textStatus, { responseText: response }]);
694
+ } else if (isFunction(asyncResponder)) {
695
+ asyncCallback = function (response) {
696
+ module.debug('Async callback returned response', response);
697
+
698
+ if (response) {
699
+ mockedXHR.resolveWith(context, [response, textStatus, { responseText: response }]);
700
+ } else {
701
+ mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
702
+ }
703
+ };
704
+ module.debug('Using specified async response callback', asyncResponder);
705
+ asyncResponder.call(context, requestSettings, asyncCallback);
706
+ }
707
+
708
+ return mockedXHR;
709
+ },
710
+
711
+ xhr: function () {
712
+ var
713
+ xhr
714
+ ;
715
+ // ajax request promise
716
+ xhr = $.ajax(ajaxSettings)
717
+ .always(module.event.xhr.always)
718
+ .done(module.event.xhr.done)
719
+ .fail(module.event.xhr.fail)
720
+ ;
721
+ module.verbose('Created server request', xhr, ajaxSettings);
722
+
723
+ return xhr;
724
+ },
725
+ },
726
+
727
+ set: {
728
+ error: function () {
729
+ module.verbose('Adding error state to element', $context);
730
+ $context.addClass(className.error);
731
+ },
732
+ loading: function () {
733
+ module.verbose('Adding loading state to element', $context);
734
+ $context.addClass(className.loading);
735
+ requestStartTime = new Date().getTime();
736
+ },
737
+ },
738
+
739
+ remove: {
740
+ error: function () {
741
+ module.verbose('Removing error state from element', $context);
742
+ $context.removeClass(className.error);
743
+ },
744
+ loading: function () {
745
+ module.verbose('Removing loading state from element', $context);
746
+ $context.removeClass(className.loading);
747
+ },
748
+ },
749
+
750
+ get: {
751
+ normalizedData: function () {
752
+ return typeof settings.data === 'string' ? settings.data : JSON.stringify(settings.data, Object.keys(settings.data).sort());
753
+ },
754
+ responseFromXHR: function (xhr) {
755
+ return $.isPlainObject(xhr)
756
+ ? (module.is.expectingJSON())
757
+ ? module.decode.json(xhr.responseText)
758
+ : xhr.responseText
759
+ : false;
760
+ },
761
+ errorFromRequest: function (response, status, httpMessage) {
762
+ return ($.isPlainObject(response) && response.error !== undefined)
763
+ ? response.error // use json error message
764
+ : (settings.error[status] !== undefined) // use server error message
765
+ ? settings.error[status]
766
+ : httpMessage;
767
+ },
768
+ request: function () {
769
+ return module.request || false;
770
+ },
771
+ xhr: function () {
772
+ return module.xhr || false;
773
+ },
774
+ settings: function () {
775
+ var
776
+ runSettings
777
+ ;
778
+ runSettings = settings.beforeSend.call($module, settings);
779
+ if (runSettings) {
780
+ if (runSettings.success !== undefined) {
781
+ module.debug('Legacy success callback detected', runSettings);
782
+ module.error(error.legacyParameters, runSettings.success);
783
+ runSettings.onSuccess = runSettings.success;
784
+ }
785
+ if (runSettings.failure !== undefined) {
786
+ module.debug('Legacy failure callback detected', runSettings);
787
+ module.error(error.legacyParameters, runSettings.failure);
788
+ runSettings.onFailure = runSettings.failure;
789
+ }
790
+ if (runSettings.complete !== undefined) {
791
+ module.debug('Legacy complete callback detected', runSettings);
792
+ module.error(error.legacyParameters, runSettings.complete);
793
+ runSettings.onComplete = runSettings.complete;
794
+ }
795
+ }
796
+ if (runSettings === undefined) {
797
+ module.error(error.noReturnedValue);
798
+ }
799
+ if (runSettings === false) {
800
+ return runSettings;
801
+ }
802
+
803
+ return (runSettings !== undefined)
804
+ ? $.extend(true, {}, runSettings)
805
+ : $.extend(true, {}, settings);
806
+ },
807
+ urlEncodedValue: function (value) {
808
+ var
809
+ decodedValue = window.decodeURIComponent(value),
810
+ encodedValue = window.encodeURIComponent(value),
811
+ alreadyEncoded = (decodedValue !== value)
812
+ ;
813
+ if (alreadyEncoded) {
814
+ module.debug('URL value is already encoded, avoiding double encoding', value);
815
+
816
+ return value;
817
+ }
818
+ module.verbose('Encoding value using encodeURIComponent', value, encodedValue);
819
+
820
+ return encodedValue;
821
+ },
822
+ defaultData: function () {
823
+ var
824
+ data = {}
825
+ ;
826
+ if (!isWindow(element)) {
827
+ if (module.is.input()) {
828
+ data.value = $module.val();
829
+ } else if (module.is.form()) {
830
+
831
+ } else {
832
+ data.text = $module.text();
833
+ }
834
+ }
835
+
836
+ return data;
837
+ },
838
+ event: function () {
839
+ if (isWindow(element) || settings.on == 'now') {
840
+ module.debug('API called without element, no events attached');
841
+
842
+ return false;
843
+ } else if (settings.on == 'auto') {
844
+ if ($module.is('input')) {
845
+ return (element.oninput !== undefined)
846
+ ? 'input'
847
+ : (element.onpropertychange !== undefined)
848
+ ? 'propertychange'
849
+ : 'keyup';
850
+ } else if ($module.is('form')) {
851
+ return 'submit';
852
+ } else {
853
+ return 'click';
854
+ }
855
+ } else {
856
+ return settings.on;
857
+ }
858
+ },
859
+ templatedURL: function (action) {
860
+ action = action || $module.data(metadata.action) || settings.action || false;
861
+ url = $module.data(metadata.url) || settings.url || false;
862
+ if (url) {
863
+ module.debug('Using specified url', url);
864
+
865
+ return url;
866
+ }
867
+ if (action) {
868
+ module.debug('Looking up url for action', action, settings.api);
869
+ if (settings.api[action] === undefined && !module.is.mocked()) {
870
+ module.error(error.missingAction, settings.action, settings.api);
871
+
872
+ return;
873
+ }
874
+ url = settings.api[action];
875
+ } else if (module.is.form()) {
876
+ url = $module.attr('action') || $context.attr('action') || false;
877
+ module.debug('No url or action specified, defaulting to form action', url);
878
+ }
879
+
880
+ return url;
881
+ },
882
+ },
883
+
884
+ abort: function () {
885
+ var
886
+ xhr = module.get.xhr()
387
887
  ;
388
- url = url.replace(templatedString, value);
389
- }
390
- });
391
- }
392
- if(optionalVariables) {
393
- module.debug('Looking for optional URL variables', requiredVariables);
394
- $.each(optionalVariables, function(index, templatedString) {
395
- var
396
- // allow legacy {/$var} style
397
- variable = (templatedString.indexOf('$') !== -1)
398
- ? templatedString.slice(3, -1)
399
- : templatedString.slice(2, -1),
400
- value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
401
- ? urlData[variable]
402
- : ($module.data(variable) !== undefined)
403
- ? $module.data(variable)
404
- : ($context.data(variable) !== undefined)
405
- ? $context.data(variable)
406
- : urlData[variable]
407
- ;
408
- // optional replacement
409
- if(value !== undefined) {
410
- module.verbose('Optional variable Found', variable, value);
411
- url = url.replace(templatedString, value);
412
- }
413
- else {
414
- module.verbose('Optional variable not found', variable);
415
- // remove preceding slash if set
416
- if(url.indexOf('/' + templatedString) !== -1) {
417
- url = url.replace('/' + templatedString, '');
888
+ if (xhr && xhr.state() !== 'resolved') {
889
+ module.debug('Cancelling API request');
890
+ xhr.abort();
418
891
  }
419
- else {
420
- url = url.replace(templatedString, '');
892
+ },
893
+
894
+ // reset state
895
+ reset: function () {
896
+ module.remove.error();
897
+ module.remove.loading();
898
+ },
899
+
900
+ setting: function (name, value) {
901
+ module.debug('Changing setting', name, value);
902
+ if ($.isPlainObject(name)) {
903
+ $.extend(true, settings, name);
904
+ } else if (value !== undefined) {
905
+ if ($.isPlainObject(settings[name])) {
906
+ $.extend(true, settings[name], value);
907
+ } else {
908
+ settings[name] = value;
909
+ }
910
+ } else {
911
+ return settings[name];
912
+ }
913
+ },
914
+ internal: function (name, value) {
915
+ if ($.isPlainObject(name)) {
916
+ $.extend(true, module, name);
917
+ } else if (value !== undefined) {
918
+ module[name] = value;
919
+ } else {
920
+ return module[name];
921
+ }
922
+ },
923
+ debug: function () {
924
+ if (!settings.silent && settings.debug) {
925
+ if (settings.performance) {
926
+ module.performance.log(arguments);
927
+ } else {
928
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
929
+ module.debug.apply(console, arguments);
930
+ }
931
+ }
932
+ },
933
+ verbose: function () {
934
+ if (!settings.silent && settings.verbose && settings.debug) {
935
+ if (settings.performance) {
936
+ module.performance.log(arguments);
937
+ } else {
938
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
939
+ module.verbose.apply(console, arguments);
940
+ }
941
+ }
942
+ },
943
+ error: function () {
944
+ if (!settings.silent) {
945
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
946
+ module.error.apply(console, arguments);
947
+ }
948
+ },
949
+ performance: {
950
+ log: function (message) {
951
+ var
952
+ currentTime,
953
+ executionTime,
954
+ previousTime
955
+ ;
956
+ if (settings.performance) {
957
+ currentTime = new Date().getTime();
958
+ previousTime = time || currentTime;
959
+ executionTime = currentTime - previousTime;
960
+ time = currentTime;
961
+ performance.push({
962
+ Name: message[0],
963
+ Arguments: [].slice.call(message, 1) || '',
964
+ // 'Element' : element,
965
+ 'Execution Time': executionTime,
966
+ });
967
+ }
968
+ clearTimeout(module.performance.timer);
969
+ module.performance.timer = setTimeout(module.performance.display, 500);
970
+ },
971
+ display: function () {
972
+ var
973
+ title = settings.name + ':',
974
+ totalTime = 0
975
+ ;
976
+ time = false;
977
+ clearTimeout(module.performance.timer);
978
+ $.each(performance, function (index, data) {
979
+ totalTime += data['Execution Time'];
980
+ });
981
+ title += ' ' + totalTime + 'ms';
982
+ if (moduleSelector) {
983
+ title += ' \'' + moduleSelector + '\'';
984
+ }
985
+ if ((console.group !== undefined || console.table !== undefined) && performance.length > 0) {
986
+ console.groupCollapsed(title);
987
+ if (console.table) {
988
+ console.table(performance);
989
+ } else {
990
+ $.each(performance, function (index, data) {
991
+ console.log(data.Name + ': ' + data['Execution Time'] + 'ms');
992
+ });
993
+ }
994
+ console.groupEnd();
995
+ }
996
+ performance = [];
997
+ },
998
+ },
999
+ invoke: function (query, passedArguments, context) {
1000
+ var
1001
+ object = instance,
1002
+ maxDepth,
1003
+ found,
1004
+ response
1005
+ ;
1006
+ passedArguments = passedArguments || queryArguments;
1007
+ context = context || element;
1008
+ if (typeof query == 'string' && object !== undefined) {
1009
+ query = query.split(/[\. ]/);
1010
+ maxDepth = query.length - 1;
1011
+ $.each(query, function (depth, value) {
1012
+ var camelCaseValue = (depth != maxDepth)
1013
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1014
+ : query
1015
+ ;
1016
+ if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) {
1017
+ object = object[camelCaseValue];
1018
+ } else if (object[camelCaseValue] !== undefined) {
1019
+ found = object[camelCaseValue];
1020
+
1021
+ return false;
1022
+ } else if ($.isPlainObject(object[value]) && (depth != maxDepth)) {
1023
+ object = object[value];
1024
+ } else if (object[value] !== undefined) {
1025
+ found = object[value];
1026
+
1027
+ return false;
1028
+ } else {
1029
+ module.error(error.method, query);
1030
+
1031
+ return false;
1032
+ }
1033
+ });
1034
+ }
1035
+ if (isFunction(found)) {
1036
+ response = found.apply(context, passedArguments);
1037
+ } else if (found !== undefined) {
1038
+ response = found;
1039
+ }
1040
+ if (Array.isArray(returnedValue)) {
1041
+ returnedValue.push(response);
1042
+ } else if (returnedValue !== undefined) {
1043
+ returnedValue = [returnedValue, response];
1044
+ } else if (response !== undefined) {
1045
+ returnedValue = response;
421
1046
  }
422
- }
423
- });
424
- }
425
- }
426
- return url;
427
- },
428
- formData: function(data) {
429
- var
430
- formData = {},
431
- hasOtherData,
432
- useFormDataApi = settings.serializeForm === 'formdata'
433
- ;
434
- data = data || originalData || settings.data;
435
- hasOtherData = $.isPlainObject(data);
436
1047
 
437
- if (useFormDataApi) {
438
- formData = new FormData($form[0]);
439
- settings.processData = typeof settings.processData !== 'undefined' ? settings.processData : false;
440
- settings.contentType = typeof settings.contentType !== 'undefined' ? settings.contentType : false;
441
- } else {
442
- var formArray = $form.serializeArray(),
443
- pushes = {},
444
- pushValues= {},
445
- build = function(base, key, value) {
446
- base[key] = value;
447
- return base;
448
- }
449
- ;
450
- // add files
451
- $.each($('input[type="file"]',$form), function(i, tag) {
452
- $.each($(tag)[0].files, function(j, file) {
453
- formArray.push({name:tag.name, value: file});
454
- });
455
- });
456
- $.each(formArray, function(i, el) {
457
- if (!settings.regExp.validate.test(el.name)) return;
458
- var isCheckbox = $('[name="' + el.name + '"]', $form).attr('type') === 'checkbox',
459
- floatValue = parseFloat(el.value),
460
- value = (isCheckbox && el.value === 'on') || el.value === 'true' || (String(floatValue) === el.value ? floatValue : (el.value === 'false' ? false : el.value)),
461
- nameKeys = el.name.match(settings.regExp.key) || [], k, pushKey= el.name.replace(/\[\]$/,'')
462
- ;
463
- if(!(pushKey in pushes)) {
464
- pushes[pushKey] = 0;
465
- pushValues[pushKey] = value;
466
- } else if (Array.isArray(pushValues[pushKey])) {
467
- pushValues[pushKey].push(value);
468
- } else {
469
- pushValues[pushKey] = [pushValues[pushKey] , value];
1048
+ return found;
1049
+ },
1050
+ };
1051
+
1052
+ if (methodInvoked) {
1053
+ if (instance === undefined) {
1054
+ module.initialize();
470
1055
  }
471
- value = pushValues[pushKey];
472
-
473
- while ((k = nameKeys.pop()) !== undefined) {
474
- // foo[]
475
- if (k == '' && !Array.isArray(value)){
476
- value = build([], pushes[pushKey]++, value);
477
- }
478
- // foo[n]
479
- else if (settings.regExp.fixed.test(k)) {
480
- value = build([], k, value);
481
- }
482
- // foo; foo[bar]
483
- else if (settings.regExp.named.test(k)) {
484
- value = build({}, k, value);
485
- }
1056
+ module.invoke(query);
1057
+ } else {
1058
+ if (instance !== undefined) {
1059
+ instance.invoke('destroy');
486
1060
  }
487
- formData = $.extend(true, formData, value);
488
- });
1061
+ module.initialize();
489
1062
  }
1063
+ });
490
1064
 
491
- if(hasOtherData) {
492
- module.debug('Extending existing data with form data', data, formData);
493
- if(useFormDataApi) {
494
- $.each(Object.keys(data),function(i, el){
495
- formData.append(el, data[el]);
496
- });
497
- data = formData;
498
- } else {
499
- data = $.extend(true, {}, data, formData);
500
- }
501
- }
502
- else {
503
- module.debug('Adding form data', formData);
504
- data = formData;
505
- }
506
- return data;
507
- }
508
- },
1065
+ return (returnedValue !== undefined)
1066
+ ? returnedValue
1067
+ : this;
1068
+ };
509
1069
 
510
- send: {
511
- request: function() {
512
- module.set.loading();
513
- module.request = module.create.request();
514
- if( module.is.mocked() ) {
515
- module.mockedXHR = module.create.mockedXHR();
516
- }
517
- else {
518
- module.xhr = module.create.xhr();
519
- }
520
- settings.onRequest.call(context, module.request, module.xhr);
521
- }
522
- },
1070
+ $.api.settings = {
523
1071
 
524
- event: {
525
- trigger: function(event) {
526
- module.query();
527
- if(event.type == 'submit' || event.type == 'click') {
528
- event.preventDefault();
529
- }
530
- },
531
- xhr: {
532
- always: function() {
533
- // nothing special
534
- },
535
- done: function(response, textStatus, xhr) {
536
- var
537
- context = this,
538
- elapsedTime = (new Date().getTime() - requestStartTime),
539
- timeLeft = (settings.loadingDuration - elapsedTime),
540
- translatedResponse = ( $.isFunction(settings.onResponse) )
541
- ? module.is.expectingJSON() && !settings.rawResponse
542
- ? settings.onResponse.call(context, $.extend(true, {}, response))
543
- : settings.onResponse.call(context, response)
544
- : false
545
- ;
546
- timeLeft = (timeLeft > 0)
547
- ? timeLeft
548
- : 0
549
- ;
550
- if(translatedResponse) {
551
- module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
552
- response = translatedResponse;
553
- }
554
- if(timeLeft > 0) {
555
- module.debug('Response completed early delaying state change by', timeLeft);
556
- }
557
- setTimeout(function() {
558
- if( module.is.validResponse(response) ) {
559
- module.request.resolveWith(context, [response, xhr]);
560
- }
561
- else {
562
- module.request.rejectWith(context, [xhr, 'invalid']);
563
- }
564
- }, timeLeft);
565
- },
566
- fail: function(xhr, status, httpMessage) {
567
- var
568
- context = this,
569
- elapsedTime = (new Date().getTime() - requestStartTime),
570
- timeLeft = (settings.loadingDuration - elapsedTime)
571
- ;
572
- timeLeft = (timeLeft > 0)
573
- ? timeLeft
574
- : 0
575
- ;
576
- if(timeLeft > 0) {
577
- module.debug('Response completed early delaying state change by', timeLeft);
578
- }
579
- setTimeout(function() {
580
- if( module.is.abortedRequest(xhr) ) {
581
- module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
582
- }
583
- else {
584
- module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
585
- }
586
- }, timeLeft);
587
- }
588
- },
589
- request: {
590
- done: function(response, xhr) {
591
- module.debug('Successful API Response', response);
592
- if(settings.cache === 'local' && url) {
593
- module.write.cachedResponse(url, response);
594
- module.debug('Saving server response locally', module.cache);
595
- }
596
- settings.onSuccess.call(context, response, $module, xhr);
597
- },
598
- complete: function(firstParameter, secondParameter) {
599
- var
600
- xhr,
601
- response
602
- ;
603
- // have to guess callback parameters based on request success
604
- if( module.was.successful() ) {
605
- response = firstParameter;
606
- xhr = secondParameter;
607
- }
608
- else {
609
- xhr = firstParameter;
610
- response = module.get.responseFromXHR(xhr);
611
- }
612
- module.remove.loading();
613
- settings.onComplete.call(context, response, $module, xhr);
614
- },
615
- fail: function(xhr, status, httpMessage) {
616
- var
617
- // pull response from xhr if available
618
- response = module.get.responseFromXHR(xhr),
619
- errorMessage = module.get.errorFromRequest(response, status, httpMessage)
620
- ;
621
- if(status == 'aborted') {
622
- module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
623
- settings.onAbort.call(context, status, $module, xhr);
624
- return true;
625
- }
626
- else if(status == 'invalid') {
627
- module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
628
- }
629
- else if(status == 'error') {
630
- if(xhr !== undefined) {
631
- module.debug('XHR produced a server error', status, httpMessage);
632
- // make sure we have an error to display to console
633
- if( (xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
634
- module.error(error.statusMessage + httpMessage, ajaxSettings.url);
635
- }
636
- settings.onError.call(context, errorMessage, $module, xhr);
637
- }
638
- }
1072
+ name: 'API',
1073
+ namespace: 'api',
639
1074
 
640
- if(settings.errorDuration && status !== 'aborted') {
641
- module.debug('Adding error state');
642
- module.set.error();
643
- if( module.should.removeError() ) {
644
- setTimeout(module.remove.error, settings.errorDuration);
645
- }
646
- }
647
- module.debug('API Request failed', errorMessage, xhr);
648
- settings.onFailure.call(context, response, $module, xhr);
649
- }
650
- }
651
- },
1075
+ debug: false,
1076
+ verbose: false,
1077
+ performance: true,
652
1078
 
653
- create: {
1079
+ // object containing all templates endpoints
1080
+ api: {},
654
1081
 
655
- request: function() {
656
- // api request promise
657
- return $.Deferred()
658
- .always(module.event.request.complete)
659
- .done(module.event.request.done)
660
- .fail(module.event.request.fail)
661
- ;
662
- },
1082
+ // whether to cache responses
1083
+ cache: true,
663
1084
 
664
- mockedXHR: function () {
665
- var
666
- // xhr does not simulate these properties of xhr but must return them
667
- textStatus = false,
668
- status = false,
669
- httpMessage = false,
670
- responder = settings.mockResponse || settings.response,
671
- asyncResponder = settings.mockResponseAsync || settings.responseAsync,
672
- asyncCallback,
673
- response,
674
- mockedXHR
675
- ;
1085
+ // whether new requests should abort previous requests
1086
+ interruptRequests: true,
676
1087
 
677
- mockedXHR = $.Deferred()
678
- .always(module.event.xhr.complete)
679
- .done(module.event.xhr.done)
680
- .fail(module.event.xhr.fail)
681
- ;
1088
+ // event binding
1089
+ on: 'auto',
682
1090
 
683
- if(responder) {
684
- if( $.isFunction(responder) ) {
685
- module.debug('Using specified synchronous callback', responder);
686
- response = responder.call(context, requestSettings);
687
- }
688
- else {
689
- module.debug('Using settings specified response', responder);
690
- response = responder;
691
- }
692
- // simulating response
693
- mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
694
- }
695
- else if( $.isFunction(asyncResponder) ) {
696
- asyncCallback = function(response) {
697
- module.debug('Async callback returned response', response);
1091
+ // context for applying state classes
1092
+ stateContext: false,
698
1093
 
699
- if(response) {
700
- mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
701
- }
702
- else {
703
- mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
704
- }
705
- };
706
- module.debug('Using specified async response callback', asyncResponder);
707
- asyncResponder.call(context, requestSettings, asyncCallback);
708
- }
709
- return mockedXHR;
710
- },
1094
+ // duration for loading state
1095
+ loadingDuration: 0,
711
1096
 
712
- xhr: function() {
713
- var
714
- xhr
715
- ;
716
- // ajax request promise
717
- xhr = $.ajax(ajaxSettings)
718
- .always(module.event.xhr.always)
719
- .done(module.event.xhr.done)
720
- .fail(module.event.xhr.fail)
721
- ;
722
- module.verbose('Created server request', xhr, ajaxSettings);
723
- return xhr;
724
- }
725
- },
1097
+ // whether to hide errors after a period of time
1098
+ hideError: 'auto',
726
1099
 
727
- set: {
728
- error: function() {
729
- module.verbose('Adding error state to element', $context);
730
- $context.addClass(className.error);
731
- },
732
- loading: function() {
733
- module.verbose('Adding loading state to element', $context);
734
- $context.addClass(className.loading);
735
- requestStartTime = new Date().getTime();
736
- }
737
- },
1100
+ // duration for error state
1101
+ errorDuration: 2000,
738
1102
 
739
- remove: {
740
- error: function() {
741
- module.verbose('Removing error state from element', $context);
742
- $context.removeClass(className.error);
743
- },
744
- loading: function() {
745
- module.verbose('Removing loading state from element', $context);
746
- $context.removeClass(className.loading);
747
- }
748
- },
1103
+ // whether parameters should be encoded with encodeURIComponent
1104
+ encodeParameters: true,
749
1105
 
750
- get: {
751
- normalizedData: function(){
752
- return typeof settings.data === "string" ? settings.data : JSON.stringify(settings.data, Object.keys(settings.data).sort());
753
- },
754
- responseFromXHR: function(xhr) {
755
- return $.isPlainObject(xhr)
756
- ? (module.is.expectingJSON())
757
- ? module.decode.json(xhr.responseText)
758
- : xhr.responseText
759
- : false
760
- ;
761
- },
762
- errorFromRequest: function(response, status, httpMessage) {
763
- return ($.isPlainObject(response) && response.error !== undefined)
764
- ? response.error // use json error message
765
- : (settings.error[status] !== undefined) // use server error message
766
- ? settings.error[status]
767
- : httpMessage
768
- ;
769
- },
770
- request: function() {
771
- return module.request || false;
772
- },
773
- xhr: function() {
774
- return module.xhr || false;
775
- },
776
- settings: function() {
777
- var
778
- runSettings
779
- ;
780
- runSettings = settings.beforeSend.call($module, settings);
781
- if(runSettings) {
782
- if(runSettings.success !== undefined) {
783
- module.debug('Legacy success callback detected', runSettings);
784
- module.error(error.legacyParameters, runSettings.success);
785
- runSettings.onSuccess = runSettings.success;
786
- }
787
- if(runSettings.failure !== undefined) {
788
- module.debug('Legacy failure callback detected', runSettings);
789
- module.error(error.legacyParameters, runSettings.failure);
790
- runSettings.onFailure = runSettings.failure;
791
- }
792
- if(runSettings.complete !== undefined) {
793
- module.debug('Legacy complete callback detected', runSettings);
794
- module.error(error.legacyParameters, runSettings.complete);
795
- runSettings.onComplete = runSettings.complete;
796
- }
797
- }
798
- if(runSettings === undefined) {
799
- module.error(error.noReturnedValue);
800
- }
801
- if(runSettings === false) {
802
- return runSettings;
803
- }
804
- return (runSettings !== undefined)
805
- ? $.extend(true, {}, runSettings)
806
- : $.extend(true, {}, settings)
807
- ;
808
- },
809
- urlEncodedValue: function(value) {
810
- var
811
- decodedValue = window.decodeURIComponent(value),
812
- encodedValue = window.encodeURIComponent(value),
813
- alreadyEncoded = (decodedValue !== value)
814
- ;
815
- if(alreadyEncoded) {
816
- module.debug('URL value is already encoded, avoiding double encoding', value);
817
- return value;
818
- }
819
- module.verbose('Encoding value using encodeURIComponent', value, encodedValue);
820
- return encodedValue;
821
- },
822
- defaultData: function() {
823
- var
824
- data = {}
825
- ;
826
- if( !$.isWindow(element) ) {
827
- if( module.is.input() ) {
828
- data.value = $module.val();
829
- }
830
- else if( module.is.form() ) {
831
-
832
- }
833
- else {
834
- data.text = $module.text();
835
- }
836
- }
837
- return data;
838
- },
839
- event: function() {
840
- if( $.isWindow(element) || settings.on == 'now' ) {
841
- module.debug('API called without element, no events attached');
842
- return false;
843
- }
844
- else if(settings.on == 'auto') {
845
- if( $module.is('input') ) {
846
- return (element.oninput !== undefined)
847
- ? 'input'
848
- : (element.onpropertychange !== undefined)
849
- ? 'propertychange'
850
- : 'keyup'
851
- ;
852
- }
853
- else if( $module.is('form') ) {
854
- return 'submit';
855
- }
856
- else {
857
- return 'click';
858
- }
859
- }
860
- else {
861
- return settings.on;
862
- }
863
- },
864
- templatedURL: function(action) {
865
- action = action || $module.data(metadata.action) || settings.action || false;
866
- url = $module.data(metadata.url) || settings.url || false;
867
- if(url) {
868
- module.debug('Using specified url', url);
869
- return url;
870
- }
871
- if(action) {
872
- module.debug('Looking up url for action', action, settings.api);
873
- if(settings.api[action] === undefined && !module.is.mocked()) {
874
- module.error(error.missingAction, settings.action, settings.api);
875
- return;
876
- }
877
- url = settings.api[action];
878
- }
879
- else if( module.is.form() ) {
880
- url = $module.attr('action') || $context.attr('action') || false;
881
- module.debug('No url or action specified, defaulting to form action', url);
882
- }
883
- return url;
884
- }
885
- },
1106
+ // API action to use
1107
+ action: false,
886
1108
 
887
- abort: function() {
888
- var
889
- xhr = module.get.xhr()
890
- ;
891
- if( xhr && xhr.state() !== 'resolved') {
892
- module.debug('Cancelling API request');
893
- xhr.abort();
894
- }
895
- },
1109
+ // templated URL to use
1110
+ url: false,
896
1111
 
897
- // reset state
898
- reset: function() {
899
- module.remove.error();
900
- module.remove.loading();
901
- },
1112
+ // base URL to apply to all endpoints
1113
+ base: '',
902
1114
 
903
- setting: function(name, value) {
904
- module.debug('Changing setting', name, value);
905
- if( $.isPlainObject(name) ) {
906
- $.extend(true, settings, name);
907
- }
908
- else if(value !== undefined) {
909
- if($.isPlainObject(settings[name])) {
910
- $.extend(true, settings[name], value);
911
- }
912
- else {
913
- settings[name] = value;
914
- }
915
- }
916
- else {
917
- return settings[name];
918
- }
919
- },
920
- internal: function(name, value) {
921
- if( $.isPlainObject(name) ) {
922
- $.extend(true, module, name);
923
- }
924
- else if(value !== undefined) {
925
- module[name] = value;
926
- }
927
- else {
928
- return module[name];
929
- }
930
- },
931
- debug: function() {
932
- if(!settings.silent && settings.debug) {
933
- if(settings.performance) {
934
- module.performance.log(arguments);
935
- }
936
- else {
937
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
938
- module.debug.apply(console, arguments);
939
- }
940
- }
941
- },
942
- verbose: function() {
943
- if(!settings.silent && settings.verbose && settings.debug) {
944
- if(settings.performance) {
945
- module.performance.log(arguments);
946
- }
947
- else {
948
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
949
- module.verbose.apply(console, arguments);
950
- }
951
- }
952
- },
953
- error: function() {
954
- if(!settings.silent) {
955
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
956
- module.error.apply(console, arguments);
957
- }
958
- },
959
- performance: {
960
- log: function(message) {
961
- var
962
- currentTime,
963
- executionTime,
964
- previousTime
965
- ;
966
- if(settings.performance) {
967
- currentTime = new Date().getTime();
968
- previousTime = time || currentTime;
969
- executionTime = currentTime - previousTime;
970
- time = currentTime;
971
- performance.push({
972
- 'Name' : message[0],
973
- 'Arguments' : [].slice.call(message, 1) || '',
974
- //'Element' : element,
975
- 'Execution Time' : executionTime
976
- });
977
- }
978
- clearTimeout(module.performance.timer);
979
- module.performance.timer = setTimeout(module.performance.display, 500);
980
- },
981
- display: function() {
982
- var
983
- title = settings.name + ':',
984
- totalTime = 0
985
- ;
986
- time = false;
987
- clearTimeout(module.performance.timer);
988
- $.each(performance, function(index, data) {
989
- totalTime += data['Execution Time'];
990
- });
991
- title += ' ' + totalTime + 'ms';
992
- if(moduleSelector) {
993
- title += ' \'' + moduleSelector + '\'';
994
- }
995
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
996
- console.groupCollapsed(title);
997
- if(console.table) {
998
- console.table(performance);
999
- }
1000
- else {
1001
- $.each(performance, function(index, data) {
1002
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
1003
- });
1004
- }
1005
- console.groupEnd();
1006
- }
1007
- performance = [];
1008
- }
1009
- },
1010
- invoke: function(query, passedArguments, context) {
1011
- var
1012
- object = instance,
1013
- maxDepth,
1014
- found,
1015
- response
1016
- ;
1017
- passedArguments = passedArguments || queryArguments;
1018
- context = context || element;
1019
- if(typeof query == 'string' && object !== undefined) {
1020
- query = query.split(/[\. ]/);
1021
- maxDepth = query.length - 1;
1022
- $.each(query, function(depth, value) {
1023
- var camelCaseValue = (depth != maxDepth)
1024
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1025
- : query
1026
- ;
1027
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
1028
- object = object[camelCaseValue];
1029
- }
1030
- else if( object[camelCaseValue] !== undefined ) {
1031
- found = object[camelCaseValue];
1032
- return false;
1033
- }
1034
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
1035
- object = object[value];
1036
- }
1037
- else if( object[value] !== undefined ) {
1038
- found = object[value];
1039
- return false;
1040
- }
1041
- else {
1042
- module.error(error.method, query);
1043
- return false;
1044
- }
1045
- });
1046
- }
1047
- if ( $.isFunction( found ) ) {
1048
- response = found.apply(context, passedArguments);
1049
- }
1050
- else if(found !== undefined) {
1051
- response = found;
1052
- }
1053
- if(Array.isArray(returnedValue)) {
1054
- returnedValue.push(response);
1055
- }
1056
- else if(returnedValue !== undefined) {
1057
- returnedValue = [returnedValue, response];
1058
- }
1059
- else if(response !== undefined) {
1060
- returnedValue = response;
1061
- }
1062
- return found;
1063
- }
1064
- };
1065
-
1066
- if(methodInvoked) {
1067
- if(instance === undefined) {
1068
- module.initialize();
1069
- }
1070
- module.invoke(query);
1071
- }
1072
- else {
1073
- if(instance !== undefined) {
1074
- instance.invoke('destroy');
1075
- }
1076
- module.initialize();
1077
- }
1078
- })
1079
- ;
1080
-
1081
- return (returnedValue !== undefined)
1082
- ? returnedValue
1083
- : this
1084
- ;
1085
- };
1086
-
1087
- $.api.settings = {
1088
-
1089
- name : 'API',
1090
- namespace : 'api',
1091
-
1092
- debug : false,
1093
- verbose : false,
1094
- performance : true,
1095
-
1096
- // object containing all templates endpoints
1097
- api : {},
1098
-
1099
- // whether to cache responses
1100
- cache : true,
1101
-
1102
- // whether new requests should abort previous requests
1103
- interruptRequests : true,
1104
-
1105
- // event binding
1106
- on : 'auto',
1107
-
1108
- // context for applying state classes
1109
- stateContext : false,
1110
-
1111
- // duration for loading state
1112
- loadingDuration : 0,
1113
-
1114
- // whether to hide errors after a period of time
1115
- hideError : 'auto',
1116
-
1117
- // duration for error state
1118
- errorDuration : 2000,
1119
-
1120
- // whether parameters should be encoded with encodeURIComponent
1121
- encodeParameters : true,
1122
-
1123
- // API action to use
1124
- action : false,
1125
-
1126
- // templated URL to use
1127
- url : false,
1128
-
1129
- // base URL to apply to all endpoints
1130
- base : '',
1131
-
1132
- // data that will
1133
- urlData : {},
1134
-
1135
- // whether to add default data to url data
1136
- defaultData : true,
1137
-
1138
- // whether to serialize closest form
1139
- // use true to convert complex named keys like a[b][1][c][] into a nested object
1140
- // use 'formdata' for formdata web api
1141
- serializeForm : false,
1142
-
1143
- // how long to wait before request should occur
1144
- throttle : 0,
1145
-
1146
- // whether to throttle first request or only repeated
1147
- throttleFirstRequest : true,
1148
-
1149
- // standard ajax settings
1150
- method : 'get',
1151
- data : {},
1152
- dataType : 'json',
1153
-
1154
- // mock response
1155
- mockResponse : false,
1156
- mockResponseAsync : false,
1157
-
1158
- // aliases for mock
1159
- response : false,
1160
- responseAsync : false,
1161
-
1162
- // whether onResponse should work with response value without force converting into an object
1163
- rawResponse : true,
1164
-
1165
- // callbacks before request
1166
- beforeSend : function(settings) { return settings; },
1167
- beforeXHR : function(xhr) {},
1168
- onRequest : function(promise, xhr) {},
1169
-
1170
- // after request
1171
- onResponse : false, // function(response) { },
1115
+ // data that will
1116
+ urlData: {},
1117
+
1118
+ // whether to add default data to url data
1119
+ defaultData: true,
1172
1120
 
1173
- // response was successful, if JSON passed validation
1174
- onSuccess : function(response, $module) {},
1121
+ // whether to serialize closest form
1122
+ // use true to convert complex named keys like a[b][1][c][] into a nested object
1123
+ // use 'formdata' for formdata web api
1124
+ serializeForm: false,
1175
1125
 
1176
- // request finished without aborting
1177
- onComplete : function(response, $module) {},
1126
+ // how long to wait before request should occur
1127
+ throttle: 0,
1178
1128
 
1179
- // failed JSON success test
1180
- onFailure : function(response, $module) {},
1129
+ // whether to throttle first request or only repeated
1130
+ throttleFirstRequest: true,
1181
1131
 
1182
- // server error
1183
- onError : function(errorMessage, $module) {},
1132
+ // standard ajax settings
1133
+ method: 'get',
1134
+ data: {},
1135
+ dataType: 'json',
1184
1136
 
1185
- // request aborted
1186
- onAbort : function(errorMessage, $module) {},
1137
+ // mock response
1138
+ mockResponse: false,
1139
+ mockResponseAsync: false,
1187
1140
 
1188
- successTest : false,
1141
+ // aliases for mock
1142
+ response: false,
1143
+ responseAsync: false,
1189
1144
 
1190
- // errors
1191
- error : {
1192
- beforeSend : 'The before send function has aborted the request',
1193
- error : 'There was an error with your request',
1194
- exitConditions : 'API Request Aborted. Exit conditions met',
1195
- JSONParse : 'JSON could not be parsed during error handling',
1196
- legacyParameters : 'You are using legacy API success callback names',
1197
- method : 'The method you called is not defined',
1198
- missingAction : 'API action used but no url was defined',
1199
- missingURL : 'No URL specified for api event',
1200
- noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.',
1201
- noStorage : 'Caching responses locally requires session storage',
1202
- parseError : 'There was an error parsing your request',
1203
- requiredParameter : 'Missing a required URL parameter: ',
1204
- statusMessage : 'Server gave an error: ',
1205
- timeout : 'Your request timed out'
1206
- },
1145
+ // whether onResponse should work with response value without force converting into an object
1146
+ rawResponse: true,
1207
1147
 
1208
- regExp : {
1209
- required : /\{\$*[a-z0-9]+\}/gi,
1210
- optional : /\{\/\$*[a-z0-9]+\}/gi,
1211
- validate: /^[a-z_][a-z0-9_-]*(?:\[[a-z0-9_-]*\])*$/i,
1212
- key: /[a-z0-9_-]+|(?=\[\])/gi,
1213
- push: /^$/,
1214
- fixed: /^\d+$/,
1215
- named: /^[a-z0-9_-]+$/i
1216
- },
1217
-
1218
- className: {
1219
- loading : 'loading',
1220
- error : 'error'
1221
- },
1148
+ // callbacks before request
1149
+ beforeSend: function (settings) {
1150
+ return settings;
1151
+ },
1152
+ beforeXHR: function (xhr) {},
1153
+ onRequest: function (promise, xhr) {},
1154
+
1155
+ // after request
1156
+ onResponse: false, // function(response) { },
1157
+
1158
+ // response was successful, if JSON passed validation
1159
+ onSuccess: function (response, $module) {},
1160
+
1161
+ // request finished without aborting
1162
+ onComplete: function (response, $module) {},
1163
+
1164
+ // failed JSON success test
1165
+ onFailure: function (response, $module) {},
1166
+
1167
+ // server error
1168
+ onError: function (errorMessage, $module) {},
1169
+
1170
+ // request aborted
1171
+ onAbort: function (errorMessage, $module) {},
1172
+
1173
+ successTest: false,
1174
+
1175
+ // errors
1176
+ error: {
1177
+ beforeSend: 'The before send function has aborted the request',
1178
+ error: 'There was an error with your request',
1179
+ exitConditions: 'API Request Aborted. Exit conditions met',
1180
+ JSONParse: 'JSON could not be parsed during error handling',
1181
+ legacyParameters: 'You are using legacy API success callback names',
1182
+ method: 'The method you called is not defined',
1183
+ missingAction: 'API action used but no url was defined',
1184
+ missingURL: 'No URL specified for api event',
1185
+ noReturnedValue: 'The beforeSend callback must return a settings object, beforeSend ignored.',
1186
+ noStorage: 'Caching responses locally requires session storage',
1187
+ parseError: 'There was an error parsing your request',
1188
+ requiredParameter: 'Missing a required URL parameter: ',
1189
+ statusMessage: 'Server gave an error: ',
1190
+ timeout: 'Your request timed out',
1191
+ },
1222
1192
 
1223
- selector: {
1224
- disabled : '.disabled',
1225
- form : 'form'
1226
- },
1227
-
1228
- metadata: {
1229
- action : 'action',
1230
- url : 'url'
1231
- }
1232
- };
1233
-
1234
-
1235
-
1236
- })( jQuery, window, document );
1193
+ regExp: {
1194
+ required: /\{\$*[a-z0-9]+\}/gi,
1195
+ optional: /\{\/\$*[a-z0-9]+\}/gi,
1196
+ validate: /^[a-z_][a-z0-9_-]*(?:\[[a-z0-9_-]*\])*$/i,
1197
+ key: /[a-z0-9_-]+|(?=\[\])/gi,
1198
+ push: /^$/,
1199
+ fixed: /^\d+$/,
1200
+ named: /^[a-z0-9_-]+$/i,
1201
+ },
1202
+
1203
+ className: {
1204
+ loading: 'loading',
1205
+ error: 'error',
1206
+ },
1207
+
1208
+ selector: {
1209
+ disabled: '.disabled',
1210
+ form: 'form',
1211
+ },
1212
+
1213
+ metadata: {
1214
+ action: 'action',
1215
+ url: 'url',
1216
+ },
1217
+ };
1218
+ })(jQuery, window, document);