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

mercurial