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