testing/mochitest/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 eval('(' + arguments[0].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, 304 is NOT_MODIFIED
   288             if (status == 200 || status == 304) { // OK
   289                 d.callback(this);
   290             } else {
   291                 var err = new MochiKit.Async.XMLHttpRequestError(this, "Request failed");
   292                 if (err.number) {
   293                     // XXX: This seems to happen on page change
   294                     d.errback(err);
   295                 } else {
   296                     // XXX: this seems to happen when the server is unreachable
   297                     d.errback(err);
   298                 }
   299             }
   300         }
   301     },
   303     _xhr_canceller: function (req) {
   304         // IE SUCKS
   305         try {
   306             req.onreadystatechange = null;
   307         } catch (e) {
   308             try {
   309                 req.onreadystatechange = MochiKit.Base.noop;
   310             } catch (e) {
   311             }
   312         }
   313         req.abort();
   314     },
   317     /** @id MochiKit.Async.sendXMLHttpRequest */
   318     sendXMLHttpRequest: function (req, /* optional */ sendContent) {
   319         if (typeof(sendContent) == "undefined" || sendContent === null) {
   320             sendContent = "";
   321         }
   323         var m = MochiKit.Base;
   324         var self = MochiKit.Async;
   325         var d = new self.Deferred(m.partial(self._xhr_canceller, req));
   327         try {
   328             req.onreadystatechange = m.bind(self._xhr_onreadystatechange,
   329                 req, d);
   330             req.send(sendContent);
   331         } catch (e) {
   332             try {
   333                 req.onreadystatechange = null;
   334             } catch (ignore) {
   335                 // pass
   336             }
   337             d.errback(e);
   338         }
   340         return d;
   342     },
   344     /** @id MochiKit.Async.doXHR */
   345     doXHR: function (url, opts) {
   346         var m = MochiKit.Base;
   347         opts = m.update({
   348             method: 'GET',
   349             sendContent: ''
   350             /*
   351             queryString: undefined,
   352             username: undefined,
   353             password: undefined,
   354             headers: undefined,
   355             mimeType: undefined
   356             */
   357         }, opts);
   358         var self = MochiKit.Async;
   359         var req = self.getXMLHttpRequest();
   360         if (opts.queryString) {
   361             var qs = m.queryString(opts.queryString);
   362             if (qs) {
   363                 url += "?" + qs;
   364             }
   365         }
   366         req.open(opts.method, url, true, opts.username, opts.password);
   367         if (req.overrideMimeType && opts.mimeType) {
   368             req.overrideMimeType(opts.mimeType);
   369         }
   370         if (opts.headers) {
   371             var headers = opts.headers;
   372             if (!m.isArrayLike(headers)) {
   373                 headers = m.items(headers);
   374             }
   375             for (var i = 0; i < headers.length; i++) {
   376                 var header = headers[i];
   377                 var name = header[0];
   378                 var value = header[1];
   379                 req.setRequestHeader(name, value);
   380             }
   381         }
   382         return self.sendXMLHttpRequest(req, opts.sendContent);
   383     },
   385     _buildURL: function (url/*, ...*/) {
   386         if (arguments.length > 1) {
   387             var m = MochiKit.Base;
   388             var qs = m.queryString.apply(null, m.extend(null, arguments, 1));
   389             if (qs) {
   390                 return url + "?" + qs;
   391             }
   392         }
   393         return url;
   394     },
   396     /** @id MochiKit.Async.doSimpleXMLHttpRequest */
   397     doSimpleXMLHttpRequest: function (url/*, ...*/) {
   398         var self = MochiKit.Async;
   399         url = self._buildURL.apply(self, arguments);
   400         return self.doXHR(url);
   401     },
   403     /** @id MochiKit.Async.loadJSONDoc */
   404     loadJSONDoc: function (url/*, ...*/) {
   405         var self = MochiKit.Async;
   406         url = self._buildURL.apply(self, arguments);
   407         var d = self.doXHR(url, {
   408             'mimeType': 'text/plain',
   409             'headers': [['Accept', 'application/json']]
   410         });
   411         d = d.addCallback(self.evalJSONRequest);
   412         return d;
   413     },
   415     /** @id MochiKit.Async.wait */
   416     wait: function (seconds, /* optional */value) {
   417         var d = new MochiKit.Async.Deferred();
   418         var m = MochiKit.Base;
   419         if (typeof(value) != 'undefined') {
   420             d.addCallback(function () { return value; });
   421         }
   422         var timeout = setTimeout(
   423             m.bind("callback", d),
   424             Math.floor(seconds * 1000));
   425         d.canceller = function () {
   426             try {
   427                 clearTimeout(timeout);
   428             } catch (e) {
   429                 // pass
   430             }
   431         };
   432         return d;
   433     },
   435     /** @id MochiKit.Async.callLater */
   436     callLater: function (seconds, func) {
   437         var m = MochiKit.Base;
   438         var pfunc = m.partial.apply(m, m.extend(null, arguments, 1));
   439         return MochiKit.Async.wait(seconds).addCallback(
   440             function (res) { return pfunc(); }
   441         );
   442     }
   443 });
   446 /** @id MochiKit.Async.DeferredLock */
   447 MochiKit.Async.DeferredLock = function () {
   448     this.waiting = [];
   449     this.locked = false;
   450     this.id = this._nextId();
   451 };
   453 MochiKit.Async.DeferredLock.prototype = {
   454     __class__: MochiKit.Async.DeferredLock,
   455     /** @id MochiKit.Async.DeferredLock.prototype.acquire */
   456     acquire: function () {
   457         var d = new MochiKit.Async.Deferred();
   458         if (this.locked) {
   459             this.waiting.push(d);
   460         } else {
   461             this.locked = true;
   462             d.callback(this);
   463         }
   464         return d;
   465     },
   466     /** @id MochiKit.Async.DeferredLock.prototype.release */
   467     release: function () {
   468         if (!this.locked) {
   469             throw TypeError("Tried to release an unlocked DeferredLock");
   470         }
   471         this.locked = false;
   472         if (this.waiting.length > 0) {
   473             this.locked = true;
   474             this.waiting.shift().callback(this);
   475         }
   476     },
   477     _nextId: MochiKit.Base.counter(),
   478     repr: function () {
   479         var state;
   480         if (this.locked) {
   481             state = 'locked, ' + this.waiting.length + ' waiting';
   482         } else {
   483             state = 'unlocked';
   484         }
   485         return 'DeferredLock(' + this.id + ', ' + state + ')';
   486     },
   487     toString: MochiKit.Base.forwardCall("repr")
   489 };
   491 /** @id MochiKit.Async.DeferredList */
   492 MochiKit.Async.DeferredList = function (list, /* optional */fireOnOneCallback, fireOnOneErrback, consumeErrors, canceller) {
   494     // call parent constructor
   495     MochiKit.Async.Deferred.apply(this, [canceller]);
   497     this.list = list;
   498     var resultList = [];
   499     this.resultList = resultList;
   501     this.finishedCount = 0;
   502     this.fireOnOneCallback = fireOnOneCallback;
   503     this.fireOnOneErrback = fireOnOneErrback;
   504     this.consumeErrors = consumeErrors;
   506     var cb = MochiKit.Base.bind(this._cbDeferred, this);
   507     for (var i = 0; i < list.length; i++) {
   508         var d = list[i];
   509         resultList.push(undefined);
   510         d.addCallback(cb, i, true);
   511         d.addErrback(cb, i, false);
   512     }
   514     if (list.length === 0 && !fireOnOneCallback) {
   515         this.callback(this.resultList);
   516     }
   518 };
   520 MochiKit.Async.DeferredList.prototype = new MochiKit.Async.Deferred();
   522 MochiKit.Async.DeferredList.prototype._cbDeferred = function (index, succeeded, result) {
   523     this.resultList[index] = [succeeded, result];
   524     this.finishedCount += 1;
   525     if (this.fired == -1) {
   526         if (succeeded && this.fireOnOneCallback) {
   527             this.callback([index, result]);
   528         } else if (!succeeded && this.fireOnOneErrback) {
   529             this.errback(result);
   530         } else if (this.finishedCount == this.list.length) {
   531             this.callback(this.resultList);
   532         }
   533     }
   534     if (!succeeded && this.consumeErrors) {
   535         result = null;
   536     }
   537     return result;
   538 };
   540 /** @id MochiKit.Async.gatherResults */
   541 MochiKit.Async.gatherResults = function (deferredList) {
   542     var d = new MochiKit.Async.DeferredList(deferredList, false, true, false);
   543     d.addCallback(function (results) {
   544         var ret = [];
   545         for (var i = 0; i < results.length; i++) {
   546             ret.push(results[i][1]);
   547         }
   548         return ret;
   549     });
   550     return d;
   551 };
   553 /** @id MochiKit.Async.maybeDeferred */
   554 MochiKit.Async.maybeDeferred = function (func) {
   555     var self = MochiKit.Async;
   556     var result;
   557     try {
   558         var r = func.apply(null, MochiKit.Base.extend([], arguments, 1));
   559         if (r instanceof self.Deferred) {
   560             result = r;
   561         } else if (r instanceof Error) {
   562             result = self.fail(r);
   563         } else {
   564             result = self.succeed(r);
   565         }
   566     } catch (e) {
   567         result = self.fail(e);
   568     }
   569     return result;
   570 };
   573 MochiKit.Async.EXPORT = [
   574     "AlreadyCalledError",
   575     "CancelledError",
   576     "BrowserComplianceError",
   577     "GenericError",
   578     "XMLHttpRequestError",
   579     "Deferred",
   580     "succeed",
   581     "fail",
   582     "getXMLHttpRequest",
   583     "doSimpleXMLHttpRequest",
   584     "loadJSONDoc",
   585     "wait",
   586     "callLater",
   587     "sendXMLHttpRequest",
   588     "DeferredLock",
   589     "DeferredList",
   590     "gatherResults",
   591     "maybeDeferred",
   592     "doXHR"
   593 ];
   595 MochiKit.Async.EXPORT_OK = [
   596     "evalJSONRequest"
   597 ];
   599 MochiKit.Async.__new__ = function () {
   600     var m = MochiKit.Base;
   601     var ne = m.partial(m._newNamedError, this);
   603     ne("AlreadyCalledError", 
   604         /** @id MochiKit.Async.AlreadyCalledError */
   605         function (deferred) {
   606             /***
   608             Raised by the Deferred if callback or errback happens
   609             after it was already fired.
   611             ***/
   612             this.deferred = deferred;
   613         }
   614     );
   616     ne("CancelledError",
   617         /** @id MochiKit.Async.CancelledError */
   618         function (deferred) {
   619             /***
   621             Raised by the Deferred cancellation mechanism.
   623             ***/
   624             this.deferred = deferred;
   625         }
   626     );
   628     ne("BrowserComplianceError",
   629         /** @id MochiKit.Async.BrowserComplianceError */
   630         function (msg) {
   631             /***
   633             Raised when the JavaScript runtime is not capable of performing
   634             the given function.  Technically, this should really never be
   635             raised because a non-conforming JavaScript runtime probably
   636             isn't going to support exceptions in the first place.
   638             ***/
   639             this.message = msg;
   640         }
   641     );
   643     ne("GenericError", 
   644         /** @id MochiKit.Async.GenericError */
   645         function (msg) {
   646             this.message = msg;
   647         }
   648     );
   650     ne("XMLHttpRequestError",
   651         /** @id MochiKit.Async.XMLHttpRequestError */
   652         function (req, msg) {
   653             /***
   655             Raised when an XMLHttpRequest does not complete for any reason.
   657             ***/
   658             this.req = req;
   659             this.message = msg;
   660             try {
   661                 // Strange but true that this can raise in some cases.
   662                 this.number = req.status;
   663             } catch (e) {
   664                 // pass
   665             }
   666         }
   667     );
   670     this.EXPORT_TAGS = {
   671         ":common": this.EXPORT,
   672         ":all": m.concat(this.EXPORT, this.EXPORT_OK)
   673     };
   675     m.nameFunctions(this);
   677 };
   679 MochiKit.Async.__new__();
   681 MochiKit.Base._exportSymbols(this, MochiKit.Async);

mercurial