semantic-ui-sass 1.12.3.0 → 2.0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/app/assets/javascripts/semantic-ui.js +1 -0
  4. data/app/assets/javascripts/semantic-ui/accordion.js +67 -53
  5. data/app/assets/javascripts/semantic-ui/api.js +395 -189
  6. data/app/assets/javascripts/semantic-ui/checkbox.js +322 -114
  7. data/app/assets/javascripts/semantic-ui/colorize.js +4 -2
  8. data/app/assets/javascripts/semantic-ui/dimmer.js +74 -50
  9. data/app/assets/javascripts/semantic-ui/dropdown.js +2046 -584
  10. data/app/assets/javascripts/semantic-ui/embed.js +662 -0
  11. data/app/assets/javascripts/semantic-ui/form.js +345 -163
  12. data/app/assets/javascripts/semantic-ui/modal.js +119 -90
  13. data/app/assets/javascripts/semantic-ui/nag.js +8 -9
  14. data/app/assets/javascripts/semantic-ui/popup.js +390 -228
  15. data/app/assets/javascripts/semantic-ui/progress.js +112 -103
  16. data/app/assets/javascripts/semantic-ui/rating.js +79 -55
  17. data/app/assets/javascripts/semantic-ui/search.js +305 -123
  18. data/app/assets/javascripts/semantic-ui/shape.js +94 -48
  19. data/app/assets/javascripts/semantic-ui/sidebar.js +84 -151
  20. data/app/assets/javascripts/semantic-ui/site.js +5 -5
  21. data/app/assets/javascripts/semantic-ui/state.js +4 -4
  22. data/app/assets/javascripts/semantic-ui/sticky.js +108 -35
  23. data/app/assets/javascripts/semantic-ui/tab.js +220 -125
  24. data/app/assets/javascripts/semantic-ui/transition.js +205 -171
  25. data/app/assets/javascripts/semantic-ui/visibility.js +220 -100
  26. data/app/assets/javascripts/semantic-ui/visit.js +6 -4
  27. data/app/assets/stylesheets/semantic-ui/collections/_breadcrumb.scss +17 -16
  28. data/app/assets/stylesheets/semantic-ui/collections/_form.scss +223 -121
  29. data/app/assets/stylesheets/semantic-ui/collections/_grid.scss +462 -448
  30. data/app/assets/stylesheets/semantic-ui/collections/_menu.scss +949 -665
  31. data/app/assets/stylesheets/semantic-ui/collections/_message.scss +134 -92
  32. data/app/assets/stylesheets/semantic-ui/collections/_table.scss +270 -208
  33. data/app/assets/stylesheets/semantic-ui/elements/_all.scss +1 -0
  34. data/app/assets/stylesheets/semantic-ui/elements/_button.scss +1357 -521
  35. data/app/assets/stylesheets/semantic-ui/elements/_container.scss +125 -0
  36. data/app/assets/stylesheets/semantic-ui/elements/_divider.scss +51 -31
  37. data/app/assets/stylesheets/semantic-ui/elements/_flag.scss +3 -3
  38. data/app/assets/stylesheets/semantic-ui/elements/_header.scss +270 -144
  39. data/app/assets/stylesheets/semantic-ui/elements/_icon.scss +241 -110
  40. data/app/assets/stylesheets/semantic-ui/elements/_image.scss +30 -16
  41. data/app/assets/stylesheets/semantic-ui/elements/_input.scss +88 -53
  42. data/app/assets/stylesheets/semantic-ui/elements/_label.scss +432 -281
  43. data/app/assets/stylesheets/semantic-ui/elements/_list.scss +172 -128
  44. data/app/assets/stylesheets/semantic-ui/elements/_loader.scss +16 -14
  45. data/app/assets/stylesheets/semantic-ui/elements/_rail.scss +15 -7
  46. data/app/assets/stylesheets/semantic-ui/elements/_reveal.scss +32 -13
  47. data/app/assets/stylesheets/semantic-ui/elements/_segment.scss +329 -212
  48. data/app/assets/stylesheets/semantic-ui/elements/_step.scss +291 -99
  49. data/app/assets/stylesheets/semantic-ui/globals/_reset.scss +2 -2
  50. data/app/assets/stylesheets/semantic-ui/globals/_site.scss +19 -18
  51. data/app/assets/stylesheets/semantic-ui/modules/_accordion.scss +17 -18
  52. data/app/assets/stylesheets/semantic-ui/modules/_all.scss +1 -0
  53. data/app/assets/stylesheets/semantic-ui/modules/_checkbox.scss +265 -161
  54. data/app/assets/stylesheets/semantic-ui/modules/_dimmer.scss +29 -15
  55. data/app/assets/stylesheets/semantic-ui/modules/_dropdown.scss +441 -156
  56. data/app/assets/stylesheets/semantic-ui/modules/_embed.scss +168 -0
  57. data/app/assets/stylesheets/semantic-ui/modules/_modal.scss +163 -85
  58. data/app/assets/stylesheets/semantic-ui/modules/_nag.scss +8 -8
  59. data/app/assets/stylesheets/semantic-ui/modules/_popup.scss +88 -23
  60. data/app/assets/stylesheets/semantic-ui/modules/_progress.scss +185 -129
  61. data/app/assets/stylesheets/semantic-ui/modules/_rating.scss +75 -60
  62. data/app/assets/stylesheets/semantic-ui/modules/_search.scss +99 -52
  63. data/app/assets/stylesheets/semantic-ui/modules/_shape.scss +11 -11
  64. data/app/assets/stylesheets/semantic-ui/modules/_sidebar.scss +16 -12
  65. data/app/assets/stylesheets/semantic-ui/modules/_sticky.scss +4 -4
  66. data/app/assets/stylesheets/semantic-ui/modules/_tab.scss +3 -3
  67. data/app/assets/stylesheets/semantic-ui/modules/_transition.scss +31 -39
  68. data/app/assets/stylesheets/semantic-ui/views/_ad.scss +3 -3
  69. data/app/assets/stylesheets/semantic-ui/views/_all.scss +1 -0
  70. data/app/assets/stylesheets/semantic-ui/views/_card.scss +204 -162
  71. data/app/assets/stylesheets/semantic-ui/views/_comment.scss +6 -6
  72. data/app/assets/stylesheets/semantic-ui/views/_feed.scss +51 -26
  73. data/app/assets/stylesheets/semantic-ui/views/_item.scss +62 -36
  74. data/app/assets/stylesheets/semantic-ui/views/_statistic.scss +265 -90
  75. data/lib/semantic/ui/sass/version.rb +2 -2
  76. data/semantic-ui-sass.gemspec +2 -2
  77. metadata +9 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a33808ada5b475e8802efba4fe1fba3d6fe02bc3
4
- data.tar.gz: b408a272d99c2c0639e21bb5e2034f6f6d1f436c
3
+ metadata.gz: 44768d4c302fb43981b9438c4e3bb7433b56930f
4
+ data.tar.gz: 71675fc771bf3900d058b65ffcb05a96eb32e13e
5
5
  SHA512:
6
- metadata.gz: 8fa04803e8b55ff9aa1e43883765d314bd3438990c11818ababdde3907e208692d0c8a008529ef0a533e10df6df8866c5e4fa3cb477ebe13ffebddec67df93eb
7
- data.tar.gz: 2a2cdd90c8e7c4e139f6efab024de091be4416f4c047d9f23d76fc4b64ba7a74dbad11e108921e26d441ea20847b0b2341dcfac4a0e10af57fa4ace01e381e1d
6
+ metadata.gz: 0f2da6aae2d0588f5c3dc54e19a70dcad76392313aaf23fb0f48daa15e04b9933c47c046a2fcc08590f79ad1b375ad415216464330e4589f8e66999a5e2964c5
7
+ data.tar.gz: bf6b181a52f0481566e803aeef934b59b3be76961ba57caae8fd06c7aa142090e565c677bfb8ede87ed2625442f0236e0499e7d77432c76b8e28e34747df19fe
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 2.0.4.0
2
+
3
+ * Update Semantic UI to 2.0.4
4
+
1
5
  ## 1.12.3.0
2
6
 
3
7
  * Update Semantic UI to 1.12.3
@@ -1,5 +1,6 @@
1
1
  //= require semantic-ui/api
2
2
  //= require semantic-ui/colorize
3
+ //= require semantic-ui/embed
3
4
  //= require semantic-ui/form
4
5
  //= require semantic-ui/state
5
6
  //= require semantic-ui/visibility
@@ -1,9 +1,9 @@
1
1
  /*!
2
- * # Semantic UI 1.12.3 - Accordion
2
+ * # Semantic UI - Accordion
3
3
  * http://github.com/semantic-org/semantic-ui/
4
4
  *
5
5
  *
6
- * Copyright 2014 Contributors
6
+ * Copyright 2015 Contributors
7
7
  * Released under the MIT license
8
8
  * http://opensource.org/licenses/MIT
9
9
  *
@@ -105,7 +105,7 @@ $.fn.accordion = function(parameters) {
105
105
  events: function() {
106
106
  module.debug('Binding delegated events');
107
107
  $module
108
- .on('click' + eventNamespace, selector.trigger, module.event.click)
108
+ .on(settings.on + eventNamespace, selector.trigger, module.event.click)
109
109
  ;
110
110
  }
111
111
  },
@@ -153,54 +153,59 @@ $.fn.accordion = function(parameters) {
153
153
  $activeContent = $activeTitle.next($content),
154
154
  isAnimating = $activeContent.hasClass(className.animating),
155
155
  isActive = $activeContent.hasClass(className.active),
156
- isUnopen = (!isActive && !isAnimating)
156
+ isOpen = (isActive || isAnimating)
157
157
  ;
158
- if(isUnopen) {
159
- module.debug('Opening accordion content', $activeTitle);
160
- if(settings.exclusive) {
161
- module.closeOthers.call($activeTitle);
158
+ if(isOpen) {
159
+ module.debug('Accordion already open, skipping', $activeContent);
160
+ return;
161
+ }
162
+ module.debug('Opening accordion content', $activeTitle);
163
+ settings.onOpening.call($activeContent);
164
+ if(settings.exclusive) {
165
+ module.closeOthers.call($activeTitle);
166
+ }
167
+ $activeTitle
168
+ .addClass(className.active)
169
+ ;
170
+ $activeContent
171
+ .stop(true, true)
172
+ .addClass(className.animating)
173
+ ;
174
+ if(settings.animateChildren) {
175
+ if($.fn.transition !== undefined && $module.transition('is supported')) {
176
+ $activeContent
177
+ .children()
178
+ .transition({
179
+ animation : 'fade in',
180
+ queue : false,
181
+ useFailSafe : true,
182
+ debug : settings.debug,
183
+ verbose : settings.verbose,
184
+ duration : settings.duration
185
+ })
186
+ ;
162
187
  }
163
- $activeTitle
164
- .addClass(className.active)
165
- ;
166
- $activeContent.addClass(className.animating);
167
- if(settings.animateChildren) {
168
- if($.fn.transition !== undefined && $module.transition('is supported')) {
169
- $activeContent
170
- .children()
171
- .transition({
172
- animation : 'fade in',
173
- queue : false,
174
- useFailSafe : true,
175
- debug : settings.debug,
176
- verbose : settings.verbose,
177
- duration : settings.duration
178
- })
179
- ;
180
- }
181
- else {
182
- $activeContent
183
- .children()
184
- .stop(true)
185
- .animate({
186
- opacity: 1
187
- }, settings.duration, module.resetOpacity)
188
- ;
189
- }
188
+ else {
189
+ $activeContent
190
+ .children()
191
+ .stop(true, true)
192
+ .animate({
193
+ opacity: 1
194
+ }, settings.duration, module.resetOpacity)
195
+ ;
190
196
  }
191
- $activeContent
192
- .stop(true)
193
- .slideDown(settings.duration, settings.easing, function() {
194
- $activeContent
195
- .removeClass(className.animating)
196
- .addClass(className.active)
197
- ;
198
- module.reset.display.call(this);
199
- settings.onOpen.call(this);
200
- settings.onChange.call(this);
201
- })
202
- ;
203
197
  }
198
+ $activeContent
199
+ .slideDown(settings.duration, settings.easing, function() {
200
+ $activeContent
201
+ .removeClass(className.animating)
202
+ .addClass(className.active)
203
+ ;
204
+ module.reset.display.call(this);
205
+ settings.onOpen.call(this);
206
+ settings.onChange.call(this);
207
+ })
208
+ ;
204
209
  },
205
210
 
206
211
  close: function(query) {
@@ -218,10 +223,12 @@ $.fn.accordion = function(parameters) {
218
223
  ;
219
224
  if((isActive || isOpening) && !isClosing) {
220
225
  module.debug('Closing accordion content', $activeContent);
226
+ settings.onClosing.call($activeContent);
221
227
  $activeTitle
222
228
  .removeClass(className.active)
223
229
  ;
224
230
  $activeContent
231
+ .stop(true, true)
225
232
  .addClass(className.animating)
226
233
  ;
227
234
  if(settings.animateChildren) {
@@ -241,7 +248,7 @@ $.fn.accordion = function(parameters) {
241
248
  else {
242
249
  $activeContent
243
250
  .children()
244
- .stop(true)
251
+ .stop(true, true)
245
252
  .animate({
246
253
  opacity: 0
247
254
  }, settings.duration, module.resetOpacity)
@@ -249,7 +256,6 @@ $.fn.accordion = function(parameters) {
249
256
  }
250
257
  }
251
258
  $activeContent
252
- .stop(true)
253
259
  .slideUp(settings.duration, settings.easing, function() {
254
260
  $activeContent
255
261
  .removeClass(className.animating)
@@ -291,6 +297,10 @@ $.fn.accordion = function(parameters) {
291
297
  $openTitles
292
298
  .removeClass(className.active)
293
299
  ;
300
+ $openContents
301
+ .removeClass(className.animating)
302
+ .stop(true, true)
303
+ ;
294
304
  if(settings.animateChildren) {
295
305
  if($.fn.transition !== undefined && $module.transition('is supported')) {
296
306
  $openContents
@@ -307,7 +317,7 @@ $.fn.accordion = function(parameters) {
307
317
  else {
308
318
  $openContents
309
319
  .children()
310
- .stop()
320
+ .stop(true, true)
311
321
  .animate({
312
322
  opacity: 0
313
323
  }, settings.duration, module.resetOpacity)
@@ -315,7 +325,6 @@ $.fn.accordion = function(parameters) {
315
325
  }
316
326
  }
317
327
  $openContents
318
- .stop()
319
328
  .slideUp(settings.duration , settings.easing, function() {
320
329
  $(this).removeClass(className.active);
321
330
  module.reset.display.call(this);
@@ -422,7 +431,7 @@ $.fn.accordion = function(parameters) {
422
431
  });
423
432
  }
424
433
  clearTimeout(module.performance.timer);
425
- module.performance.timer = setTimeout(module.performance.display, 100);
434
+ module.performance.timer = setTimeout(module.performance.display, 500);
426
435
  },
427
436
  display: function() {
428
437
  var
@@ -534,9 +543,11 @@ $.fn.accordion.settings = {
534
543
  namespace : 'accordion',
535
544
 
536
545
  debug : false,
537
- verbose : true,
546
+ verbose : false,
538
547
  performance : true,
539
548
 
549
+ on : 'click',
550
+
540
551
  exclusive : true,
541
552
  collapsible : true,
542
553
  closeNested : false,
@@ -545,7 +556,10 @@ $.fn.accordion.settings = {
545
556
  duration : 350,
546
557
  easing : 'easeOutQuad',
547
558
 
559
+
560
+ onOpening : function(){},
548
561
  onOpen : function(){},
562
+ onClosing : function(){},
549
563
  onClose : function(){},
550
564
  onChange : function(){},
551
565
 
@@ -1,9 +1,9 @@
1
1
  /*!
2
- * # Semantic UI 1.12.3 - API
2
+ * # Semantic UI - API
3
3
  * http://github.com/semantic-org/semantic-ui/
4
4
  *
5
5
  *
6
- * Copyright 2014 Contributors
6
+ * Copyright 2015 Contributors
7
7
  * Released under the MIT license
8
8
  * http://opensource.org/licenses/MIT
9
9
  *
@@ -63,10 +63,11 @@ $.api = $.fn.api = function(parameters) {
63
63
  requestSettings,
64
64
  url,
65
65
  data,
66
+ requestStartTime,
66
67
 
67
68
  // standard module
68
69
  element = this,
69
- context = $context.get(),
70
+ context = $context[0],
70
71
  instance = $module.data(moduleNamespace),
71
72
  module
72
73
  ;
@@ -74,21 +75,8 @@ $.api = $.fn.api = function(parameters) {
74
75
  module = {
75
76
 
76
77
  initialize: function() {
77
- var
78
- triggerEvent = module.get.event()
79
- ;
80
- // bind events
81
78
  if(!methodInvoked) {
82
- if( triggerEvent ) {
83
- module.debug('Attaching API events to element', triggerEvent);
84
- $module
85
- .on(triggerEvent + eventNamespace, module.event.trigger)
86
- ;
87
- }
88
- else if(settings.on == 'now') {
89
- module.debug('Querying API now', triggerEvent);
90
- module.query();
91
- }
79
+ module.bind.events();
92
80
  }
93
81
  module.instantiate();
94
82
  },
@@ -109,16 +97,81 @@ $.api = $.fn.api = function(parameters) {
109
97
  ;
110
98
  },
111
99
 
100
+ bind: {
101
+ events: function() {
102
+ var
103
+ triggerEvent = module.get.event()
104
+ ;
105
+ if( triggerEvent ) {
106
+ module.verbose('Attaching API events to element', triggerEvent);
107
+ $module
108
+ .on(triggerEvent + eventNamespace, module.event.trigger)
109
+ ;
110
+ }
111
+ else if(settings.on == 'now') {
112
+ module.debug('Querying API now', triggerEvent);
113
+ module.query();
114
+ }
115
+ }
116
+ },
117
+
118
+ read: {
119
+ cachedResponse: function(url) {
120
+ var
121
+ response
122
+ ;
123
+ if(window.Storage === undefined) {
124
+ module.error(error.noStorage);
125
+ return;
126
+ }
127
+ response = sessionStorage.getItem(url);
128
+ module.debug('Using cached response', url, response);
129
+ if(response !== undefined) {
130
+ try {
131
+ response = JSON.parse(response);
132
+ }
133
+ catch(e) {
134
+ // didnt store object
135
+ }
136
+ return response;
137
+ }
138
+ return false;
139
+ }
140
+ },
141
+ write: {
142
+ cachedResponse: function(url, response) {
143
+ if(response && response === '') {
144
+ module.debug('Response empty, not caching', response);
145
+ return;
146
+ }
147
+ if(window.Storage === undefined) {
148
+ module.error(error.noStorage);
149
+ return;
150
+ }
151
+ if( $.isPlainObject(response) ) {
152
+ response = JSON.stringify(response);
153
+ }
154
+ sessionStorage.setItem(url, response);
155
+ module.verbose('Storing cached response for url', url, response);
156
+ }
157
+ },
158
+
112
159
  query: function() {
113
160
 
114
161
  if(module.is.disabled()) {
115
162
  module.debug('Element is disabled API request aborted');
116
163
  return;
117
164
  }
118
- // determine if an api event already occurred
119
- if(module.is.loading() && settings.throttle === 0 ) {
120
- module.debug('Cancelling request, previous request is still pending');
121
- return;
165
+
166
+ if(module.is.loading()) {
167
+ if(settings.interruptRequests) {
168
+ module.debug('Interrupting previous request');
169
+ module.abort();
170
+ }
171
+ else {
172
+ module.debug('Cancelling request, previous request is still pending');
173
+ return;
174
+ }
122
175
  }
123
176
 
124
177
  // pass element metadata to url (value, text)
@@ -127,17 +180,12 @@ $.api = $.fn.api = function(parameters) {
127
180
  }
128
181
 
129
182
  // Add form content
130
- if(settings.serializeForm !== false || $context.is('form')) {
131
- if(settings.serializeForm == 'json') {
132
- $.extend(true, settings.data, module.get.formData());
133
- }
134
- else {
135
- settings.data = module.get.formData();
136
- }
183
+ if(settings.serializeForm) {
184
+ settings.data = module.add.formData(settings.data);
137
185
  }
138
186
 
139
187
  // call beforesend and get any settings changes
140
- requestSettings = module.get.settings();
188
+ requestSettings = module.get.settings();
141
189
 
142
190
  // check if before send cancelled request
143
191
  if(requestSettings === false) {
@@ -149,31 +197,22 @@ $.api = $.fn.api = function(parameters) {
149
197
  module.cancelled = false;
150
198
  }
151
199
 
152
- if(settings.url) {
153
- // override with url if specified
154
- module.debug('Using specified url', url);
155
- url = module.add.urlData( settings.url );
156
- }
157
- else {
158
- // otherwise find url from api endpoints
159
- url = module.add.urlData( module.get.templateURL() );
160
- module.debug('Added URL Data to url', url);
200
+ // get url
201
+ url = module.get.templatedURL();
202
+
203
+ if(!url && !module.is.mocked()) {
204
+ module.error(error.missingURL);
205
+ return;
161
206
  }
162
207
 
163
- // exit conditions reached, missing url parameters
164
- if( !url ) {
165
- if( module.is.form() ) {
166
- url = $module.attr('action') || '';
167
- module.debug('No url or action specified, defaulting to form action', url);
168
- }
169
- else {
170
- module.error(error.missingURL, settings.action);
171
- return;
172
- }
208
+ // replace variables
209
+ url = module.add.urlData( url );
210
+
211
+ // missing url parameters
212
+ if( !url && !module.is.mocked()) {
213
+ return;
173
214
  }
174
215
 
175
- // add loading state
176
- module.set.loading();
177
216
 
178
217
  // look for jQuery ajax parameters in settings
179
218
  ajaxSettings = $.extend(true, {}, settings, {
@@ -187,39 +226,80 @@ $.api = $.fn.api = function(parameters) {
187
226
  });
188
227
 
189
228
  module.debug('Querying URL', ajaxSettings.url);
190
- module.debug('Sending data', data, ajaxSettings.method);
191
229
  module.verbose('Using AJAX settings', ajaxSettings);
192
230
 
193
- if( module.is.loading() ) {
194
- // throttle additional requests
195
- module.timer = setTimeout(function() {
196
- module.request = module.create.request();
197
- module.xhr = module.create.xhr();
198
- settings.onRequest.call(context, module.request, module.xhr);
199
- }, settings.throttle);
231
+ if(settings.cache === 'local' && module.read.cachedResponse(url)) {
232
+ module.debug('Response returned from local cache');
233
+ module.request = module.create.request();
234
+ module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
235
+ return;
236
+ }
237
+
238
+ if( !settings.throttle ) {
239
+ module.debug('Sending request', data, ajaxSettings.method);
240
+ module.send.request();
200
241
  }
201
242
  else {
202
- // immediately on first request
203
- module.request = module.create.request();
204
- module.xhr = module.create.xhr();
205
- settings.onRequest.call(context, module.request, module.xhr);
243
+ if(!settings.throttleFirstRequest && !module.timer) {
244
+ module.debug('Sending request', data, ajaxSettings.method);
245
+ module.send.request();
246
+ module.timer = setTimeout(function(){}, settings.throttle);
247
+ }
248
+ else {
249
+ module.debug('Throttling request', settings.throttle);
250
+ clearTimeout(module.timer);
251
+ module.timer = setTimeout(function() {
252
+ if(module.timer) {
253
+ delete module.timer;
254
+ }
255
+ module.debug('Sending throttled request', data, ajaxSettings.method);
256
+ module.send.request();
257
+ }, settings.throttle);
258
+ }
206
259
  }
207
260
 
208
261
  },
209
262
 
210
-
211
263
  is: {
212
264
  disabled: function() {
213
- return ($module.filter(settings.filter).length > 0);
265
+ return ($module.filter(selector.disabled).length > 0);
214
266
  },
215
267
  form: function() {
216
268
  return $module.is('form');
217
269
  },
270
+ mocked: function() {
271
+ return (settings.mockResponse || settings.mockResponseAsync);
272
+ },
218
273
  input: function() {
219
274
  return $module.is('input');
220
275
  },
221
276
  loading: function() {
222
277
  return (module.request && module.request.state() == 'pending');
278
+ },
279
+ abortedRequest: function(xhr) {
280
+ if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
281
+ module.verbose('XHR request determined to be aborted');
282
+ return true;
283
+ }
284
+ else {
285
+ module.verbose('XHR request was not aborted');
286
+ return false;
287
+ }
288
+ },
289
+ validResponse: function(response) {
290
+ if( settings.dataType !== 'json' || !$.isFunction(settings.successTest) ) {
291
+ module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
292
+ return true;
293
+ }
294
+ module.debug('Checking JSON returned success', settings.successTest, response);
295
+ if( settings.successTest(response) ) {
296
+ module.debug('Response passed success test', response);
297
+ return true;
298
+ }
299
+ else {
300
+ module.debug('Response failed success test', response);
301
+ return false;
302
+ }
223
303
  }
224
304
  },
225
305
 
@@ -311,6 +391,48 @@ $.api = $.fn.api = function(parameters) {
311
391
  }
312
392
  }
313
393
  return url;
394
+ },
395
+ formData: function(data) {
396
+ var
397
+ canSerialize = ($.fn.serializeObject !== undefined),
398
+ formData = (canSerialize)
399
+ ? $form.serializeObject()
400
+ : $form.serialize(),
401
+ hasOtherData
402
+ ;
403
+ data = data || settings.data;
404
+ hasOtherData = $.isPlainObject(data);
405
+
406
+ if(hasOtherData) {
407
+ if(canSerialize) {
408
+ module.debug('Extending existing data with form data', data, formData);
409
+ data = $.extend(true, {}, data, formData);
410
+ }
411
+ else {
412
+ module.error(error.missingSerialize);
413
+ module.debug('Cant extend data. Replacing data with form data', data, formData);
414
+ data = formData;
415
+ }
416
+ }
417
+ else {
418
+ module.debug('Adding form data', formData);
419
+ data = formData;
420
+ }
421
+ return data;
422
+ }
423
+ },
424
+
425
+ send: {
426
+ request: function() {
427
+ module.set.loading();
428
+ module.request = module.create.request();
429
+ if( module.is.mocked() ) {
430
+ module.mockedXHR = module.create.mockedXHR();
431
+ }
432
+ else {
433
+ module.xhr = module.create.xhr();
434
+ }
435
+ settings.onRequest.call(context, module.request, module.xhr);
314
436
  }
315
437
  },
316
438
 
@@ -325,37 +447,54 @@ $.api = $.fn.api = function(parameters) {
325
447
  always: function() {
326
448
  // calculate if loading time was below minimum threshold
327
449
  },
328
- done: function(response) {
450
+ done: function(response, textStatus, xhr) {
329
451
  var
330
452
  context = this,
331
- elapsedTime = (new Date().getTime() - time),
332
- timeLeft = (settings.loadingDuration - elapsedTime)
453
+ elapsedTime = (new Date().getTime() - requestStartTime),
454
+ timeLeft = (settings.loadingDuration - elapsedTime),
455
+ translatedResponse = ( $.isFunction(settings.onResponse) )
456
+ ? settings.onResponse.call(context, $.extend(true, {}, response))
457
+ : false
333
458
  ;
334
459
  timeLeft = (timeLeft > 0)
335
460
  ? timeLeft
336
461
  : 0
337
462
  ;
463
+ if(translatedResponse) {
464
+ module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
465
+ response = translatedResponse;
466
+ }
467
+ if(timeLeft > 0) {
468
+ module.debug('Response completed early delaying state change by', timeLeft);
469
+ }
338
470
  setTimeout(function() {
339
- module.request.resolveWith(context, [response]);
471
+ if( module.is.validResponse(response) ) {
472
+ module.request.resolveWith(context, [response]);
473
+ }
474
+ else {
475
+ module.request.rejectWith(context, [xhr, 'invalid']);
476
+ }
340
477
  }, timeLeft);
341
478
  },
342
479
  fail: function(xhr, status, httpMessage) {
343
480
  var
344
481
  context = this,
345
- elapsedTime = (new Date().getTime() - time),
482
+ elapsedTime = (new Date().getTime() - requestStartTime),
346
483
  timeLeft = (settings.loadingDuration - elapsedTime)
347
484
  ;
348
485
  timeLeft = (timeLeft > 0)
349
486
  ? timeLeft
350
487
  : 0
351
488
  ;
352
- // page triggers abort on navigation, dont show error
489
+ if(timeLeft > 0) {
490
+ module.debug('Response completed early delaying state change by', timeLeft);
491
+ }
353
492
  setTimeout(function() {
354
- if(status !== 'abort') {
355
- module.request.rejectWith(context, [xhr, status, httpMessage]);
493
+ if( module.is.abortedRequest(xhr) ) {
494
+ module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
356
495
  }
357
496
  else {
358
- module.reset();
497
+ module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
359
498
  }
360
499
  }, timeLeft);
361
500
  }
@@ -366,87 +505,124 @@ $.api = $.fn.api = function(parameters) {
366
505
  settings.onComplete.call(context, response, $module);
367
506
  },
368
507
  done: function(response) {
369
- module.debug('API Response Received', response);
370
- if(settings.dataType == 'json') {
371
- if( $.isFunction(settings.successTest) ) {
372
- module.debug('Checking JSON returned success', settings.successTest, response);
373
- if( settings.successTest(response) ) {
374
- settings.onSuccess.call(context, response, $module);
375
- }
376
- else {
377
- module.debug('JSON test specified by user and response failed', response);
378
- settings.onFailure.call(context, response, $module);
379
- }
380
- }
381
- else {
382
- settings.onSuccess.call(context, response, $module);
383
- }
384
- }
385
- else {
386
- settings.onSuccess.call(context, response, $module);
508
+ module.debug('Successful API Response', response);
509
+ if(settings.cache === 'local' && url) {
510
+ module.write.cachedResponse(url, response);
511
+ module.debug('Saving server response locally', module.cache);
387
512
  }
513
+ settings.onSuccess.call(context, response, $module);
388
514
  },
389
- error: function(xhr, status, httpMessage) {
515
+ fail: function(xhr, status, httpMessage) {
390
516
  var
391
- errorMessage = (settings.error[status] !== undefined)
392
- ? settings.error[status]
393
- : httpMessage,
394
- response
517
+ // pull response from xhr if available
518
+ response = $.isPlainObject(xhr)
519
+ ? (xhr.responseText)
520
+ : false,
521
+ errorMessage = ($.isPlainObject(response) && response.error !== undefined)
522
+ ? response.error // use json error message
523
+ : (settings.error[status] !== undefined) // use server error message
524
+ ? settings.error[status]
525
+ : httpMessage
395
526
  ;
396
- // let em know unless request aborted
397
- if(xhr !== undefined) {
398
- // readyState 4 = done, anything less is not really sent
399
- if(xhr.readyState !== undefined && xhr.readyState == 4) {
527
+ if(status == 'aborted') {
528
+ module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
529
+ settings.onAbort.call(context, status, $module);
530
+ }
531
+ else if(status == 'invalid') {
532
+ module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
533
+ }
534
+ else if(status == 'error') {
400
535
 
401
- // if http status code returned and json returned error, look for it
536
+ if(xhr !== undefined) {
537
+ module.debug('XHR produced a server error', status, httpMessage);
538
+ // make sure we have an error to display to console
402
539
  if( xhr.status != 200 && httpMessage !== undefined && httpMessage !== '') {
403
540
  module.error(error.statusMessage + httpMessage, ajaxSettings.url);
404
541
  }
405
- else {
406
- if(status == 'error' && settings.dataType == 'json') {
407
- try {
408
- response = $.parseJSON(xhr.responseText);
409
- if(response && response.error !== undefined) {
410
- errorMessage = response.error;
411
- }
412
- }
413
- catch(e) {
414
- module.error(error.JSONParse);
415
- }
416
- }
417
- }
418
- module.remove.loading();
419
- module.set.error();
420
- // show error state only for duration specified in settings
421
- if(settings.errorDuration) {
422
- setTimeout(module.remove.error, settings.errorDuration);
423
- }
424
- module.debug('API Request error:', errorMessage);
425
542
  settings.onError.call(context, errorMessage, $module);
426
543
  }
427
- else {
428
- settings.onAbort.call(context, errorMessage, $module);
429
- module.debug('Request Aborted (Most likely caused by page change or CORS Policy)', status, httpMessage);
430
- }
431
544
  }
545
+
546
+ if(settings.errorDuration && status !== 'aborted') {
547
+ module.debug('Adding error state');
548
+ module.set.error();
549
+ setTimeout(module.remove.error, settings.errorDuration);
550
+ }
551
+ module.debug('API Request failed', errorMessage, xhr);
552
+ settings.onFailure.call(context, response, $module);
432
553
  }
433
554
  }
434
555
  },
435
556
 
436
557
  create: {
558
+
437
559
  request: function() {
560
+ // api request promise
438
561
  return $.Deferred()
439
562
  .always(module.event.request.complete)
440
563
  .done(module.event.request.done)
441
- .fail(module.event.request.error)
564
+ .fail(module.event.request.fail)
442
565
  ;
443
566
  },
567
+
568
+ mockedXHR: function () {
569
+ var
570
+ // xhr does not simulate these properties of xhr but must return them
571
+ textStatus = false,
572
+ status = false,
573
+ httpMessage = false,
574
+ asyncCallback,
575
+ response,
576
+ mockedXHR
577
+ ;
578
+
579
+ mockedXHR = $.Deferred()
580
+ .always(module.event.xhr.complete)
581
+ .done(module.event.xhr.done)
582
+ .fail(module.event.xhr.fail)
583
+ ;
584
+
585
+ if(settings.mockResponse) {
586
+ if( $.isFunction(settings.mockResponse) ) {
587
+ module.debug('Using mocked callback returning response', settings.mockResponse);
588
+ response = settings.mockResponse.call(context, settings);
589
+ }
590
+ else {
591
+ module.debug('Using specified response', settings.mockResponse);
592
+ response = settings.mockResponse;
593
+ }
594
+ // simulating response
595
+ mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
596
+ }
597
+ else if( $.isFunction(settings.mockResponseAsync) ) {
598
+ asyncCallback = function(response) {
599
+ module.debug('Async callback returned response', response);
600
+
601
+ if(response) {
602
+ mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
603
+ }
604
+ else {
605
+ mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
606
+ }
607
+ };
608
+ module.debug('Using async mocked response', settings.mockResponseAsync);
609
+ settings.mockResponseAsync.call(context, settings, asyncCallback);
610
+ }
611
+ return mockedXHR;
612
+ },
613
+
444
614
  xhr: function() {
445
- return $.ajax(ajaxSettings)
615
+ var
616
+ xhr
617
+ ;
618
+ // ajax request promise
619
+ xhr = $.ajax(ajaxSettings)
446
620
  .always(module.event.xhr.always)
447
621
  .done(module.event.xhr.done)
448
622
  .fail(module.event.xhr.fail)
449
623
  ;
624
+ module.verbose('Created server request', xhr);
625
+ return xhr;
450
626
  }
451
627
  },
452
628
 
@@ -458,6 +634,7 @@ $.api = $.fn.api = function(parameters) {
458
634
  loading: function() {
459
635
  module.verbose('Adding loading state to element', $context);
460
636
  $context.addClass(className.loading);
637
+ requestStartTime = new Date().getTime();
461
638
  }
462
639
  },
463
640
 
@@ -483,7 +660,7 @@ $.api = $.fn.api = function(parameters) {
483
660
  var
484
661
  runSettings
485
662
  ;
486
- runSettings = settings.beforeSend.call($module, settings);
663
+ runSettings = settings.beforeSend.call(context, settings);
487
664
  if(runSettings) {
488
665
  if(runSettings.success !== undefined) {
489
666
  module.debug('Legacy success callback detected', runSettings);
@@ -551,34 +728,24 @@ $.api = $.fn.api = function(parameters) {
551
728
  return settings.on;
552
729
  }
553
730
  },
554
- formData: function() {
555
- var
556
- formData
557
- ;
558
- if($module.serializeObject !== undefined) {
559
- formData = $form.serializeObject();
560
- }
561
- else {
562
- module.error(error.missingSerialize);
563
- formData = $form.serialize();
564
- }
565
- module.debug('Retrieved form data', formData);
566
- return formData;
567
- },
568
- templateURL: function(action) {
569
- var
570
- url
571
- ;
731
+ templatedURL: function(action) {
572
732
  action = action || $module.data(metadata.action) || settings.action || false;
733
+ url = $module.data(metadata.url) || settings.url || false;
734
+ if(url) {
735
+ module.debug('Using specified url', url);
736
+ return url;
737
+ }
573
738
  if(action) {
574
739
  module.debug('Looking up url for action', action, settings.api);
575
- if(settings.api[action] !== undefined) {
576
- url = settings.api[action];
577
- module.debug('Found template url', url);
578
- }
579
- else if( !module.is.form() ) {
740
+ if(settings.api[action] === undefined && !module.is.mocked()) {
580
741
  module.error(error.missingAction, settings.action, settings.api);
742
+ return;
581
743
  }
744
+ url = settings.api[action];
745
+ }
746
+ else if( module.is.form() ) {
747
+ url = $module.attr('action') || false;
748
+ module.debug('No url or action specified, defaulting to form action', url);
582
749
  }
583
750
  return url;
584
751
  }
@@ -591,7 +758,6 @@ $.api = $.fn.api = function(parameters) {
591
758
  if( xhr && xhr.state() !== 'resolved') {
592
759
  module.debug('Cancelling API request');
593
760
  xhr.abort();
594
- module.request.rejectWith(settings.apiSettings);
595
761
  }
596
762
  },
597
763
 
@@ -670,7 +836,7 @@ $.api = $.fn.api = function(parameters) {
670
836
  });
671
837
  }
672
838
  clearTimeout(module.performance.timer);
673
- module.performance.timer = setTimeout(module.performance.display, 100);
839
+ module.performance.timer = setTimeout(module.performance.display, 500);
674
840
  },
675
841
  display: function() {
676
842
  var
@@ -780,49 +946,88 @@ $.api = $.fn.api = function(parameters) {
780
946
 
781
947
  $.api.settings = {
782
948
 
783
- name : 'API',
784
- namespace : 'api',
949
+ name : 'API',
950
+ namespace : 'api',
951
+
952
+ debug : true,
953
+ verbose : false,
954
+ performance : true,
955
+
956
+ // object containing all templates endpoints
957
+ api : {},
785
958
 
786
- debug : true,
787
- verbose : false,
788
- performance : true,
959
+ // whether to cache responses
960
+ cache : true,
961
+
962
+ // whether new requests should abort previous requests
963
+ interruptRequests : true,
789
964
 
790
965
  // event binding
791
- on : 'auto',
792
- filter : '.disabled',
793
- stateContext : false,
966
+ on : 'auto',
967
+
968
+ // context for applying state classes
969
+ stateContext : false,
970
+
971
+ // duration for loading state
972
+ loadingDuration : 0,
973
+
974
+ // duration for error state
975
+ errorDuration : 2000,
976
+
977
+ // API action to use
978
+ action : false,
979
+
980
+ // templated URL to use
981
+ url : false,
794
982
 
795
- // state
796
- loadingDuration : 0,
797
- errorDuration : 2000,
983
+ // base URL to apply to all endpoints
984
+ base : '',
798
985
 
799
- // templating
800
- action : false,
801
- url : false,
802
- base : '',
986
+ // data that will
987
+ urlData : {},
803
988
 
804
- // data
805
- urlData : {},
989
+ // whether to add default data to url data
990
+ defaultData : true,
806
991
 
807
- // ui
808
- defaultData : true,
809
- serializeForm : false,
810
- throttle : 0,
992
+ // whether to serialize closest form
993
+ serializeForm : false,
811
994
 
812
- // jQ ajax
813
- method : 'get',
814
- data : {},
815
- dataType : 'json',
995
+ // how long to wait before request should occur
996
+ throttle : 0,
816
997
 
817
- // callbacks
998
+ // whether to throttle first request or only repeated
999
+ throttleFirstRequest : true,
1000
+
1001
+ // standard ajax settings
1002
+ method : 'get',
1003
+ data : {},
1004
+ dataType : 'json',
1005
+
1006
+ // mock response
1007
+ mockResponse : false,
1008
+ mockResponseAsync : false,
1009
+
1010
+ // callbacks before request
818
1011
  beforeSend : function(settings) { return settings; },
819
1012
  beforeXHR : function(xhr) {},
820
-
821
1013
  onRequest : function(promise, xhr) {},
1014
+
1015
+ // after request
1016
+ onResponse : false, // function(response) { },
1017
+
1018
+ // response was successful, if JSON passed validation
822
1019
  onSuccess : function(response, $module) {},
1020
+
1021
+ // request finished without aborting
823
1022
  onComplete : function(response, $module) {},
824
- onFailure : function(errorMessage, $module) {},
1023
+
1024
+ // failed JSON success test
1025
+ onFailure : function(response, $module) {},
1026
+
1027
+ // server error
825
1028
  onError : function(errorMessage, $module) {},
1029
+
1030
+ // request aborted
826
1031
  onAbort : function(errorMessage, $module) {},
827
1032
 
828
1033
  successTest : false,
@@ -836,9 +1041,10 @@ $.api.settings = {
836
1041
  legacyParameters : 'You are using legacy API success callback names',
837
1042
  method : 'The method you called is not defined',
838
1043
  missingAction : 'API action used but no url was defined',
839
- missingSerialize : 'Required dependency jquery-serialize-object missing, using basic serialize',
1044
+ missingSerialize : 'jquery-serialize-object is required to add form data to an existing data object',
840
1045
  missingURL : 'No URL specified for api event',
841
1046
  noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.',
1047
+ noStorage : 'Caching respopnses locally requires session storage',
842
1048
  parseError : 'There was an error parsing your request',
843
1049
  requiredParameter : 'Missing a required URL parameter: ',
844
1050
  statusMessage : 'Server gave an error: ',
@@ -846,8 +1052,8 @@ $.api.settings = {
846
1052
  },
847
1053
 
848
1054
  regExp : {
849
- required: /\{\$*[A-z0-9]+\}/g,
850
- optional: /\{\/\$*[A-z0-9]+\}/g,
1055
+ required : /\{\$*[A-z0-9]+\}/g,
1056
+ optional : /\{\/\$*[A-z0-9]+\}/g,
851
1057
  },
852
1058
 
853
1059
  className: {
@@ -856,16 +1062,16 @@ $.api.settings = {
856
1062
  },
857
1063
 
858
1064
  selector: {
859
- form: 'form'
1065
+ disabled : '.disabled',
1066
+ form : 'form'
860
1067
  },
861
1068
 
862
1069
  metadata: {
863
- action : 'action'
1070
+ action : 'action',
1071
+ url : 'url'
864
1072
  }
865
1073
  };
866
1074
 
867
1075
 
868
- $.api.settings.api = {};
869
-
870
1076
 
871
- })( jQuery, window , document );
1077
+ })( jQuery, window , document );