fomantic-ui 2.9.1-beta.16 → 2.9.1-beta.18

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