fomantic-ui 2.9.1-beta.17 → 2.9.1-beta.19

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 +569 -598
  5. package/dist/components/accordion.min.css +1 -1
  6. package/dist/components/accordion.min.js +2 -2
  7. package/dist/components/ad.css +1 -1
  8. package/dist/components/ad.min.css +1 -1
  9. package/dist/components/api.js +1161 -1184
  10. package/dist/components/api.min.js +2 -2
  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 +1895 -1818
  17. package/dist/components/calendar.min.css +1 -1
  18. package/dist/components/calendar.min.js +2 -2
  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 +842 -841
  23. package/dist/components/checkbox.min.css +1 -1
  24. package/dist/components/checkbox.min.js +2 -2
  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 +708 -737
  31. package/dist/components/dimmer.min.css +1 -1
  32. package/dist/components/dimmer.min.js +2 -2
  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 +4161 -4238
  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 +651 -675
  41. package/dist/components/embed.min.css +1 -1
  42. package/dist/components/embed.min.js +2 -2
  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 +1464 -1467
  49. package/dist/components/flyout.min.css +2 -2
  50. package/dist/components/flyout.min.js +2 -2
  51. package/dist/components/form.css +1 -1
  52. package/dist/components/form.js +1979 -2004
  53. package/dist/components/form.min.css +1 -1
  54. package/dist/components/form.min.js +2 -2
  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 +1491 -1487
  77. package/dist/components/modal.min.css +2 -2
  78. package/dist/components/modal.min.js +2 -2
  79. package/dist/components/nag.css +1 -1
  80. package/dist/components/nag.js +518 -529
  81. package/dist/components/nag.min.css +1 -1
  82. package/dist/components/nag.min.js +2 -2
  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 +1437 -1456
  87. package/dist/components/popup.min.css +1 -1
  88. package/dist/components/popup.min.js +2 -2
  89. package/dist/components/progress.css +1 -1
  90. package/dist/components/progress.js +969 -997
  91. package/dist/components/progress.min.css +1 -1
  92. package/dist/components/progress.min.js +2 -2
  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 +505 -523
  97. package/dist/components/rating.min.css +1 -1
  98. package/dist/components/rating.min.js +2 -2
  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 +1498 -1534
  105. package/dist/components/search.min.css +2 -2
  106. package/dist/components/search.min.js +2 -2
  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 +792 -809
  111. package/dist/components/shape.min.css +1 -1
  112. package/dist/components/shape.min.js +2 -2
  113. package/dist/components/sidebar.css +3 -1
  114. package/dist/components/sidebar.js +1071 -1098
  115. package/dist/components/sidebar.min.css +2 -2
  116. package/dist/components/sidebar.min.js +2 -2
  117. package/dist/components/site.css +1 -1
  118. package/dist/components/site.js +462 -471
  119. package/dist/components/site.min.css +1 -1
  120. package/dist/components/site.min.js +2 -2
  121. package/dist/components/slider.js +1287 -1311
  122. package/dist/components/slider.min.js +2 -2
  123. package/dist/components/state.js +639 -657
  124. package/dist/components/state.min.js +2 -2
  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 +857 -903
  131. package/dist/components/sticky.min.css +1 -1
  132. package/dist/components/sticky.min.js +2 -2
  133. package/dist/components/tab.css +1 -1
  134. package/dist/components/tab.js +922 -963
  135. package/dist/components/tab.min.css +1 -1
  136. package/dist/components/tab.min.js +2 -2
  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 +886 -890
  143. package/dist/components/toast.min.css +1 -1
  144. package/dist/components/toast.min.js +2 -2
  145. package/dist/components/transition.css +1 -1
  146. package/dist/components/transition.js +1041 -1077
  147. package/dist/components/transition.min.css +1 -1
  148. package/dist/components/transition.min.js +2 -2
  149. package/dist/components/visibility.js +1220 -1244
  150. package/dist/components/visibility.min.js +2 -2
  151. package/dist/semantic.css +84 -60
  152. package/dist/semantic.js +28949 -29435
  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 +1162 -1185
  160. package/src/definitions/behaviors/form.js +1978 -2003
  161. package/src/definitions/behaviors/state.js +645 -663
  162. package/src/definitions/behaviors/visibility.js +1219 -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 +461 -470
  167. package/src/definitions/modules/accordion.js +568 -597
  168. package/src/definitions/modules/calendar.js +1894 -1817
  169. package/src/definitions/modules/checkbox.js +841 -840
  170. package/src/definitions/modules/dimmer.js +707 -736
  171. package/src/definitions/modules/dropdown.js +4160 -4237
  172. package/src/definitions/modules/dropdown.less +5 -8
  173. package/src/definitions/modules/embed.js +650 -674
  174. package/src/definitions/modules/flyout.js +1463 -1466
  175. package/src/definitions/modules/flyout.less +15 -12
  176. package/src/definitions/modules/modal.js +1490 -1486
  177. package/src/definitions/modules/modal.less +3 -0
  178. package/src/definitions/modules/nag.js +517 -528
  179. package/src/definitions/modules/popup.js +1436 -1455
  180. package/src/definitions/modules/progress.js +968 -996
  181. package/src/definitions/modules/rating.js +504 -522
  182. package/src/definitions/modules/search.js +1497 -1533
  183. package/src/definitions/modules/search.less +1 -0
  184. package/src/definitions/modules/shape.js +791 -808
  185. package/src/definitions/modules/sidebar.js +1070 -1097
  186. package/src/definitions/modules/sidebar.less +1 -0
  187. package/src/definitions/modules/slider.js +1286 -1310
  188. package/src/definitions/modules/sticky.js +873 -919
  189. package/src/definitions/modules/tab.js +921 -962
  190. package/src/definitions/modules/toast.js +885 -889
  191. package/src/definitions/modules/transition.js +1040 -1076
  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.17+3767ba8 - API
2
+ * # Fomantic-UI 2.9.1-beta.19+e589cd1 - API
3
3
  * https://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
@@ -8,1234 +8,1211 @@
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';
98
13
 
99
- instantiate: function() {
100
- module.verbose('Storing instance of module', module);
101
- instance = module;
102
- $module
103
- .data(moduleNamespace, instance)
104
- ;
105
- },
14
+ function isWindow(obj) {
15
+ return obj != null && obj === obj.window;
16
+ }
106
17
 
107
- destroy: function() {
108
- module.verbose('Destroying previous module for', element);
109
- $module
110
- .removeData(moduleNamespace)
111
- .off(eventNamespace)
112
- ;
113
- },
18
+ function isFunction(obj) {
19
+ return typeof obj === 'function' && typeof obj.nodeType !== 'number';
20
+ }
114
21
 
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
- },
22
+ window = (typeof window != 'undefined' && window.Math == Math)
23
+ ? window
24
+ : globalThis;
132
25
 
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
- },
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 = [],
146
35
 
147
- read: {
148
- cachedResponse: function(url) {
36
+ query = arguments[0],
37
+ methodInvoked = (typeof query == 'string'),
38
+ queryArguments = [].slice.call(arguments, 1),
39
+
40
+ returnedValue
41
+ ;
42
+
43
+ $allModules.each(function () {
149
44
  var
150
- response
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
151
82
  ;
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
- },
179
83
 
180
- query: function() {
84
+ module = {
181
85
 
182
- if(module.is.disabled()) {
183
- module.debug('Element is disabled API request aborted');
184
- return;
185
- }
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
+ },
186
101
 
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
- }
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
+ }
280
184
 
281
- },
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');
282
191
 
283
- should: {
284
- removeError: function() {
285
- return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
286
- }
287
- },
192
+ return;
193
+ }
194
+ }
288
195
 
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
- },
196
+ // pass element metadata to url (value, text)
197
+ if (settings.defaultData) {
198
+ $.extend(true, settings.urlData, module.get.defaultData());
199
+ }
337
200
 
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
- },
201
+ // Add form content
202
+ if (settings.serializeForm) {
203
+ settings.data = module.add.formData(originalData || settings.data);
204
+ }
352
205
 
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
390
- ;
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, '');
206
+ // call beforesend and get any settings changes
207
+ requestSettings = module.get.settings();
208
+
209
+ // check if before send cancelled request
210
+ if (requestSettings === false) {
211
+ module.cancelled = true;
212
+ module.error(error.beforeSend);
213
+
214
+ return;
215
+ } else {
216
+ module.cancelled = false;
421
217
  }
422
- else {
423
- url = url.replace(templatedString, '');
218
+
219
+ // get url
220
+ url = module.get.templatedURL();
221
+
222
+ if (!url && !module.is.mocked()) {
223
+ module.error(error.missingURL);
224
+
225
+ return;
424
226
  }
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
227
 
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
- }
228
+ // replace variables
229
+ url = module.add.urlData(url);
230
+ // missing url parameters
231
+ if (!url && !module.is.mocked()) {
232
+ return;
233
+ }
477
234
 
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
- }
491
- }
492
- formData = $.extend(true, formData, value);
493
- });
494
- }
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
+ }
495
257
 
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
- },
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()
887
+ ;
888
+ if (xhr && xhr.state() !== 'resolved') {
889
+ module.debug('Cancelling API request');
890
+ xhr.abort();
891
+ }
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;
1046
+ }
514
1047
 
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
- },
1048
+ return found;
1049
+ },
1050
+ };
528
1051
 
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]);
1052
+ if (methodInvoked) {
1053
+ if (instance === undefined) {
1054
+ module.initialize();
565
1055
  }
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]);
1056
+ module.invoke(query);
1057
+ } else {
1058
+ if (instance !== undefined) {
1059
+ instance.invoke('destroy');
590
1060
  }
591
- }, timeLeft);
1061
+ module.initialize();
592
1062
  }
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
- }
1063
+ });
644
1064
 
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
- },
1065
+ return (returnedValue !== undefined)
1066
+ ? returnedValue
1067
+ : this;
1068
+ };
657
1069
 
658
- create: {
1070
+ $.api.settings = {
659
1071
 
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
- },
1072
+ name: 'API',
1073
+ namespace: 'api',
668
1074
 
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
- ;
1075
+ debug: false,
1076
+ verbose: false,
1077
+ performance: true,
681
1078
 
682
- mockedXHR = $.Deferred()
683
- .always(module.event.xhr.complete)
684
- .done(module.event.xhr.done)
685
- .fail(module.event.xhr.fail)
686
- ;
1079
+ // object containing all templates endpoints
1080
+ api: {},
687
1081
 
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);
1082
+ // whether to cache responses
1083
+ cache: true,
703
1084
 
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
- },
1085
+ // whether new requests should abort previous requests
1086
+ interruptRequests: true,
716
1087
 
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
- },
1088
+ // event binding
1089
+ on: 'auto',
731
1090
 
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
- },
1091
+ // context for applying state classes
1092
+ stateContext: false,
743
1093
 
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
- },
1094
+ // duration for loading state
1095
+ loadingDuration: 0,
754
1096
 
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
- },
1097
+ // whether to hide errors after a period of time
1098
+ hideError: 'auto',
891
1099
 
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
- },
1100
+ // duration for error state
1101
+ errorDuration: 2000,
901
1102
 
902
- // reset state
903
- reset: function() {
904
- module.remove.error();
905
- module.remove.loading();
906
- },
1103
+ // whether parameters should be encoded with encodeURIComponent
1104
+ encodeParameters: true,
907
1105
 
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) { },
1106
+ // API action to use
1107
+ action: false,
1108
+
1109
+ // templated URL to use
1110
+ url: false,
1111
+
1112
+ // base URL to apply to all endpoints
1113
+ base: '',
1177
1114
 
1178
- // response was successful, if JSON passed validation
1179
- onSuccess : function(response, $module) {},
1115
+ // data that will
1116
+ urlData: {},
1180
1117
 
1181
- // request finished without aborting
1182
- onComplete : function(response, $module) {},
1118
+ // whether to add default data to url data
1119
+ defaultData: true,
1183
1120
 
1184
- // failed JSON success test
1185
- onFailure : 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,
1186
1125
 
1187
- // server error
1188
- onError : function(errorMessage, $module) {},
1126
+ // how long to wait before request should occur
1127
+ throttle: 0,
1189
1128
 
1190
- // request aborted
1191
- onAbort : function(errorMessage, $module) {},
1129
+ // whether to throttle first request or only repeated
1130
+ throttleFirstRequest: true,
1192
1131
 
1193
- successTest : false,
1132
+ // standard ajax settings
1133
+ method: 'get',
1134
+ data: {},
1135
+ dataType: 'json',
1194
1136
 
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
- },
1137
+ // mock response
1138
+ mockResponse: false,
1139
+ mockResponseAsync: false,
1212
1140
 
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
- },
1141
+ // aliases for mock
1142
+ response: false,
1143
+ responseAsync: false,
1227
1144
 
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 );
1145
+ // whether onResponse should work with response value without force converting into an object
1146
+ rawResponse: true,
1147
+
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
+ },
1192
+
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);