dom/tests/mochitest/ajax/mochikit/MochiKit/Async.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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);

mercurial