rasputin 0.5.1
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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +22 -0
- data/Rakefile +1 -0
- data/lib/rasputin.rb +3 -0
- data/lib/rasputin/engine.rb +4 -0
- data/lib/rasputin/handlebars_template.rb +32 -0
- data/lib/rasputin/version.rb +3 -0
- data/rasputin.gemspec +23 -0
- data/vendor/assets/javascripts/jquery-ui.js +13804 -0
- data/vendor/assets/javascripts/sproutcore-ajax.js +1071 -0
- data/vendor/assets/javascripts/sproutcore-datastore.js +11758 -0
- data/vendor/assets/javascripts/sproutcore-i18n.js +40 -0
- data/vendor/assets/javascripts/sproutcore-jui.js +732 -0
- data/vendor/assets/javascripts/sproutcore-statechart.js +3264 -0
- data/vendor/assets/javascripts/sproutcore-throbber.js +258 -0
- data/vendor/assets/javascripts/sproutcore.js +13225 -0
- data/vendor/assets/stylesheets/aristo.css +480 -0
- data/vendor/assets/stylesheets/jquery-ui.css +1444 -0
- metadata +105 -0
@@ -0,0 +1,1071 @@
|
|
1
|
+
|
2
|
+
(function(exports) {
|
3
|
+
// ==========================================================================
|
4
|
+
// Project: SproutCore - JavaScript Application Framework
|
5
|
+
// Copyright: ©2006-2011 Strobe Inc. and contributors.
|
6
|
+
// Portions ©2008-2011 Apple Inc. All rights reserved.
|
7
|
+
// License: Licensed under MIT license (see license.js)
|
8
|
+
// ==========================================================================
|
9
|
+
|
10
|
+
var get = SC.get, set = SC.set, getPath = SC.getPath;
|
11
|
+
|
12
|
+
/**
|
13
|
+
@class
|
14
|
+
|
15
|
+
A response represents a single response from a server request. An instance
|
16
|
+
of this class is returned whenever you call SC.Request.send().
|
17
|
+
|
18
|
+
@extend SC.Object
|
19
|
+
@since SproutCore 1.0
|
20
|
+
*/
|
21
|
+
SC.Response = SC.Object.extend(
|
22
|
+
/** @scope SC.Response.prototype */ {
|
23
|
+
|
24
|
+
/**
|
25
|
+
Walk like a duck
|
26
|
+
|
27
|
+
@type Boolean
|
28
|
+
*/
|
29
|
+
isResponse: true,
|
30
|
+
|
31
|
+
/**
|
32
|
+
Becomes true if there was a failure. Makes this into an error object.
|
33
|
+
|
34
|
+
@type Boolean
|
35
|
+
@default false
|
36
|
+
*/
|
37
|
+
isError: false,
|
38
|
+
|
39
|
+
/**
|
40
|
+
Always the current response
|
41
|
+
|
42
|
+
@field
|
43
|
+
@type SC.Response
|
44
|
+
@default `this`
|
45
|
+
*/
|
46
|
+
errorValue: function() {
|
47
|
+
return this;
|
48
|
+
}.property().cacheable(),
|
49
|
+
|
50
|
+
/**
|
51
|
+
The error object generated when this becomes an error
|
52
|
+
|
53
|
+
@type SC.Error
|
54
|
+
@default null
|
55
|
+
*/
|
56
|
+
errorObject: null,
|
57
|
+
|
58
|
+
/**
|
59
|
+
Request used to generate this response. This is a copy of the original
|
60
|
+
request object as you may have modified the original request object since
|
61
|
+
then.
|
62
|
+
|
63
|
+
To retrieve the original request object use originalRequest.
|
64
|
+
|
65
|
+
@type SC.Request
|
66
|
+
@default null
|
67
|
+
*/
|
68
|
+
request: null,
|
69
|
+
|
70
|
+
/**
|
71
|
+
The request object that originated this request series. Mostly this is
|
72
|
+
useful if you are looking for a reference to the original request. To
|
73
|
+
inspect actual properties you should use request instead.
|
74
|
+
|
75
|
+
@field
|
76
|
+
@type SC.Request
|
77
|
+
@observes request
|
78
|
+
*/
|
79
|
+
originalRequest: function() {
|
80
|
+
var ret = get(this, 'request');
|
81
|
+
while (get(ret, 'source')) { ret = get(ret, 'source'); }
|
82
|
+
return ret ;
|
83
|
+
}.property('request').cacheable(),
|
84
|
+
|
85
|
+
/**
|
86
|
+
Type of request. Must be an HTTP method. Based on the request.
|
87
|
+
|
88
|
+
@field
|
89
|
+
@type String
|
90
|
+
@observes request
|
91
|
+
*/
|
92
|
+
type: function() {
|
93
|
+
return getPath(this, 'request.type');
|
94
|
+
}.property('request').cacheable(),
|
95
|
+
|
96
|
+
/**
|
97
|
+
URL of request.
|
98
|
+
|
99
|
+
@field
|
100
|
+
@type String
|
101
|
+
@observes request
|
102
|
+
*/
|
103
|
+
url: function() {
|
104
|
+
return getPath(this, 'request.url');
|
105
|
+
}.property('request').cacheable(),
|
106
|
+
|
107
|
+
/**
|
108
|
+
Returns the hash of listeners set on the request.
|
109
|
+
|
110
|
+
@field
|
111
|
+
@type Hash
|
112
|
+
@observes request
|
113
|
+
*/
|
114
|
+
listeners: function() {
|
115
|
+
return getPath(this, 'request.listeners');
|
116
|
+
}.property('request').cacheable(),
|
117
|
+
|
118
|
+
/**
|
119
|
+
The response status code.
|
120
|
+
|
121
|
+
@type Number
|
122
|
+
@default -100
|
123
|
+
*/
|
124
|
+
status: -100, // READY
|
125
|
+
|
126
|
+
/**
|
127
|
+
Headers from the response. Computed on-demand
|
128
|
+
|
129
|
+
@type Hash
|
130
|
+
@default null
|
131
|
+
*/
|
132
|
+
headers: null,
|
133
|
+
|
134
|
+
/**
|
135
|
+
The response body or the parsed JSON.
|
136
|
+
*/
|
137
|
+
body: null,
|
138
|
+
|
139
|
+
/**
|
140
|
+
Set to true if response is cancelled
|
141
|
+
|
142
|
+
@type Boolean
|
143
|
+
@default false
|
144
|
+
*/
|
145
|
+
isCancelled: false,
|
146
|
+
|
147
|
+
/**
|
148
|
+
Set to true if the request timed out. Set to false if the request has
|
149
|
+
completed before the timeout value. Set to null if the timeout timer is
|
150
|
+
still ticking.
|
151
|
+
|
152
|
+
@type Boolean
|
153
|
+
@default null
|
154
|
+
*/
|
155
|
+
timedOut: null,
|
156
|
+
|
157
|
+
// ..........................................................
|
158
|
+
// METHODS
|
159
|
+
//
|
160
|
+
|
161
|
+
/**
|
162
|
+
Called by the request manager when its time to actually run. This will
|
163
|
+
invoke any callbacks on the source request then invoke transport() to
|
164
|
+
begin the actual request.
|
165
|
+
*/
|
166
|
+
fire: function() {
|
167
|
+
var req = get(this, 'request'),
|
168
|
+
source = req ? get(req, 'source') : null;
|
169
|
+
|
170
|
+
// first give the source a chance to fixup the request and response
|
171
|
+
// then freeze req so no more changes can happen.
|
172
|
+
if (source && source.willSend) { source.willSend(req, this); }
|
173
|
+
req.freeze();
|
174
|
+
|
175
|
+
// if the source did not cancel the request, then invoke the transport
|
176
|
+
// to actually trigger the request. This might receive a response
|
177
|
+
// immediately if it is synchronous.
|
178
|
+
if (!get(this, 'isCancelled')) { this.invokeTransport(); }
|
179
|
+
|
180
|
+
// if the transport did not cancel the request for some reason, let the
|
181
|
+
// source know that the request was sent
|
182
|
+
if (!this.get('isCancelled') && source && source.didSend) {
|
183
|
+
source.didSend(req, this);
|
184
|
+
}
|
185
|
+
},
|
186
|
+
|
187
|
+
/**
|
188
|
+
Called by `SC.Response#fire()`. Starts the transport by invoking the
|
189
|
+
`SC.Response#receive()` function.
|
190
|
+
*/
|
191
|
+
invokeTransport: function() {
|
192
|
+
this.receive(function(proceed) { set(this, 'status', 200); }, this);
|
193
|
+
},
|
194
|
+
|
195
|
+
/**
|
196
|
+
Invoked by the transport when it receives a response. The passed-in
|
197
|
+
callback will be invoked to actually process the response. If cancelled
|
198
|
+
we will pass false. You should clean up instead.
|
199
|
+
|
200
|
+
Invokes callbacks on the source request also.
|
201
|
+
|
202
|
+
@param {Function} callback the function to receive
|
203
|
+
@param {Object} context context to execute the callback in
|
204
|
+
@returns {SC.Response} receiver
|
205
|
+
*/
|
206
|
+
receive: function(callback, context) {
|
207
|
+
var req = get(this, 'request');
|
208
|
+
var source = req ? get(req, 'source') : null;
|
209
|
+
|
210
|
+
SC.run(this, function() {
|
211
|
+
// invoke the source, giving a chance to fixup the response or (more
|
212
|
+
// likely) cancel the request.
|
213
|
+
if (source && source.willReceive) { source.willReceive(req, this); }
|
214
|
+
|
215
|
+
// invoke the callback. note if the response was cancelled or not
|
216
|
+
callback.call(context, !get(this, 'isCancelled'));
|
217
|
+
|
218
|
+
// if we weren't cancelled, then give the source first crack at handling
|
219
|
+
// the response. if the source doesn't want listeners to be notified,
|
220
|
+
// it will cancel the response.
|
221
|
+
if (!get(this, 'isCancelled') && source && source.didReceive) {
|
222
|
+
source.didReceive(req, this);
|
223
|
+
}
|
224
|
+
|
225
|
+
// notify listeners if we weren't cancelled.
|
226
|
+
if (!get(this, 'isCancelled')) { this.notify(); }
|
227
|
+
});
|
228
|
+
|
229
|
+
// no matter what, remove from inflight queue
|
230
|
+
SC.Request.manager.transportDidClose(this);
|
231
|
+
return this;
|
232
|
+
},
|
233
|
+
|
234
|
+
/**
|
235
|
+
Default method just closes the connection. It will also mark the request
|
236
|
+
as cancelled, which will not call any listeners.
|
237
|
+
*/
|
238
|
+
cancel: function() {
|
239
|
+
if (!get(this, 'isCancelled')) {
|
240
|
+
set(this, 'isCancelled', true) ;
|
241
|
+
this.cancelTransport() ;
|
242
|
+
SC.Request.manager.transportDidClose(this) ;
|
243
|
+
}
|
244
|
+
},
|
245
|
+
|
246
|
+
/**
|
247
|
+
Override with concrete implementation to actually cancel the transport.
|
248
|
+
*/
|
249
|
+
cancelTransport: function() {},
|
250
|
+
|
251
|
+
/**
|
252
|
+
Notifies any saved target/action. Call whenever you cancel, or end.
|
253
|
+
|
254
|
+
@returns {SC.Response} receiver
|
255
|
+
*/
|
256
|
+
notify: function() {
|
257
|
+
var listeners = this.get('listeners'),
|
258
|
+
status = this.get('status'),
|
259
|
+
baseStat = Math.floor(status / 100) * 100,
|
260
|
+
handled = false;
|
261
|
+
|
262
|
+
if (!listeners) { return this; }
|
263
|
+
|
264
|
+
handled = this._notifyListeners(listeners, status);
|
265
|
+
if (!handled && baseStat !== status) { handled = this._notifyListeners(listeners, baseStat); }
|
266
|
+
if (!handled && status !== 0) { handled = this._notifyListeners(listeners, 0); }
|
267
|
+
|
268
|
+
return this ;
|
269
|
+
},
|
270
|
+
|
271
|
+
/**
|
272
|
+
String representation of the response object
|
273
|
+
|
274
|
+
@returns {String}
|
275
|
+
*/
|
276
|
+
toString: function() {
|
277
|
+
var ret = this._super();
|
278
|
+
return "%@<%@ %@, status=%@".fmt(ret, this.get('type'), this.get('address'), this.get('status'));
|
279
|
+
},
|
280
|
+
|
281
|
+
/**
|
282
|
+
@private
|
283
|
+
|
284
|
+
Will notify each listener. Returns true if any of the listeners handle.
|
285
|
+
*/
|
286
|
+
_notifyListeners: function(listeners, status) {
|
287
|
+
var notifiers = listeners[status], params, target, action;
|
288
|
+
if (!notifiers) { return false; }
|
289
|
+
|
290
|
+
var handled = false;
|
291
|
+
var len = notifiers.length;
|
292
|
+
|
293
|
+
for (var i = 0; i < len; i++) {
|
294
|
+
var notifier = notifiers[i];
|
295
|
+
params = (notifier.params || []).copy();
|
296
|
+
params.unshift(this);
|
297
|
+
|
298
|
+
target = notifier.target;
|
299
|
+
action = notifier.action;
|
300
|
+
if (SC.typeOf(action) === 'string') { action = target[action]; }
|
301
|
+
|
302
|
+
handled = action.apply(target, params);
|
303
|
+
}
|
304
|
+
|
305
|
+
return handled;
|
306
|
+
}
|
307
|
+
});
|
308
|
+
|
309
|
+
/**
|
310
|
+
Concrete implementation of SC.Response that implements support for using
|
311
|
+
jqXHR requests.
|
312
|
+
|
313
|
+
@extends SC.Response
|
314
|
+
*/
|
315
|
+
SC.XHRResponse = SC.Response.extend({
|
316
|
+
|
317
|
+
/**
|
318
|
+
Implement transport-specific support for fetching all headers
|
319
|
+
*/
|
320
|
+
headers: function() {
|
321
|
+
var rawRequest = get(this, 'rawRequest'),
|
322
|
+
str = rawRequest ? rawRequest.getAllResponseHeaders() : null,
|
323
|
+
ret = {};
|
324
|
+
|
325
|
+
if (!str) return ret;
|
326
|
+
|
327
|
+
str.split("\n").forEach(function(header) {
|
328
|
+
var idx = header.indexOf(':'),
|
329
|
+
key, value;
|
330
|
+
if (idx>=0) {
|
331
|
+
key = header.slice(0,idx);
|
332
|
+
value = SC.String.trim(header.slice(idx+1));
|
333
|
+
ret[key] = value ;
|
334
|
+
}
|
335
|
+
}, this);
|
336
|
+
|
337
|
+
return ret ;
|
338
|
+
}.property('status').cacheable(),
|
339
|
+
|
340
|
+
/**
|
341
|
+
Implement transport-specific support for fetching named header
|
342
|
+
*/
|
343
|
+
header: function(key) {
|
344
|
+
var rawRequest = get(this, 'rawRequest');
|
345
|
+
return rawRequest ? rawRequest.getResponseHeader(key) : null;
|
346
|
+
},
|
347
|
+
|
348
|
+
/**
|
349
|
+
*/
|
350
|
+
cancelTransport: function() {
|
351
|
+
var rawRequest = get(this, 'rawRequest');
|
352
|
+
if (rawRequest) rawRequest.abort();
|
353
|
+
set(this, 'rawRequest', null);
|
354
|
+
},
|
355
|
+
|
356
|
+
/**
|
357
|
+
*/
|
358
|
+
invokeTransport: function() {
|
359
|
+
var async = !!getPath(this, 'request.isAsynchronous');
|
360
|
+
var rawRequest = this.createRequest();
|
361
|
+
|
362
|
+
// save it
|
363
|
+
set(this, 'rawRequest', rawRequest);
|
364
|
+
|
365
|
+
// not async
|
366
|
+
if (!async) this.finishRequest();
|
367
|
+
|
368
|
+
return rawRequest;
|
369
|
+
},
|
370
|
+
|
371
|
+
/**
|
372
|
+
Creates the jqXHR object.
|
373
|
+
|
374
|
+
@returns {jqXHR}
|
375
|
+
*/
|
376
|
+
createRequest: function() {
|
377
|
+
var request = get(this, 'request');
|
378
|
+
return SC.$.ajax({
|
379
|
+
url: get(this, 'url'),
|
380
|
+
type: get(this, 'type'),
|
381
|
+
dataType: get(request, 'dataType'),
|
382
|
+
async: get(request, 'isAsynchronous'),
|
383
|
+
headers: get(request, 'headers'),
|
384
|
+
data: get(request, 'body'),
|
385
|
+
timeout: get(request, 'timeout'),
|
386
|
+
ifModified: get(request, 'ifModified'),
|
387
|
+
complete: this.finishRequest,
|
388
|
+
success: this._didLoadContent,
|
389
|
+
context: this
|
390
|
+
});
|
391
|
+
},
|
392
|
+
|
393
|
+
/**
|
394
|
+
@private
|
395
|
+
|
396
|
+
Called by the jqXHR when it responds with some final results.
|
397
|
+
|
398
|
+
@param {jqXHR} rawRequest the actual request
|
399
|
+
@returns {Boolean} request success
|
400
|
+
*/
|
401
|
+
finishRequest: function(rawRequest) {
|
402
|
+
if (rawRequest.readyState === 4 && !get(this, 'timedOut')) {
|
403
|
+
this.receive(function(proceed) {
|
404
|
+
if (!proceed) return; // skip receiving...
|
405
|
+
var statusText = rawRequest.statusText,
|
406
|
+
status = rawRequest.status;
|
407
|
+
set(this, 'status', status);
|
408
|
+
if (statusText === 'success' || statusText === 'notmodified') {
|
409
|
+
set(this, 'isError', false);
|
410
|
+
set(this, 'errorObject', null);
|
411
|
+
} else {
|
412
|
+
if (statusText === 'timeout') {
|
413
|
+
set(this, 'timedOut', true);
|
414
|
+
this.cancelTransport();
|
415
|
+
}
|
416
|
+
var error = new SC.Error('%@ Request %@'.fmt(statusText, status));
|
417
|
+
set(error, 'errorValue', this);
|
418
|
+
set(this, 'isError', true);
|
419
|
+
set(this, 'errorObject', error);
|
420
|
+
}
|
421
|
+
}, this);
|
422
|
+
|
423
|
+
return true;
|
424
|
+
}
|
425
|
+
return false;
|
426
|
+
},
|
427
|
+
|
428
|
+
/**
|
429
|
+
@private
|
430
|
+
*/
|
431
|
+
_didLoadContent: function(data) {
|
432
|
+
set(this, 'body', data);
|
433
|
+
}
|
434
|
+
|
435
|
+
});
|
436
|
+
|
437
|
+
SC.HTTPError = SC.Object.extend({
|
438
|
+
|
439
|
+
});
|
440
|
+
|
441
|
+
SC.ok = function(ret) {
|
442
|
+
return (ret !== false) && !(ret && ret.isError);
|
443
|
+
};
|
444
|
+
|
445
|
+
})({});
|
446
|
+
|
447
|
+
|
448
|
+
(function(exports) {
|
449
|
+
// ==========================================================================
|
450
|
+
// Project: SproutCore - JavaScript Application Framework
|
451
|
+
// Copyright: ©2006-2011 Strobe Inc. and contributors.
|
452
|
+
// Portions ©2008-2011 Apple Inc. All rights reserved.
|
453
|
+
// License: Licensed under MIT license (see license.js)
|
454
|
+
// ==========================================================================
|
455
|
+
|
456
|
+
var get = SC.get, set = SC.set;
|
457
|
+
|
458
|
+
/**
|
459
|
+
@class
|
460
|
+
|
461
|
+
Implements support for Ajax requests using XHR and other prototcols.
|
462
|
+
|
463
|
+
SC.Request is much like an inverted version of the request/response objects
|
464
|
+
you receive when implementing HTTP servers.
|
465
|
+
|
466
|
+
To send a request, you just need to create your request object, configure
|
467
|
+
your options, and call send() to initiate the request.
|
468
|
+
|
469
|
+
@extends SC.Object
|
470
|
+
@extends SC.Copyable
|
471
|
+
@extends SC.Freezable
|
472
|
+
*/
|
473
|
+
SC.Request = SC.Object.extend(SC.Copyable, SC.Freezable, {
|
474
|
+
|
475
|
+
// ..........................................................
|
476
|
+
// PROPERTIES
|
477
|
+
//
|
478
|
+
|
479
|
+
/**
|
480
|
+
Sends the request asynchronously instead of blocking the browser. You
|
481
|
+
should almost always make requests asynchronous. You can change this
|
482
|
+
options with the async() helper option (or simply set it directly).
|
483
|
+
|
484
|
+
@type Boolean
|
485
|
+
@default YES
|
486
|
+
*/
|
487
|
+
isAsynchronous: true,
|
488
|
+
|
489
|
+
/**
|
490
|
+
Current set of headers for the request
|
491
|
+
|
492
|
+
@field
|
493
|
+
@type Hash
|
494
|
+
@default {}
|
495
|
+
*/
|
496
|
+
headers: function() {
|
497
|
+
var ret = this._headers;
|
498
|
+
if (!ret) { ret = this._headers = {}; }
|
499
|
+
return ret;
|
500
|
+
}.property().cacheable(),
|
501
|
+
|
502
|
+
/**
|
503
|
+
Underlying response class to actually handle this request. Currently the
|
504
|
+
only supported option is SC.XHRResponse which uses a traditional
|
505
|
+
XHR transport.
|
506
|
+
|
507
|
+
@type SC.Response
|
508
|
+
@default SC.XHRResponse
|
509
|
+
*/
|
510
|
+
responseClass: SC.XHRResponse,
|
511
|
+
|
512
|
+
/**
|
513
|
+
The original request for copied requests.
|
514
|
+
|
515
|
+
@property SC.Request
|
516
|
+
@default null
|
517
|
+
*/
|
518
|
+
source: null,
|
519
|
+
|
520
|
+
/**
|
521
|
+
The URL this request to go to.
|
522
|
+
|
523
|
+
@type String
|
524
|
+
@default null
|
525
|
+
*/
|
526
|
+
url: null,
|
527
|
+
|
528
|
+
/**
|
529
|
+
The HTTP method to use.
|
530
|
+
|
531
|
+
@type String
|
532
|
+
@default 'GET'
|
533
|
+
*/
|
534
|
+
type: 'GET',
|
535
|
+
|
536
|
+
/**
|
537
|
+
The body of the request. May be an object is isJSON or isXML is set,
|
538
|
+
otherwise should be a string.
|
539
|
+
|
540
|
+
@type Object|String
|
541
|
+
@default null
|
542
|
+
*/
|
543
|
+
body: null,
|
544
|
+
|
545
|
+
/**
|
546
|
+
@type String
|
547
|
+
@default 'json'
|
548
|
+
*/
|
549
|
+
dataType: 'json',
|
550
|
+
|
551
|
+
/**
|
552
|
+
An optional timeout value of the request, in milliseconds. The timer
|
553
|
+
begins when SC.Response#fire is actually invoked by the request manager
|
554
|
+
and not necessarily when SC.Request#send is invoked. If this timeout is
|
555
|
+
reached before a response is received, the equivalent of
|
556
|
+
SC.Request.manager#cancel() will be invoked on the SC.Response instance
|
557
|
+
and the didReceive() callback will be called.
|
558
|
+
|
559
|
+
An exception will be thrown if you try to invoke send() on a request that
|
560
|
+
has both a timeout and isAsyncronous set to NO.
|
561
|
+
|
562
|
+
@type Number
|
563
|
+
@default null
|
564
|
+
*/
|
565
|
+
timeout: null,
|
566
|
+
|
567
|
+
/**
|
568
|
+
|
569
|
+
*/
|
570
|
+
ifModified: false,
|
571
|
+
|
572
|
+
// ..........................................................
|
573
|
+
// CALLBACKS
|
574
|
+
//
|
575
|
+
|
576
|
+
/**
|
577
|
+
Invoked on the original request object just before a copied request is
|
578
|
+
frozen and then sent to the server. This gives you one last change to
|
579
|
+
fixup the request; possibly adding headers and other options.
|
580
|
+
|
581
|
+
If you do not want the request to actually send, call cancel().
|
582
|
+
|
583
|
+
@param {SC.Request} request A copy of the request object, not frozen
|
584
|
+
@param {SC.Response} response The object that will wrap the response
|
585
|
+
*/
|
586
|
+
willSend: function(request, response) {},
|
587
|
+
|
588
|
+
/**
|
589
|
+
Invoked on the original request object just after the request is sent to
|
590
|
+
the server. You might use this callback to update some state in your
|
591
|
+
application.
|
592
|
+
|
593
|
+
The passed request is a frozen copy of the request, indicating the
|
594
|
+
options set at the time of the request.
|
595
|
+
|
596
|
+
@param {SC.Request} request A copy of the request object, frozen
|
597
|
+
@param {SC.Response} response The object that will wrap the response
|
598
|
+
@returns {Boolean} YES on success, NO on failure
|
599
|
+
*/
|
600
|
+
didSend: function(request, response) {},
|
601
|
+
|
602
|
+
/**
|
603
|
+
Invoked when a response has been received but not yet processed. This is
|
604
|
+
your chance to fix up the response based on the results. If you don't
|
605
|
+
want to continue processing the response call response.cancel().
|
606
|
+
|
607
|
+
@param {SC.Request} request A copy of the request object, frozen
|
608
|
+
@param {SC.Response} response The object that will wrap the response
|
609
|
+
*/
|
610
|
+
willReceive: function(request, response) {},
|
611
|
+
|
612
|
+
/**
|
613
|
+
Invoked after a response has been processed but before any listeners are
|
614
|
+
notified. You can do any standard processing on the request at this
|
615
|
+
point. If you don't want to allow notifications to continue, call
|
616
|
+
response.cancel()
|
617
|
+
|
618
|
+
@param {SC.Request} request A copy of the request object, frozen
|
619
|
+
@param {SC.Response} response The object that will wrap the response
|
620
|
+
*/
|
621
|
+
didReceive: function(request, response) {},
|
622
|
+
|
623
|
+
|
624
|
+
// ..........................................................
|
625
|
+
// HELPER METHODS
|
626
|
+
//
|
627
|
+
|
628
|
+
/** @private */
|
629
|
+
concatenatedProperties: 'COPY_KEYS',
|
630
|
+
|
631
|
+
/** @private */
|
632
|
+
COPY_KEYS: ['isAsynchronous', 'dataType', 'url', 'type', 'timeout', 'body', 'responseClass', 'willSend', 'didSend', 'willReceive', 'didReceive'],
|
633
|
+
|
634
|
+
/**
|
635
|
+
Returns a copy of the current request. This will only copy certain
|
636
|
+
properties so if you want to add additional properties to the copy you
|
637
|
+
will need to override copy() in a subclass.
|
638
|
+
|
639
|
+
@returns {SC.Request} new request
|
640
|
+
*/
|
641
|
+
copy: function() {
|
642
|
+
var ret = {},
|
643
|
+
keys = this.COPY_KEYS,
|
644
|
+
loc = keys.length,
|
645
|
+
key, listeners, headers;
|
646
|
+
|
647
|
+
while(--loc >= 0) {
|
648
|
+
key = keys[loc];
|
649
|
+
if (this.hasOwnProperty(key)) {
|
650
|
+
ret[key] = this.get(key);
|
651
|
+
}
|
652
|
+
}
|
653
|
+
|
654
|
+
if (this.hasOwnProperty('listeners')) {
|
655
|
+
ret.listeners = SC.copy(this.get('listeners'));
|
656
|
+
}
|
657
|
+
|
658
|
+
if (this.hasOwnProperty('_headers')) {
|
659
|
+
ret._headers = SC.copy(this._headers);
|
660
|
+
}
|
661
|
+
|
662
|
+
ret.source = get(this, 'source') || this;
|
663
|
+
|
664
|
+
return this.constructor.create(ret);
|
665
|
+
},
|
666
|
+
|
667
|
+
/**
|
668
|
+
To set headers on the request object. Pass either a single key/value
|
669
|
+
pair or a hash of key/value pairs. If you pass only a header name, this
|
670
|
+
will return the current value of the header.
|
671
|
+
|
672
|
+
@param {String|Hash} key
|
673
|
+
@param {String} value
|
674
|
+
@returns {SC.Request|Object} receiver
|
675
|
+
*/
|
676
|
+
header: function(key, value) {
|
677
|
+
var headers;
|
678
|
+
|
679
|
+
if (SC.typeOf(key) === 'string') {
|
680
|
+
headers = this._headers;
|
681
|
+
if (arguments.length === 1) {
|
682
|
+
return headers ? headers[key] : null;
|
683
|
+
} else {
|
684
|
+
this.propertyWillChange('headers');
|
685
|
+
if (!headers) { headers = this._headers = {}; }
|
686
|
+
headers[key] = value;
|
687
|
+
this.propertyDidChange('headers');
|
688
|
+
return this;
|
689
|
+
}
|
690
|
+
|
691
|
+
// handle parsing hash of parameters
|
692
|
+
} else if (value === undefined) {
|
693
|
+
headers = key;
|
694
|
+
this.beginPropertyChanges();
|
695
|
+
for(key in headers) {
|
696
|
+
if (!headers.hasOwnProperty(key)) { continue; }
|
697
|
+
this.header(key, headers[key]);
|
698
|
+
}
|
699
|
+
this.endPropertyChanges();
|
700
|
+
return this;
|
701
|
+
}
|
702
|
+
|
703
|
+
return this;
|
704
|
+
},
|
705
|
+
|
706
|
+
/**
|
707
|
+
Clears the list of headers that were set on this request.
|
708
|
+
This could be used by a subclass to blow-away any custom
|
709
|
+
headers that were added by the super class.
|
710
|
+
*/
|
711
|
+
clearHeaders: function() {
|
712
|
+
this.propertyWillChange('headers');
|
713
|
+
this._headers = {};
|
714
|
+
this.propertyDidChange('headers');
|
715
|
+
},
|
716
|
+
|
717
|
+
/**
|
718
|
+
Converts the current request to be asynchronous.
|
719
|
+
|
720
|
+
@param {Boolean} flag YES to make asynchronous, NO or undefined. Default YES.
|
721
|
+
@returns {SC.Request} receiver
|
722
|
+
*/
|
723
|
+
async: function(flag) {
|
724
|
+
if (flag === undefined) { flag = true; }
|
725
|
+
set(this, 'isAsynchronous', flag);
|
726
|
+
return this;
|
727
|
+
},
|
728
|
+
|
729
|
+
/**
|
730
|
+
Sets the maximum amount of time the request will wait for a response.
|
731
|
+
|
732
|
+
@param {Number} timeout The timeout in milliseconds.
|
733
|
+
@returns {SC.Request} receiver
|
734
|
+
*/
|
735
|
+
timeoutAfter: function(timeout) {
|
736
|
+
set(this, 'timeout', timeout);
|
737
|
+
return this;
|
738
|
+
},
|
739
|
+
|
740
|
+
/**
|
741
|
+
Converts the current request to use JSON.
|
742
|
+
|
743
|
+
@returns {SC.Request} receiver
|
744
|
+
*/
|
745
|
+
json: function() {
|
746
|
+
set(this, 'dataType', 'json');
|
747
|
+
return this;
|
748
|
+
},
|
749
|
+
|
750
|
+
/**
|
751
|
+
Converts the current request to use XML.
|
752
|
+
|
753
|
+
@returns {SC.Request} recevier
|
754
|
+
*/
|
755
|
+
xml: function() {
|
756
|
+
set(this, 'dataType', 'xml');
|
757
|
+
return this;
|
758
|
+
},
|
759
|
+
|
760
|
+
/**
|
761
|
+
|
762
|
+
*/
|
763
|
+
isJSON: function() {
|
764
|
+
return get(this, 'dataType') === 'json';
|
765
|
+
}.property('dataType').cacheable(),
|
766
|
+
|
767
|
+
/**
|
768
|
+
|
769
|
+
*/
|
770
|
+
isXML: function() {
|
771
|
+
return get(this, 'dataType') === 'xml';
|
772
|
+
}.property('dataType').cacheable(),
|
773
|
+
|
774
|
+
/**
|
775
|
+
Will fire the actual request. If you have set the request to use JSON
|
776
|
+
mode then you can pass any object that can be converted to JSON as the
|
777
|
+
body. Otherwise you should pass a string body.
|
778
|
+
|
779
|
+
@param {String|Object} [body]
|
780
|
+
@returns {SC.Response} New response object
|
781
|
+
*/
|
782
|
+
send: function(body) {
|
783
|
+
// Sanity-check: Be sure a timeout value was not specified if the request
|
784
|
+
// is synchronous (because it wouldn't work).
|
785
|
+
var timeout = get(this, 'timeout');
|
786
|
+
if (timeout && !get(this, 'isAsynchronous')) {
|
787
|
+
throw "Timeout values cannot be used with synchronous requests";
|
788
|
+
} else if (timeout === 0) {
|
789
|
+
throw "The timeout value must either not be specified or must be greater than 0";
|
790
|
+
}
|
791
|
+
|
792
|
+
if (body) { set(this, 'body', body); }
|
793
|
+
return SC.Request.manager.sendRequest(this.copy());
|
794
|
+
},
|
795
|
+
|
796
|
+
/**
|
797
|
+
Resends the current request. This is more efficient than calling send()
|
798
|
+
for requests that have already been used in a send. Otherwise acts just
|
799
|
+
like send(). Does not take a body argument.
|
800
|
+
|
801
|
+
@returns {SC.Response} new response object
|
802
|
+
*/
|
803
|
+
resend: function() {
|
804
|
+
var req = get(this, 'source') ? this : this.copy();
|
805
|
+
return SC.Request.manager.sendRequest(req);
|
806
|
+
},
|
807
|
+
|
808
|
+
/**
|
809
|
+
Configures a callback to execute when a request completes. You must pass
|
810
|
+
at least a target and action/method to this and optionally a status code.
|
811
|
+
You may also pass additional parameters which will be passed along to your
|
812
|
+
callback. If your callback handled the notification, it should return YES.
|
813
|
+
|
814
|
+
## Scoping With Status Codes
|
815
|
+
|
816
|
+
If you pass a status code as the first option to this method, then your
|
817
|
+
notification callback will only be called if the response status matches
|
818
|
+
the code. For example, if you pass 201 (or SC.Request.CREATED) then
|
819
|
+
your method will only be called if the response status from the server
|
820
|
+
is 201.
|
821
|
+
|
822
|
+
You can also pass "generic" status codes such as 200, 300, or 400, which
|
823
|
+
will be invoked anytime the status code is the range if a more specific
|
824
|
+
notifier was not registered first and returned YES.
|
825
|
+
|
826
|
+
Finally, passing a status code of 0 or no status at all will cause your
|
827
|
+
method to be executed no matter what the resulting status is unless a
|
828
|
+
more specific notifier was registered and returned YES.
|
829
|
+
|
830
|
+
## Callback Format
|
831
|
+
|
832
|
+
Your notification callback should expect to receive the Response object
|
833
|
+
as the first parameter plus any additional parameters that you pass.
|
834
|
+
|
835
|
+
@param {Number} status
|
836
|
+
@param {Object} target
|
837
|
+
@param {String|Function} action
|
838
|
+
@param {Hash} params
|
839
|
+
@returns {SC.Request} receiver
|
840
|
+
*/
|
841
|
+
notify: function(status, target, action, params) {
|
842
|
+
// normalize status
|
843
|
+
var hasStatus = true;
|
844
|
+
if (SC.typeOf(status) !== 'number') {
|
845
|
+
params = $.makeArray(arguments).slice(2);
|
846
|
+
action = target;
|
847
|
+
target = status;
|
848
|
+
status = 0;
|
849
|
+
hasStatus = false;
|
850
|
+
} else {
|
851
|
+
params = $.makeArray(arguments).slice(3);
|
852
|
+
}
|
853
|
+
|
854
|
+
var listeners = get(this, 'listeners');
|
855
|
+
if (!listeners) { set(this, 'listeners', listeners = {}); }
|
856
|
+
if(!listeners[status]) { listeners[status] = []; }
|
857
|
+
|
858
|
+
listeners[status].push({target: target, action: action, params: params});
|
859
|
+
|
860
|
+
return this;
|
861
|
+
}
|
862
|
+
|
863
|
+
});
|
864
|
+
|
865
|
+
SC.Request.reopenClass({
|
866
|
+
|
867
|
+
/**
|
868
|
+
Helper method for quickly setting up a GET request.
|
869
|
+
|
870
|
+
@param {String} url of request
|
871
|
+
@returns {SC.Request} receiver
|
872
|
+
*/
|
873
|
+
getUrl: function(url) {
|
874
|
+
return this.create().set('url', url).set('type', 'GET');
|
875
|
+
},
|
876
|
+
|
877
|
+
/**
|
878
|
+
Helper method for quickly setting up a HEAD request.
|
879
|
+
|
880
|
+
@param {String} url of request
|
881
|
+
@returns {SC.Request} receiver
|
882
|
+
*/
|
883
|
+
headUrl: function(url) {
|
884
|
+
return this.create().set('url', url).set('type', 'HEAD');
|
885
|
+
},
|
886
|
+
|
887
|
+
/**
|
888
|
+
Helper method for quickly setting up a DELETE request.
|
889
|
+
|
890
|
+
@param {String} url of request
|
891
|
+
@returns {SC.Request} receiver
|
892
|
+
*/
|
893
|
+
deleteUrl: function(url) {
|
894
|
+
return this.create().set('url', url).set('type', 'DELETE');
|
895
|
+
},
|
896
|
+
|
897
|
+
/**
|
898
|
+
Helper method for quickly setting up a POST request.
|
899
|
+
|
900
|
+
@param {String} url of request
|
901
|
+
@param {String} body
|
902
|
+
@returns {SC.Request} receiver
|
903
|
+
*/
|
904
|
+
postUrl: function(url, body) {
|
905
|
+
var req = this.create().set('url', url).set('type', 'POST');
|
906
|
+
if (body) { set(req, 'body', body); }
|
907
|
+
return req;
|
908
|
+
},
|
909
|
+
|
910
|
+
/**
|
911
|
+
Helper method for quickly setting up a PUT request.
|
912
|
+
|
913
|
+
@param {String} url of request
|
914
|
+
@param {String} body
|
915
|
+
@returns {SC.Request} receiver
|
916
|
+
*/
|
917
|
+
putUrl: function(url, body) {
|
918
|
+
var req = this.create().set('url', url).set('type', 'PUT');
|
919
|
+
if (body) { set(req, 'body', body); }
|
920
|
+
return req;
|
921
|
+
}
|
922
|
+
|
923
|
+
});
|
924
|
+
|
925
|
+
/**
|
926
|
+
@class
|
927
|
+
|
928
|
+
The request manager coordinates all of the active XHR requests. It will
|
929
|
+
only allow a certain number of requests to be active at a time; queuing
|
930
|
+
any others. This allows you more precise control over which requests load
|
931
|
+
in which order.
|
932
|
+
*/
|
933
|
+
SC.Request.manager = SC.Object.create({
|
934
|
+
|
935
|
+
/**
|
936
|
+
Maximum number of concurrent requests allowed. 6 for all browsers.
|
937
|
+
|
938
|
+
@type Number
|
939
|
+
@default 6
|
940
|
+
*/
|
941
|
+
maxRequests: 6,
|
942
|
+
|
943
|
+
/**
|
944
|
+
Current requests that are inflight.
|
945
|
+
|
946
|
+
@type Array
|
947
|
+
@default []
|
948
|
+
*/
|
949
|
+
inflight: [],
|
950
|
+
|
951
|
+
/**
|
952
|
+
Requests that are pending and have not been started yet.
|
953
|
+
|
954
|
+
@type Array
|
955
|
+
@default []
|
956
|
+
*/
|
957
|
+
pending: [],
|
958
|
+
|
959
|
+
|
960
|
+
// ..........................................................
|
961
|
+
// METHODS
|
962
|
+
//
|
963
|
+
|
964
|
+
/**
|
965
|
+
Invoked by the send() method on a request. This will create a new low-
|
966
|
+
level transport object and queue it if needed.
|
967
|
+
|
968
|
+
@param {SC.Request} request the request to send
|
969
|
+
@returns {SC.Object} response object
|
970
|
+
*/
|
971
|
+
sendRequest: function(request) {
|
972
|
+
if (!request) { return null; }
|
973
|
+
|
974
|
+
// create low-level transport. copy all critical data for request over
|
975
|
+
// so that if the request has been reconfigured the transport will still
|
976
|
+
// work.
|
977
|
+
var response = get(request, 'responseClass').create({request: request});
|
978
|
+
|
979
|
+
// add to pending queue
|
980
|
+
get(this, 'pending').pushObject(response);
|
981
|
+
this.fireRequestIfNeeded();
|
982
|
+
|
983
|
+
return response;
|
984
|
+
},
|
985
|
+
|
986
|
+
/**
|
987
|
+
Cancels a specific request. If the request is pending it will simply
|
988
|
+
be removed. Otherwise it will actually be cancelled.
|
989
|
+
|
990
|
+
@param {Object} response a response object
|
991
|
+
@returns {Boolean} YES if cancelled
|
992
|
+
*/
|
993
|
+
cancel: function(response) {
|
994
|
+
var pending = get(this, 'pending'),
|
995
|
+
inflight = get(this, 'inflight'),
|
996
|
+
idx;
|
997
|
+
|
998
|
+
if (pending.indexOf(response) >= 0) {
|
999
|
+
this.propertyWillChange('pending');
|
1000
|
+
pending.removeObject(response);
|
1001
|
+
this.propertyDidChange('pending');
|
1002
|
+
return true;
|
1003
|
+
} else if (inflight.indexOf(response) >= 0) {
|
1004
|
+
response.cancel();
|
1005
|
+
|
1006
|
+
inflight.removeObject(response);
|
1007
|
+
this.fireRequestIfNeeded();
|
1008
|
+
return true;
|
1009
|
+
}
|
1010
|
+
|
1011
|
+
return false;
|
1012
|
+
},
|
1013
|
+
|
1014
|
+
/**
|
1015
|
+
Cancels all inflight and pending requests.
|
1016
|
+
|
1017
|
+
@returns {Boolean} YES if any items were cancelled.
|
1018
|
+
*/
|
1019
|
+
cancelAll: function() {
|
1020
|
+
if (get(this, 'pending').length || get(this, 'inflight').length) {
|
1021
|
+
set(this, 'pending', []);
|
1022
|
+
get(this, 'inflight').forEach(function(r) { r.cancel(); });
|
1023
|
+
set(this, 'inflight', []);
|
1024
|
+
return true;
|
1025
|
+
}
|
1026
|
+
|
1027
|
+
return false;
|
1028
|
+
},
|
1029
|
+
|
1030
|
+
/**
|
1031
|
+
Checks the inflight queue. If there is an open slot, this will move a
|
1032
|
+
request from pending to inflight.
|
1033
|
+
|
1034
|
+
@returns {Object} receiver
|
1035
|
+
*/
|
1036
|
+
fireRequestIfNeeded: function() {
|
1037
|
+
var pending = get(this, 'pending'),
|
1038
|
+
inflight = get(this, 'inflight'),
|
1039
|
+
max = get(this, 'maxRequests'),
|
1040
|
+
next;
|
1041
|
+
|
1042
|
+
if ((pending.length>0) && (inflight.length<max)) {
|
1043
|
+
next = pending.shiftObject();
|
1044
|
+
inflight.pushObject(next);
|
1045
|
+
next.fire();
|
1046
|
+
}
|
1047
|
+
},
|
1048
|
+
|
1049
|
+
/**
|
1050
|
+
Called by a response/transport object when finishes running. Removes
|
1051
|
+
the transport from the queue and kicks off the next one.
|
1052
|
+
*/
|
1053
|
+
transportDidClose: function(response) {
|
1054
|
+
get(this, 'inflight').removeObject(response);
|
1055
|
+
this.fireRequestIfNeeded();
|
1056
|
+
}
|
1057
|
+
|
1058
|
+
});
|
1059
|
+
|
1060
|
+
})({});
|
1061
|
+
|
1062
|
+
|
1063
|
+
(function(exports) {
|
1064
|
+
// ==========================================================================
|
1065
|
+
// Project: SproutCore AJAX
|
1066
|
+
// Copyright: ©2011 Paul Chavard
|
1067
|
+
// License: Licensed under MIT license (see license.js)
|
1068
|
+
// ==========================================================================
|
1069
|
+
|
1070
|
+
|
1071
|
+
})({});
|