summaryrefslogtreecommitdiff
path: root/frontend/gamma/js/MochiKit/Async.js
Unidiff
Diffstat (limited to 'frontend/gamma/js/MochiKit/Async.js') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/gamma/js/MochiKit/Async.js640
1 files changed, 640 insertions, 0 deletions
diff --git a/frontend/gamma/js/MochiKit/Async.js b/frontend/gamma/js/MochiKit/Async.js
new file mode 100644
index 0000000..c7408e7
--- a/dev/null
+++ b/frontend/gamma/js/MochiKit/Async.js
@@ -0,0 +1,640 @@
1/***
2
3MochiKit.Async 1.5
4
5See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7(c) 2005 Bob Ippolito. All rights Reserved.
8
9***/
10
11MochiKit.Base._module('Async', '1.5', ['Base']);
12
13/** @id MochiKit.Async.Deferred */
14MochiKit.Async.Deferred = function (/* optional */ canceller) {
15 this.chain = [];
16 this.id = this._nextId();
17 this.fired = -1;
18 this.paused = 0;
19 this.results = [null, null];
20 this.canceller = canceller;
21 this.silentlyCancelled = false;
22 this.chained = false;
23};
24
25MochiKit.Async.Deferred.prototype = {
26 /** @id MochiKit.Async.Deferred.prototype.repr */
27 repr: function () {
28 var state;
29 if (this.fired == -1) {
30 state = 'unfired';
31 } else if (this.fired === 0) {
32 state = 'success';
33 } else {
34 state = 'error';
35 }
36 return 'Deferred(' + this.id + ', ' + state + ')';
37 },
38
39 toString: MochiKit.Base.forwardCall("repr"),
40
41 _nextId: MochiKit.Base.counter(),
42
43 /** @id MochiKit.Async.Deferred.prototype.cancel */
44 cancel: function () {
45 var self = MochiKit.Async;
46 if (this.fired == -1) {
47 if (this.canceller) {
48 this.canceller(this);
49 } else {
50 this.silentlyCancelled = true;
51 }
52 if (this.fired == -1) {
53 this.errback(new self.CancelledError(this));
54 }
55 } else if ((this.fired === 0) && (this.results[0] instanceof self.Deferred)) {
56 this.results[0].cancel();
57 }
58 },
59
60 _resback: function (res) {
61 /***
62
63 The primitive that means either callback or errback
64
65 ***/
66 this.fired = ((res instanceof Error) ? 1 : 0);
67 this.results[this.fired] = res;
68 this._fire();
69 },
70
71 _check: function () {
72 if (this.fired != -1) {
73 if (!this.silentlyCancelled) {
74 throw new MochiKit.Async.AlreadyCalledError(this);
75 }
76 this.silentlyCancelled = false;
77 return;
78 }
79 },
80
81 /** @id MochiKit.Async.Deferred.prototype.callback */
82 callback: function (res) {
83 this._check();
84 if (res instanceof MochiKit.Async.Deferred) {
85 throw new Error("Deferred instances can only be chained if they are the result of a callback");
86 }
87 this._resback(res);
88 },
89
90 /** @id MochiKit.Async.Deferred.prototype.errback */
91 errback: function (res) {
92 this._check();
93 var self = MochiKit.Async;
94 if (res instanceof self.Deferred) {
95 throw new Error("Deferred instances can only be chained if they are the result of a callback");
96 }
97 if (!(res instanceof Error)) {
98 res = new self.GenericError(res);
99 }
100 this._resback(res);
101 },
102
103 /** @id MochiKit.Async.Deferred.prototype.addBoth */
104 addBoth: function (fn) {
105 if (arguments.length > 1) {
106 fn = MochiKit.Base.partial.apply(null, arguments);
107 }
108 return this.addCallbacks(fn, fn);
109 },
110
111 /** @id MochiKit.Async.Deferred.prototype.addCallback */
112 addCallback: function (fn) {
113 if (arguments.length > 1) {
114 fn = MochiKit.Base.partial.apply(null, arguments);
115 }
116 return this.addCallbacks(fn, null);
117 },
118
119 /** @id MochiKit.Async.Deferred.prototype.addErrback */
120 addErrback: function (fn) {
121 if (arguments.length > 1) {
122 fn = MochiKit.Base.partial.apply(null, arguments);
123 }
124 return this.addCallbacks(null, fn);
125 },
126
127 /** @id MochiKit.Async.Deferred.prototype.addCallbacks */
128 addCallbacks: function (cb, eb) {
129 if (this.chained) {
130 throw new Error("Chained Deferreds can not be re-used");
131 }
132 this.chain.push([cb, eb]);
133 if (this.fired >= 0) {
134 this._fire();
135 }
136 return this;
137 },
138
139 _fire: function () {
140 /***
141
142 Used internally to exhaust the callback sequence when a result
143 is available.
144
145 ***/
146 var chain = this.chain;
147 var fired = this.fired;
148 var res = this.results[fired];
149 var self = this;
150 var cb = null;
151 while (chain.length > 0 && this.paused === 0) {
152 // Array
153 var pair = chain.shift();
154 var f = pair[fired];
155 if (f === null) {
156 continue;
157 }
158 try {
159 res = f(res);
160 fired = ((res instanceof Error) ? 1 : 0);
161 if (res instanceof MochiKit.Async.Deferred) {
162 cb = function (res) {
163 self._resback(res);
164 self.paused--;
165 if ((self.paused === 0) && (self.fired >= 0)) {
166 self._fire();
167 }
168 };
169 this.paused++;
170 }
171 } catch (err) {
172 fired = 1;
173 if (!(err instanceof Error)) {
174 err = new MochiKit.Async.GenericError(err);
175 }
176 res = err;
177 }
178 }
179 this.fired = fired;
180 this.results[fired] = res;
181 if (cb && this.paused) {
182 // this is for "tail recursion" in case the dependent deferred
183 // is already fired
184 res.addBoth(cb);
185 res.chained = true;
186 }
187 }
188};
189
190MochiKit.Base.update(MochiKit.Async, {
191 /** @id MochiKit.Async.evalJSONRequest */
192 evalJSONRequest: function (req) {
193 return MochiKit.Base.evalJSON(req.responseText);
194 },
195
196 /** @id MochiKit.Async.succeed */
197 succeed: function (/* optional */result) {
198 var d = new MochiKit.Async.Deferred();
199 d.callback.apply(d, arguments);
200 return d;
201 },
202
203 /** @id MochiKit.Async.fail */
204 fail: function (/* optional */result) {
205 var d = new MochiKit.Async.Deferred();
206 d.errback.apply(d, arguments);
207 return d;
208 },
209
210 /** @id MochiKit.Async.getXMLHttpRequest */
211 getXMLHttpRequest: function () {
212 var self = arguments.callee;
213 if (!self.XMLHttpRequest) {
214 var tryThese = [
215 function () { return new XMLHttpRequest(); },
216 function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
217 function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
218 function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
219 function () {
220 throw new MochiKit.Async.BrowserComplianceError("Browser does not support XMLHttpRequest");
221 }
222 ];
223 for (var i = 0; i < tryThese.length; i++) {
224 var func = tryThese[i];
225 try {
226 self.XMLHttpRequest = func;
227 return func();
228 } catch (e) {
229 // pass
230 }
231 }
232 }
233 return self.XMLHttpRequest();
234 },
235
236 _xhr_onreadystatechange: function (d) {
237 // MochiKit.Logging.logDebug('this.readyState', this.readyState);
238 var m = MochiKit.Base;
239 if (this.readyState == 4) {
240 // IE SUCKS
241 try {
242 this.onreadystatechange = null;
243 } catch (e) {
244 try {
245 this.onreadystatechange = m.noop;
246 } catch (e) {
247 }
248 }
249 var status = null;
250 try {
251 status = this.status;
252 if (!status && m.isNotEmpty(this.responseText)) {
253 // 0 or undefined seems to mean cached or local
254 status = 304;
255 }
256 } catch (e) {
257 // pass
258 // MochiKit.Logging.logDebug('error getting status?', repr(items(e)));
259 }
260 // 200 is OK, 201 is CREATED, 204 is NO CONTENT
261 // 304 is NOT MODIFIED, 1223 is apparently a bug in IE
262 if (status == 200 || status == 201 || status == 204 ||
263 status == 304 || status == 1223) {
264 d.callback(this);
265 } else {
266 var err = new MochiKit.Async.XMLHttpRequestError(this, "Request failed");
267 if (err.number) {
268 // XXX: This seems to happen on page change
269 d.errback(err);
270 } else {
271 // XXX: this seems to happen when the server is unreachable
272 d.errback(err);
273 }
274 }
275 }
276 },
277
278 _xhr_canceller: function (req) {
279 // IE SUCKS
280 try {
281 req.onreadystatechange = null;
282 } catch (e) {
283 try {
284 req.onreadystatechange = MochiKit.Base.noop;
285 } catch (e) {
286 }
287 }
288 req.abort();
289 },
290
291
292 /** @id MochiKit.Async.sendXMLHttpRequest */
293 sendXMLHttpRequest: function (req, /* optional */ sendContent) {
294 if (typeof(sendContent) == "undefined" || sendContent === null) {
295 sendContent = "";
296 }
297
298 var m = MochiKit.Base;
299 var self = MochiKit.Async;
300 var d = new self.Deferred(m.partial(self._xhr_canceller, req));
301
302 try {
303 req.onreadystatechange = m.bind(self._xhr_onreadystatechange,
304 req, d);
305 req.send(sendContent);
306 } catch (e) {
307 try {
308 req.onreadystatechange = null;
309 } catch (ignore) {
310 // pass
311 }
312 d.errback(e);
313 }
314
315 return d;
316
317 },
318
319 /** @id MochiKit.Async.doXHR */
320 doXHR: function (url, opts) {
321 /*
322 Work around a Firefox bug by dealing with XHR during
323 the next event loop iteration. Maybe it's this one:
324 https://bugzilla.mozilla.org/show_bug.cgi?id=249843
325 */
326 var self = MochiKit.Async;
327 return self.callLater(0, self._doXHR, url, opts);
328 },
329
330 _doXHR: function (url, opts) {
331 var m = MochiKit.Base;
332 opts = m.update({
333 method: 'GET',
334 sendContent: ''
335 /*
336 queryString: undefined,
337 username: undefined,
338 password: undefined,
339 headers: undefined,
340 mimeType: undefined
341 */
342 }, opts);
343 var self = MochiKit.Async;
344 var req = self.getXMLHttpRequest();
345 if (opts.queryString) {
346 var qs = m.queryString(opts.queryString);
347 if (qs) {
348 url += "?" + qs;
349 }
350 }
351 // Safari will send undefined:undefined, so we have to check.
352 // We can't use apply, since the function is native.
353 if ('username' in opts) {
354 req.open(opts.method, url, true, opts.username, opts.password);
355 } else {
356 req.open(opts.method, url, true);
357 }
358 if (req.overrideMimeType && opts.mimeType) {
359 req.overrideMimeType(opts.mimeType);
360 }
361 req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
362 if (opts.headers) {
363 var headers = opts.headers;
364 if (!m.isArrayLike(headers)) {
365 headers = m.items(headers);
366 }
367 for (var i = 0; i < headers.length; i++) {
368 var header = headers[i];
369 var name = header[0];
370 var value = header[1];
371 req.setRequestHeader(name, value);
372 }
373 }
374 return self.sendXMLHttpRequest(req, opts.sendContent);
375 },
376
377 _buildURL: function (url/*, ...*/) {
378 if (arguments.length > 1) {
379 var m = MochiKit.Base;
380 var qs = m.queryString.apply(null, m.extend(null, arguments, 1));
381 if (qs) {
382 return url + "?" + qs;
383 }
384 }
385 return url;
386 },
387
388 /** @id MochiKit.Async.doSimpleXMLHttpRequest */
389 doSimpleXMLHttpRequest: function (url/*, ...*/) {
390 var self = MochiKit.Async;
391 url = self._buildURL.apply(self, arguments);
392 return self.doXHR(url);
393 },
394
395 /** @id MochiKit.Async.loadJSONDoc */
396 loadJSONDoc: function (url/*, ...*/) {
397 var self = MochiKit.Async;
398 url = self._buildURL.apply(self, arguments);
399 var d = self.doXHR(url, {
400 'mimeType': 'text/plain',
401 'headers': [['Accept', 'application/json']]
402 });
403 d = d.addCallback(self.evalJSONRequest);
404 return d;
405 },
406
407 /** @id MochiKit.Async.wait */
408 wait: function (seconds, /* optional */value) {
409 var d = new MochiKit.Async.Deferred();
410 var m = MochiKit.Base;
411 if (typeof(value) != 'undefined') {
412 d.addCallback(function () { return value; });
413 }
414 var timeout = setTimeout(
415 m.bind("callback", d),
416 Math.floor(seconds * 1000));
417 d.canceller = function () {
418 try {
419 clearTimeout(timeout);
420 } catch (e) {
421 // pass
422 }
423 };
424 return d;
425 },
426
427 /** @id MochiKit.Async.callLater */
428 callLater: function (seconds, func) {
429 var m = MochiKit.Base;
430 var pfunc = m.partial.apply(m, m.extend(null, arguments, 1));
431 return MochiKit.Async.wait(seconds).addCallback(
432 function (res) { return pfunc(); }
433 );
434 }
435});
436
437
438/** @id MochiKit.Async.DeferredLock */
439MochiKit.Async.DeferredLock = function () {
440 this.waiting = [];
441 this.locked = false;
442 this.id = this._nextId();
443};
444
445MochiKit.Async.DeferredLock.prototype = {
446 __class__: MochiKit.Async.DeferredLock,
447 /** @id MochiKit.Async.DeferredLock.prototype.acquire */
448 acquire: function () {
449 var d = new MochiKit.Async.Deferred();
450 if (this.locked) {
451 this.waiting.push(d);
452 } else {
453 this.locked = true;
454 d.callback(this);
455 }
456 return d;
457 },
458 /** @id MochiKit.Async.DeferredLock.prototype.release */
459 release: function () {
460 if (!this.locked) {
461 throw TypeError("Tried to release an unlocked DeferredLock");
462 }
463 this.locked = false;
464 if (this.waiting.length > 0) {
465 this.locked = true;
466 this.waiting.shift().callback(this);
467 }
468 },
469 _nextId: MochiKit.Base.counter(),
470 repr: function () {
471 var state;
472 if (this.locked) {
473 state = 'locked, ' + this.waiting.length + ' waiting';
474 } else {
475 state = 'unlocked';
476 }
477 return 'DeferredLock(' + this.id + ', ' + state + ')';
478 },
479 toString: MochiKit.Base.forwardCall("repr")
480
481};
482
483/** @id MochiKit.Async.DeferredList */
484MochiKit.Async.DeferredList = function (list, /* optional */fireOnOneCallback, fireOnOneErrback, consumeErrors, canceller) {
485
486 // call parent constructor
487 MochiKit.Async.Deferred.apply(this, [canceller]);
488
489 this.list = list;
490 var resultList = [];
491 this.resultList = resultList;
492
493 this.finishedCount = 0;
494 this.fireOnOneCallback = fireOnOneCallback;
495 this.fireOnOneErrback = fireOnOneErrback;
496 this.consumeErrors = consumeErrors;
497
498 var cb = MochiKit.Base.bind(this._cbDeferred, this);
499 for (var i = 0; i < list.length; i++) {
500 var d = list[i];
501 resultList.push(undefined);
502 d.addCallback(cb, i, true);
503 d.addErrback(cb, i, false);
504 }
505
506 if (list.length === 0 && !fireOnOneCallback) {
507 this.callback(this.resultList);
508 }
509
510};
511
512MochiKit.Async.DeferredList.prototype = new MochiKit.Async.Deferred();
513
514MochiKit.Async.DeferredList.prototype._cbDeferred = function (index, succeeded, result) {
515 this.resultList[index] = [succeeded, result];
516 this.finishedCount += 1;
517 if (this.fired == -1) {
518 if (succeeded && this.fireOnOneCallback) {
519 this.callback([index, result]);
520 } else if (!succeeded && this.fireOnOneErrback) {
521 this.errback(result);
522 } else if (this.finishedCount == this.list.length) {
523 this.callback(this.resultList);
524 }
525 }
526 if (!succeeded && this.consumeErrors) {
527 result = null;
528 }
529 return result;
530};
531
532/** @id MochiKit.Async.gatherResults */
533MochiKit.Async.gatherResults = function (deferredList) {
534 var d = new MochiKit.Async.DeferredList(deferredList, false, true, false);
535 d.addCallback(function (results) {
536 var ret = [];
537 for (var i = 0; i < results.length; i++) {
538 ret.push(results[i][1]);
539 }
540 return ret;
541 });
542 return d;
543};
544
545/** @id MochiKit.Async.maybeDeferred */
546MochiKit.Async.maybeDeferred = function (func) {
547 var self = MochiKit.Async;
548 var result;
549 try {
550 var r = func.apply(null, MochiKit.Base.extend([], arguments, 1));
551 if (r instanceof self.Deferred) {
552 result = r;
553 } else if (r instanceof Error) {
554 result = self.fail(r);
555 } else {
556 result = self.succeed(r);
557 }
558 } catch (e) {
559 result = self.fail(e);
560 }
561 return result;
562};
563
564
565MochiKit.Async.__new__ = function () {
566 var m = MochiKit.Base;
567 var ne = m.partial(m._newNamedError, this);
568
569 ne("AlreadyCalledError",
570 /** @id MochiKit.Async.AlreadyCalledError */
571 function (deferred) {
572 /***
573
574 Raised by the Deferred if callback or errback happens
575 after it was already fired.
576
577 ***/
578 this.deferred = deferred;
579 }
580 );
581
582 ne("CancelledError",
583 /** @id MochiKit.Async.CancelledError */
584 function (deferred) {
585 /***
586
587 Raised by the Deferred cancellation mechanism.
588
589 ***/
590 this.deferred = deferred;
591 }
592 );
593
594 ne("BrowserComplianceError",
595 /** @id MochiKit.Async.BrowserComplianceError */
596 function (msg) {
597 /***
598
599 Raised when the JavaScript runtime is not capable of performing
600 the given function. Technically, this should really never be
601 raised because a non-conforming JavaScript runtime probably
602 isn't going to support exceptions in the first place.
603
604 ***/
605 this.message = msg;
606 }
607 );
608
609 ne("GenericError",
610 /** @id MochiKit.Async.GenericError */
611 function (msg) {
612 this.message = msg;
613 }
614 );
615
616 ne("XMLHttpRequestError",
617 /** @id MochiKit.Async.XMLHttpRequestError */
618 function (req, msg) {
619 /***
620
621 Raised when an XMLHttpRequest does not complete for any reason.
622
623 ***/
624 this.req = req;
625 this.message = msg;
626 try {
627 // Strange but true that this can raise in some cases.
628 this.number = req.status;
629 } catch (e) {
630 // pass
631 }
632 }
633 );
634
635 m.nameFunctions(this);
636};
637
638MochiKit.Async.__new__();
639
640MochiKit.Base._exportSymbols(this, MochiKit.Async);