summaryrefslogtreecommitdiffstats
path: root/testing/mochitest/MochiKit/Async.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mochitest/MochiKit/Async.js')
-rw-r--r--testing/mochitest/MochiKit/Async.js681
1 files changed, 681 insertions, 0 deletions
diff --git a/testing/mochitest/MochiKit/Async.js b/testing/mochitest/MochiKit/Async.js
new file mode 100644
index 000000000..8ffaad259
--- /dev/null
+++ b/testing/mochitest/MochiKit/Async.js
@@ -0,0 +1,681 @@
+/***
+
+MochiKit.Async 1.4
+
+See <http://mochikit.com/> for documentation, downloads, license, etc.
+
+(c) 2005 Bob Ippolito. All rights Reserved.
+
+***/
+
+if (typeof(dojo) != 'undefined') {
+ dojo.provide("MochiKit.Async");
+ dojo.require("MochiKit.Base");
+}
+if (typeof(JSAN) != 'undefined') {
+ JSAN.use("MochiKit.Base", []);
+}
+
+try {
+ if (typeof(MochiKit.Base) == 'undefined') {
+ throw "";
+ }
+} catch (e) {
+ throw "MochiKit.Async depends on MochiKit.Base!";
+}
+
+if (typeof(MochiKit.Async) == 'undefined') {
+ MochiKit.Async = {};
+}
+
+MochiKit.Async.NAME = "MochiKit.Async";
+MochiKit.Async.VERSION = "1.4";
+MochiKit.Async.__repr__ = function () {
+ return "[" + this.NAME + " " + this.VERSION + "]";
+};
+MochiKit.Async.toString = function () {
+ return this.__repr__();
+};
+
+/** @id MochiKit.Async.Deferred */
+MochiKit.Async.Deferred = function (/* optional */ canceller) {
+ this.chain = [];
+ this.id = this._nextId();
+ this.fired = -1;
+ this.paused = 0;
+ this.results = [null, null];
+ this.canceller = canceller;
+ this.silentlyCancelled = false;
+ this.chained = false;
+};
+
+MochiKit.Async.Deferred.prototype = {
+ /** @id MochiKit.Async.Deferred.prototype.repr */
+ repr: function () {
+ var state;
+ if (this.fired == -1) {
+ state = 'unfired';
+ } else if (this.fired === 0) {
+ state = 'success';
+ } else {
+ state = 'error';
+ }
+ return 'Deferred(' + this.id + ', ' + state + ')';
+ },
+
+ toString: MochiKit.Base.forwardCall("repr"),
+
+ _nextId: MochiKit.Base.counter(),
+
+ /** @id MochiKit.Async.Deferred.prototype.cancel */
+ cancel: function () {
+ var self = MochiKit.Async;
+ if (this.fired == -1) {
+ if (this.canceller) {
+ this.canceller(this);
+ } else {
+ this.silentlyCancelled = true;
+ }
+ if (this.fired == -1) {
+ this.errback(new self.CancelledError(this));
+ }
+ } else if ((this.fired === 0) && (this.results[0] instanceof self.Deferred)) {
+ this.results[0].cancel();
+ }
+ },
+
+ _resback: function (res) {
+ /***
+
+ The primitive that means either callback or errback
+
+ ***/
+ this.fired = ((res instanceof Error) ? 1 : 0);
+ this.results[this.fired] = res;
+ this._fire();
+ },
+
+ _check: function () {
+ if (this.fired != -1) {
+ if (!this.silentlyCancelled) {
+ throw new MochiKit.Async.AlreadyCalledError(this);
+ }
+ this.silentlyCancelled = false;
+ return;
+ }
+ },
+
+ /** @id MochiKit.Async.Deferred.prototype.callback */
+ callback: function (res) {
+ this._check();
+ if (res instanceof MochiKit.Async.Deferred) {
+ throw new Error("Deferred instances can only be chained if they are the result of a callback");
+ }
+ this._resback(res);
+ },
+
+ /** @id MochiKit.Async.Deferred.prototype.errback */
+ errback: function (res) {
+ this._check();
+ var self = MochiKit.Async;
+ if (res instanceof self.Deferred) {
+ throw new Error("Deferred instances can only be chained if they are the result of a callback");
+ }
+ if (!(res instanceof Error)) {
+ res = new self.GenericError(res);
+ }
+ this._resback(res);
+ },
+
+ /** @id MochiKit.Async.Deferred.prototype.addBoth */
+ addBoth: function (fn) {
+ if (arguments.length > 1) {
+ fn = MochiKit.Base.partial.apply(null, arguments);
+ }
+ return this.addCallbacks(fn, fn);
+ },
+
+ /** @id MochiKit.Async.Deferred.prototype.addCallback */
+ addCallback: function (fn) {
+ if (arguments.length > 1) {
+ fn = MochiKit.Base.partial.apply(null, arguments);
+ }
+ return this.addCallbacks(fn, null);
+ },
+
+ /** @id MochiKit.Async.Deferred.prototype.addErrback */
+ addErrback: function (fn) {
+ if (arguments.length > 1) {
+ fn = MochiKit.Base.partial.apply(null, arguments);
+ }
+ return this.addCallbacks(null, fn);
+ },
+
+ /** @id MochiKit.Async.Deferred.prototype.addCallbacks */
+ addCallbacks: function (cb, eb) {
+ if (this.chained) {
+ throw new Error("Chained Deferreds can not be re-used");
+ }
+ this.chain.push([cb, eb]);
+ if (this.fired >= 0) {
+ this._fire();
+ }
+ return this;
+ },
+
+ _fire: function () {
+ /***
+
+ Used internally to exhaust the callback sequence when a result
+ is available.
+
+ ***/
+ var chain = this.chain;
+ var fired = this.fired;
+ var res = this.results[fired];
+ var self = this;
+ var cb = null;
+ while (chain.length > 0 && this.paused === 0) {
+ // Array
+ var pair = chain.shift();
+ var f = pair[fired];
+ if (f === null) {
+ continue;
+ }
+ try {
+ res = f(res);
+ fired = ((res instanceof Error) ? 1 : 0);
+ if (res instanceof MochiKit.Async.Deferred) {
+ cb = function (res) {
+ self._resback(res);
+ self.paused--;
+ if ((self.paused === 0) && (self.fired >= 0)) {
+ self._fire();
+ }
+ };
+ this.paused++;
+ }
+ } catch (err) {
+ fired = 1;
+ if (!(err instanceof Error)) {
+ err = new MochiKit.Async.GenericError(err);
+ }
+ res = err;
+ }
+ }
+ this.fired = fired;
+ this.results[fired] = res;
+ if (cb && this.paused) {
+ // this is for "tail recursion" in case the dependent deferred
+ // is already fired
+ res.addBoth(cb);
+ res.chained = true;
+ }
+ }
+};
+
+MochiKit.Base.update(MochiKit.Async, {
+ /** @id MochiKit.Async.evalJSONRequest */
+ evalJSONRequest: function (/* req */) {
+ return eval('(' + arguments[0].responseText + ')');
+ },
+
+ /** @id MochiKit.Async.succeed */
+ succeed: function (/* optional */result) {
+ var d = new MochiKit.Async.Deferred();
+ d.callback.apply(d, arguments);
+ return d;
+ },
+
+ /** @id MochiKit.Async.fail */
+ fail: function (/* optional */result) {
+ var d = new MochiKit.Async.Deferred();
+ d.errback.apply(d, arguments);
+ return d;
+ },
+
+ /** @id MochiKit.Async.getXMLHttpRequest */
+ getXMLHttpRequest: function () {
+ var self = arguments.callee;
+ if (!self.XMLHttpRequest) {
+ var tryThese = [
+ function () { return new XMLHttpRequest(); },
+ function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
+ function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
+ function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
+ function () {
+ throw new MochiKit.Async.BrowserComplianceError("Browser does not support XMLHttpRequest");
+ }
+ ];
+ for (var i = 0; i < tryThese.length; i++) {
+ var func = tryThese[i];
+ try {
+ self.XMLHttpRequest = func;
+ return func();
+ } catch (e) {
+ // pass
+ }
+ }
+ }
+ return self.XMLHttpRequest();
+ },
+
+ _xhr_onreadystatechange: function (d) {
+ // MochiKit.Logging.logDebug('this.readyState', this.readyState);
+ var m = MochiKit.Base;
+ if (this.readyState == 4) {
+ // IE SUCKS
+ try {
+ this.onreadystatechange = null;
+ } catch (e) {
+ try {
+ this.onreadystatechange = m.noop;
+ } catch (e) {
+ }
+ }
+ var status = null;
+ try {
+ status = this.status;
+ if (!status && m.isNotEmpty(this.responseText)) {
+ // 0 or undefined seems to mean cached or local
+ status = 304;
+ }
+ } catch (e) {
+ // pass
+ // MochiKit.Logging.logDebug('error getting status?', repr(items(e)));
+ }
+ // 200 is OK, 304 is NOT_MODIFIED
+ if (status == 200 || status == 304) { // OK
+ d.callback(this);
+ } else {
+ var err = new MochiKit.Async.XMLHttpRequestError(this, "Request failed");
+ if (err.number) {
+ // XXX: This seems to happen on page change
+ d.errback(err);
+ } else {
+ // XXX: this seems to happen when the server is unreachable
+ d.errback(err);
+ }
+ }
+ }
+ },
+
+ _xhr_canceller: function (req) {
+ // IE SUCKS
+ try {
+ req.onreadystatechange = null;
+ } catch (e) {
+ try {
+ req.onreadystatechange = MochiKit.Base.noop;
+ } catch (e) {
+ }
+ }
+ req.abort();
+ },
+
+
+ /** @id MochiKit.Async.sendXMLHttpRequest */
+ sendXMLHttpRequest: function (req, /* optional */ sendContent) {
+ if (typeof(sendContent) == "undefined" || sendContent === null) {
+ sendContent = "";
+ }
+
+ var m = MochiKit.Base;
+ var self = MochiKit.Async;
+ var d = new self.Deferred(m.partial(self._xhr_canceller, req));
+
+ try {
+ req.onreadystatechange = m.bind(self._xhr_onreadystatechange,
+ req, d);
+ req.send(sendContent);
+ } catch (e) {
+ try {
+ req.onreadystatechange = null;
+ } catch (ignore) {
+ // pass
+ }
+ d.errback(e);
+ }
+
+ return d;
+
+ },
+
+ /** @id MochiKit.Async.doXHR */
+ doXHR: function (url, opts) {
+ var m = MochiKit.Base;
+ opts = m.update({
+ method: 'GET',
+ sendContent: ''
+ /*
+ queryString: undefined,
+ username: undefined,
+ password: undefined,
+ headers: undefined,
+ mimeType: undefined
+ */
+ }, opts);
+ var self = MochiKit.Async;
+ var req = self.getXMLHttpRequest();
+ if (opts.queryString) {
+ var qs = m.queryString(opts.queryString);
+ if (qs) {
+ url += "?" + qs;
+ }
+ }
+ req.open(opts.method, url, true, opts.username, opts.password);
+ if (req.overrideMimeType && opts.mimeType) {
+ req.overrideMimeType(opts.mimeType);
+ }
+ if (opts.headers) {
+ var headers = opts.headers;
+ if (!m.isArrayLike(headers)) {
+ headers = m.items(headers);
+ }
+ for (var i = 0; i < headers.length; i++) {
+ var header = headers[i];
+ var name = header[0];
+ var value = header[1];
+ req.setRequestHeader(name, value);
+ }
+ }
+ return self.sendXMLHttpRequest(req, opts.sendContent);
+ },
+
+ _buildURL: function (url/*, ...*/) {
+ if (arguments.length > 1) {
+ var m = MochiKit.Base;
+ var qs = m.queryString.apply(null, m.extend(null, arguments, 1));
+ if (qs) {
+ return url + "?" + qs;
+ }
+ }
+ return url;
+ },
+
+ /** @id MochiKit.Async.doSimpleXMLHttpRequest */
+ doSimpleXMLHttpRequest: function (url/*, ...*/) {
+ var self = MochiKit.Async;
+ url = self._buildURL.apply(self, arguments);
+ return self.doXHR(url);
+ },
+
+ /** @id MochiKit.Async.loadJSONDoc */
+ loadJSONDoc: function (url/*, ...*/) {
+ var self = MochiKit.Async;
+ url = self._buildURL.apply(self, arguments);
+ var d = self.doXHR(url, {
+ 'mimeType': 'text/plain',
+ 'headers': [['Accept', 'application/json']]
+ });
+ d = d.addCallback(self.evalJSONRequest);
+ return d;
+ },
+
+ /** @id MochiKit.Async.wait */
+ wait: function (seconds, /* optional */value) {
+ var d = new MochiKit.Async.Deferred();
+ var m = MochiKit.Base;
+ if (typeof(value) != 'undefined') {
+ d.addCallback(function () { return value; });
+ }
+ var timeout = setTimeout(
+ m.bind("callback", d),
+ Math.floor(seconds * 1000));
+ d.canceller = function () {
+ try {
+ clearTimeout(timeout);
+ } catch (e) {
+ // pass
+ }
+ };
+ return d;
+ },
+
+ /** @id MochiKit.Async.callLater */
+ callLater: function (seconds, func) {
+ var m = MochiKit.Base;
+ var pfunc = m.partial.apply(m, m.extend(null, arguments, 1));
+ return MochiKit.Async.wait(seconds).addCallback(
+ function (res) { return pfunc(); }
+ );
+ }
+});
+
+
+/** @id MochiKit.Async.DeferredLock */
+MochiKit.Async.DeferredLock = function () {
+ this.waiting = [];
+ this.locked = false;
+ this.id = this._nextId();
+};
+
+MochiKit.Async.DeferredLock.prototype = {
+ __class__: MochiKit.Async.DeferredLock,
+ /** @id MochiKit.Async.DeferredLock.prototype.acquire */
+ acquire: function () {
+ var d = new MochiKit.Async.Deferred();
+ if (this.locked) {
+ this.waiting.push(d);
+ } else {
+ this.locked = true;
+ d.callback(this);
+ }
+ return d;
+ },
+ /** @id MochiKit.Async.DeferredLock.prototype.release */
+ release: function () {
+ if (!this.locked) {
+ throw TypeError("Tried to release an unlocked DeferredLock");
+ }
+ this.locked = false;
+ if (this.waiting.length > 0) {
+ this.locked = true;
+ this.waiting.shift().callback(this);
+ }
+ },
+ _nextId: MochiKit.Base.counter(),
+ repr: function () {
+ var state;
+ if (this.locked) {
+ state = 'locked, ' + this.waiting.length + ' waiting';
+ } else {
+ state = 'unlocked';
+ }
+ return 'DeferredLock(' + this.id + ', ' + state + ')';
+ },
+ toString: MochiKit.Base.forwardCall("repr")
+
+};
+
+/** @id MochiKit.Async.DeferredList */
+MochiKit.Async.DeferredList = function (list, /* optional */fireOnOneCallback, fireOnOneErrback, consumeErrors, canceller) {
+
+ // call parent constructor
+ MochiKit.Async.Deferred.apply(this, [canceller]);
+
+ this.list = list;
+ var resultList = [];
+ this.resultList = resultList;
+
+ this.finishedCount = 0;
+ this.fireOnOneCallback = fireOnOneCallback;
+ this.fireOnOneErrback = fireOnOneErrback;
+ this.consumeErrors = consumeErrors;
+
+ var cb = MochiKit.Base.bind(this._cbDeferred, this);
+ for (var i = 0; i < list.length; i++) {
+ var d = list[i];
+ resultList.push(undefined);
+ d.addCallback(cb, i, true);
+ d.addErrback(cb, i, false);
+ }
+
+ if (list.length === 0 && !fireOnOneCallback) {
+ this.callback(this.resultList);
+ }
+
+};
+
+MochiKit.Async.DeferredList.prototype = new MochiKit.Async.Deferred();
+
+MochiKit.Async.DeferredList.prototype._cbDeferred = function (index, succeeded, result) {
+ this.resultList[index] = [succeeded, result];
+ this.finishedCount += 1;
+ if (this.fired == -1) {
+ if (succeeded && this.fireOnOneCallback) {
+ this.callback([index, result]);
+ } else if (!succeeded && this.fireOnOneErrback) {
+ this.errback(result);
+ } else if (this.finishedCount == this.list.length) {
+ this.callback(this.resultList);
+ }
+ }
+ if (!succeeded && this.consumeErrors) {
+ result = null;
+ }
+ return result;
+};
+
+/** @id MochiKit.Async.gatherResults */
+MochiKit.Async.gatherResults = function (deferredList) {
+ var d = new MochiKit.Async.DeferredList(deferredList, false, true, false);
+ d.addCallback(function (results) {
+ var ret = [];
+ for (var i = 0; i < results.length; i++) {
+ ret.push(results[i][1]);
+ }
+ return ret;
+ });
+ return d;
+};
+
+/** @id MochiKit.Async.maybeDeferred */
+MochiKit.Async.maybeDeferred = function (func) {
+ var self = MochiKit.Async;
+ var result;
+ try {
+ var r = func.apply(null, MochiKit.Base.extend([], arguments, 1));
+ if (r instanceof self.Deferred) {
+ result = r;
+ } else if (r instanceof Error) {
+ result = self.fail(r);
+ } else {
+ result = self.succeed(r);
+ }
+ } catch (e) {
+ result = self.fail(e);
+ }
+ return result;
+};
+
+
+MochiKit.Async.EXPORT = [
+ "AlreadyCalledError",
+ "CancelledError",
+ "BrowserComplianceError",
+ "GenericError",
+ "XMLHttpRequestError",
+ "Deferred",
+ "succeed",
+ "fail",
+ "getXMLHttpRequest",
+ "doSimpleXMLHttpRequest",
+ "loadJSONDoc",
+ "wait",
+ "callLater",
+ "sendXMLHttpRequest",
+ "DeferredLock",
+ "DeferredList",
+ "gatherResults",
+ "maybeDeferred",
+ "doXHR"
+];
+
+MochiKit.Async.EXPORT_OK = [
+ "evalJSONRequest"
+];
+
+MochiKit.Async.__new__ = function () {
+ var m = MochiKit.Base;
+ var ne = m.partial(m._newNamedError, this);
+
+ ne("AlreadyCalledError",
+ /** @id MochiKit.Async.AlreadyCalledError */
+ function (deferred) {
+ /***
+
+ Raised by the Deferred if callback or errback happens
+ after it was already fired.
+
+ ***/
+ this.deferred = deferred;
+ }
+ );
+
+ ne("CancelledError",
+ /** @id MochiKit.Async.CancelledError */
+ function (deferred) {
+ /***
+
+ Raised by the Deferred cancellation mechanism.
+
+ ***/
+ this.deferred = deferred;
+ }
+ );
+
+ ne("BrowserComplianceError",
+ /** @id MochiKit.Async.BrowserComplianceError */
+ function (msg) {
+ /***
+
+ Raised when the JavaScript runtime is not capable of performing
+ the given function. Technically, this should really never be
+ raised because a non-conforming JavaScript runtime probably
+ isn't going to support exceptions in the first place.
+
+ ***/
+ this.message = msg;
+ }
+ );
+
+ ne("GenericError",
+ /** @id MochiKit.Async.GenericError */
+ function (msg) {
+ this.message = msg;
+ }
+ );
+
+ ne("XMLHttpRequestError",
+ /** @id MochiKit.Async.XMLHttpRequestError */
+ function (req, msg) {
+ /***
+
+ Raised when an XMLHttpRequest does not complete for any reason.
+
+ ***/
+ this.req = req;
+ this.message = msg;
+ try {
+ // Strange but true that this can raise in some cases.
+ this.number = req.status;
+ } catch (e) {
+ // pass
+ }
+ }
+ );
+
+
+ this.EXPORT_TAGS = {
+ ":common": this.EXPORT,
+ ":all": m.concat(this.EXPORT, this.EXPORT_OK)
+ };
+
+ m.nameFunctions(this);
+
+};
+
+MochiKit.Async.__new__();
+
+MochiKit.Base._exportSymbols(this, MochiKit.Async);