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