JSJaCConnection.js
Summary
Contains all things in common for all subtypes of connections
supported.
Author: Stefan Strigler steve@zeank.in-berlin.de
Version: $Revision$
Class Summary
|
JSJaCConnection |
Somewhat abstract base class for jabber connections. |
function JSJaCConnection(oArg) {
if (oArg && oArg.oDbg && oArg.oDbg.log) {
this.oDbg = oArg.oDbg;
} else {
this.oDbg = new Object();
this.oDbg.log = function() { };
}
if (oArg && oArg.timerval)
this.setPollInterval(oArg.timerval);
else
this.setPollInterval(JSJAC_TIMERVAL);
if (oArg && oArg.httpbase)
this._httpbase = oArg.httpbase;
if (oArg &&oArg.allow_plain)
this.allow_plain = oArg.allow_plain;
else
this.allow_plain = JSJAC_ALLOW_PLAIN;
if (oArg && oArg.cookie_prefix)
this._cookie_prefix = oArg.cookie_prefix;
else
this._cookie_prefix = "";
this._connected = false;
this._events = new Array();
this._keys = null;
this._ID = 0;
this._inQ = new Array();
this._pQueue = new Array();
this._regIDs = new Array();
this._req = new Array();
this._status = 'intialized';
this._errcnt = 0;
this._inactivity = JSJAC_INACTIVITY;
this._sendRawCallbacks = new Array();
}
JSJaCConnection.prototype.connect = function(oArg) {
this._setStatus('connecting');
this.domain = oArg.domain || 'localhost';
this.username = oArg.username;
this.resource = oArg.resource;
this.pass = oArg.pass;
this.register = oArg.register;
this.authhost = oArg.authhost || this.domain;
this.authtype = oArg.authtype || 'sasl';
if (oArg.xmllang && oArg.xmllang != '')
this._xmllang = oArg.xmllang;
this.host = oArg.host || this.domain;
this.port = oArg.port || 5222;
if (oArg.secure)
this.secure = 'true';
else
this.secure = 'false';
if (oArg.wait)
this._wait = oArg.wait;
this.jid = this.username + '@' + this.domain;
this.fulljid = this.jid + '/' + this.resource;
this._rid = Math.round( 100000.5 + ( ( (900000.49999) - (100000.5) ) * Math.random() ) );
var slot = this._getFreeSlot();
this._req[slot] = this._setupRequest(true);
var reqstr = this._getInitialRequestString();
this.oDbg.log(reqstr,4);
this._req[slot].r.onreadystatechange =
JSJaC.bind(function() {
if (this._req[slot].r.readyState == 4) {
this.oDbg.log("async recv: "+this._req[slot].r.responseText,4);
this._handleInitialResponse(slot);
}
}, this);
if (typeof(this._req[slot].r.onerror) != 'undefined') {
this._req[slot].r.onerror =
JSJaC.bind(function(e) {
this.oDbg.log('XmlHttpRequest error',1);
return false;
}, this);
}
this._req[slot].r.send(reqstr);
};
JSJaCConnection.prototype.connected = function() { return this._connected; };
JSJaCConnection.prototype.disconnect = function() {
this._setStatus('disconnecting');
if (!this.connected())
return;
this._connected = false;
clearInterval(this._interval);
clearInterval(this._inQto);
if (this._timeout)
clearTimeout(this._timeout);
var slot = this._getFreeSlot();
this._req[slot] = this._setupRequest(false);
request = this._getRequestString(false, true);
this.oDbg.log("Disconnecting: " + request,4);
this._req[slot].r.send(request);
try {
JSJaCCookie.read(this._cookie_prefix+'JSJaC_State').erase();
} catch (e) {}
this.oDbg.log("Disconnected: "+this._req[slot].r.responseText,2);
this._handleEvent('ondisconnect');
};
JSJaCConnection.prototype.getPollInterval = function() {
return this._timerval;
};
JSJaCConnection.prototype.registerHandler = function(event) {
event = event.toLowerCase();
var eArg = {handler: arguments[arguments.length-1],
childName: '*',
childNS: '*',
type: '*'};
if (arguments.length > 2)
eArg.childName = arguments[1];
if (arguments.length > 3)
eArg.childNS = arguments[2];
if (arguments.length > 4)
eArg.type = arguments[3];
if (!this._events[event])
this._events[event] = new Array(eArg);
else
this._events[event] = this._events[event].concat(eArg);
this._events[event] =
this._events[event].sort(function(a,b) {
var aRank = 0;
var bRank = 0;
with (a) {
if (type == '*')
aRank++;
if (childNS == '*')
aRank++;
if (childName == '*')
aRank++;
}
with (b) {
if (type == '*')
bRank++;
if (childNS == '*')
bRank++;
if (childName == '*')
bRank++;
}
if (aRank > bRank)
return 1;
if (aRank < bRank)
return -1;
return 0;
});
this.oDbg.log("registered handler for event '"+event+"'",2);
};
JSJaCConnection.prototype.unregisterHandler = function(event,handler) {
event = event.toLowerCase();
if (!this._events[event])
return;
var arr = this._events[event], res = new Array();
for (var i=0; i<arr.length; i++)
if (arr[i].handler != handler)
res.push(arr[i]);
if (arr.length != res.length) {
this._events[event] = res;
this.oDbg.log("unregistered handler for event '"+event+"'",2);
}
};
JSJaCConnection.prototype.registerIQGet =
function(childName, childNS, handler) {
this.registerHandler('iq', childName, childNS, 'get', handler);
};
JSJaCConnection.prototype.registerIQSet =
function(childName, childNS, handler) {
this.registerHandler('iq', childName, childNS, 'set', handler);
};
JSJaCConnection.prototype.resume = function() {
try {
var json = JSJaCCookie.read(this._cookie_prefix+'JSJaC_State').getValue();
this.oDbg.log('read cookie: '+json,2);
JSJaCCookie.read(this._cookie_prefix+'JSJaC_State').erase();
return this.resumeFromData(JSJaCJSON.parse(json));
} catch (e) {}
return false;
};
JSJaCConnection.prototype.resumeFromData = function(data) {
try {
this._setStatus('resuming');
for (var i in data)
if (data.hasOwnProperty(i))
this[i] = data[i];
if (this._keys) {
this._keys2 = new JSJaCKeys();
var u = this._keys2._getSuspendVars();
for (var i=0; i<u.length; i++)
this._keys2[u[i]] = this._keys[u[i]];
this._keys = this._keys2;
}
if (this._connected) {
this._handleEvent('onresume');
setTimeout(JSJaC.bind(this._resume, this),this.getPollInterval());
this._interval = setInterval(JSJaC.bind(this._checkQueue, this),
JSJAC_CHECKQUEUEINTERVAL);
this._inQto = setInterval(JSJaC.bind(this._checkInQ, this),
JSJAC_CHECKINQUEUEINTERVAL);
}
return (this._connected === true);
} catch (e) {
if (e.message)
this.oDbg.log("Resume failed: "+e.message, 1);
else
this.oDbg.log("Resume failed: "+e, 1);
return false;
}
};
JSJaCConnection.prototype.send = function(packet,cb,arg) {
if (!packet || !packet.pType) {
this.oDbg.log("no packet: "+packet, 1);
return false;
}
if (!this.connected())
return false;
if (cb) {
if (!packet.getID())
packet.setID('JSJaCID_'+this._ID++);
this._registerPID(packet.getID(),cb,arg);
}
try {
this._handleEvent(packet.pType()+'_out', packet);
this._handleEvent("packet_out", packet);
this._pQueue = this._pQueue.concat(packet.xml());
} catch (e) {
this.oDbg.log(e.toString(),1);
return false;
}
return true;
};
JSJaCConnection.prototype.sendIQ = function(iq, handlers, arg) {
if (!iq || iq.pType() != 'iq') {
return false;
}
handlers = handlers || {};
var error_handler = handlers.error_handler || JSJaC.bind(function(aIq) {
this.oDbg.log(aIq.xml(), 1);
}, this);
var result_handler = handlers.result_handler || JSJaC.bind(function(aIq) {
this.oDbg.log(aIq.xml(), 2);
}, this);
var iqHandler = function(aIq, arg) {
switch (aIq.getType()) {
case 'error':
error_handler(aIq);
break;
case 'result':
result_handler(aIq, arg);
break;
}
};
return this.send(iq, iqHandler, arg);
};
JSJaCConnection.prototype.setPollInterval = function(timerval) {
if (timerval && !isNaN(timerval))
this._timerval = timerval;
return this._timerval;
};
JSJaCConnection.prototype.status = function() { return this._status; };
JSJaCConnection.prototype.suspend = function() {
var data = this.suspendToData();
try {
var c = new JSJaCCookie(this._cookie_prefix+'JSJaC_State', JSJaCJSON.toString(data));
this.oDbg.log("writing cookie: "+c.getValue()+"\n"+
"(length:"+c.getValue().length+")",2);
c.write();
var c2 = JSJaCCookie.get(this._cookie_prefix+'JSJaC_State');
if (c.getValue() != c2) {
this.oDbg.log("Suspend failed writing cookie.\nread: " + c2, 1);
c.erase();
return false;
}
return true;
} catch (e) {
this.oDbg.log("Failed creating cookie '"+this._cookie_prefix+
"JSJaC_State': "+e.message,1);
}
return false;
};
JSJaCConnection.prototype.suspendToData = function() {
clearTimeout(this._timeout);
clearInterval(this._interval);
clearInterval(this._inQto);
this._suspend();
var u = ('_connected,_keys,_ID,_inQ,_pQueue,_regIDs,_errcnt,_inactivity,domain,username,resource,jid,fulljid,_sid,_httpbase,_timerval,_is_polling').split(',');
u = u.concat(this._getSuspendVars());
var s = new Object();
for (var i=0; i<u.length; i++) {
if (!this[u[i]]) continue;
if (this[u[i]]._getSuspendVars) {
var uo = this[u[i]]._getSuspendVars();
var o = new Object();
for (var j=0; j<uo.length; j++)
o[uo[j]] = this[u[i]][uo[j]];
} else
var o = this[u[i]];
s[u[i]] = o;
}
this._connected = false;
this._setStatus('suspending');
return s;
};
JSJaCConnection.prototype._abort = function() {
clearTimeout(this._timeout);
clearInterval(this._inQto);
clearInterval(this._interval);
this._connected = false;
this._setStatus('aborted');
this.oDbg.log("Disconnected.",1);
this._handleEvent('ondisconnect');
this._handleEvent('onerror',
JSJaCError('500','cancel','service-unavailable'));
};
JSJaCConnection.prototype._checkInQ = function() {
for (var i=0; i<this._inQ.length && i<10; i++) {
var item = this._inQ[0];
this._inQ = this._inQ.slice(1,this._inQ.length);
var packet = JSJaCPacket.wrapNode(item);
if (!packet)
return;
this._handleEvent("packet_in", packet);
if (packet.pType && !this._handlePID(packet)) {
this._handleEvent(packet.pType()+'_in',packet);
this._handleEvent(packet.pType(),packet);
}
}
};
JSJaCConnection.prototype._checkQueue = function() {
if (this._pQueue.length != 0)
this._process();
return true;
};
JSJaCConnection.prototype._doAuth = function() {
if (this.has_sasl && this.authtype == 'nonsasl')
this.oDbg.log("Warning: SASL present but not used", 1);
if (!this._doSASLAuth() &&
!this._doLegacyAuth()) {
this.oDbg.log("Auth failed for authtype "+this.authtype,1);
this.disconnect();
return false;
}
return true;
};
JSJaCConnection.prototype._doInBandReg = function() {
if (this.authtype == 'saslanon' || this.authtype == 'anonymous')
return;
var iq = new JSJaCIQ();
iq.setType('set');
iq.setID('reg1');
iq.appendNode("query", {xmlns: "jabber:iq:register"},
[["username", this.username],
["password", this.pass]]);
this.send(iq,this._doInBandRegDone);
};
JSJaCConnection.prototype._doInBandRegDone = function(iq) {
if (iq && iq.getType() == 'error') {
this.oDbg.log("registration failed for "+this.username,0);
this._handleEvent('onerror',iq.getChild('error'));
return;
}
this.oDbg.log(this.username + " registered succesfully",0);
this._doAuth();
};
JSJaCConnection.prototype._doLegacyAuth = function() {
if (this.authtype != 'nonsasl' && this.authtype != 'anonymous')
return false;
var iq = new JSJaCIQ();
iq.setIQ(this.server,'get','auth1');
iq.appendNode('query', {xmlns: 'jabber:iq:auth'},
[['username', this.username]]);
this.send(iq,this._doLegacyAuth2);
return true;
};
JSJaCConnection.prototype._doLegacyAuth2 = function(iq) {
if (!iq || iq.getType() != 'result') {
if (iq && iq.getType() == 'error')
this._handleEvent('onerror',iq.getChild('error'));
this.disconnect();
return;
}
var use_digest = (iq.getChild('digest') != null);
var iq = new JSJaCIQ();
iq.setIQ(this.server,'set','auth2');
query = iq.appendNode('query', {xmlns: 'jabber:iq:auth'},
[['username', this.username],
['resource', this.resource]]);
if (use_digest) {
query.appendChild(iq.buildNode('digest', {xmlns: 'jabber:iq:auth'},
hex_sha1(this.streamid + this.pass)));
} else if (this.allow_plain) {
query.appendChild(iq.buildNode('password', {xmlns: 'jabber:iq:auth'},
this.pass));
} else {
this.oDbg.log("no valid login mechanism found",1);
this.disconnect();
return false;
}
this.send(iq,this._doLegacyAuthDone);
};
JSJaCConnection.prototype._doLegacyAuthDone = function(iq) {
if (iq.getType() != 'result') {
if (iq.getType() == 'error')
this._handleEvent('onerror',iq.getChild('error'));
this.disconnect();
} else
this._handleEvent('onconnect');
};
JSJaCConnection.prototype._doSASLAuth = function() {
if (this.authtype == 'nonsasl' || this.authtype == 'anonymous')
return false;
if (this.authtype == 'saslanon') {
if (this.mechs['ANONYMOUS']) {
this.oDbg.log("SASL using mechanism 'ANONYMOUS'",2);
return this._sendRaw("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>",
this._doSASLAuthDone);
}
this.oDbg.log("SASL ANONYMOUS requested but not supported",1);
} else {
if (this.mechs['DIGEST-MD5']) {
this.oDbg.log("SASL using mechanism 'DIGEST-MD5'",2);
return this._sendRaw("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>",
this._doSASLAuthDigestMd5S1);
} else if (this.allow_plain && this.mechs['PLAIN']) {
this.oDbg.log("SASL using mechanism 'PLAIN'",2);
var authStr = this.username+'@'+
this.domain+String.fromCharCode(0)+
this.username+String.fromCharCode(0)+
this.pass;
this.oDbg.log("authenticating with '"+authStr+"'",2);
authStr = btoa(authStr);
return this._sendRaw("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>"+authStr+"</auth>",
this._doSASLAuthDone);
}
this.oDbg.log("No SASL mechanism applied",1);
this.authtype = 'nonsasl';
}
return false;
};
JSJaCConnection.prototype._doSASLAuthDigestMd5S1 = function(el) {
if (el.nodeName != "challenge") {
this.oDbg.log("challenge missing",1);
this._handleEvent('onerror',JSJaCError('401','auth','not-authorized'));
this.disconnect();
} else {
var challenge = atob(el.firstChild.nodeValue);
this.oDbg.log("got challenge: "+challenge,2);
this._nonce = challenge.substring(challenge.indexOf("nonce=")+7);
this._nonce = this._nonce.substring(0,this._nonce.indexOf("\""));
this.oDbg.log("nonce: "+this._nonce,2);
if (this._nonce == '' || this._nonce.indexOf('\"') != -1) {
this.oDbg.log("nonce not valid, aborting",1);
this.disconnect();
return;
}
this._digest_uri = "xmpp/";
// if (typeof(this.host) != 'undefined' && this.host != '') {
// this._digest-uri += this.host;
// if (typeof(this.port) != 'undefined' && this.port)
// this._digest-uri += ":" + this.port;
// this._digest-uri += '/';
// }
this._digest_uri += this.domain;
this._cnonce = cnonce(14);
this._nc = '00000001';
var A1 = str_md5(this.username+':'+this.domain+':'+this.pass)+
':'+this._nonce+':'+this._cnonce;
var A2 = 'AUTHENTICATE:'+this._digest_uri;
var response = hex_md5(hex_md5(A1)+':'+this._nonce+':'+this._nc+':'+
this._cnonce+':auth:'+hex_md5(A2));
var rPlain = 'username="'+this.username+'",realm="'+this.domain+
'",nonce="'+this._nonce+'",cnonce="'+this._cnonce+'",nc="'+this._nc+
'",qop=auth,digest-uri="'+this._digest_uri+'",response="'+response+
'",charset="utf-8"';
this.oDbg.log("response: "+rPlain,2);
this._sendRaw("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"+
binb2b64(str2binb(rPlain))+"</response>",
this._doSASLAuthDigestMd5S2);
}
};
/**
* @private
*/
JSJaCConnection.prototype._doSASLAuthDigestMd5S2 = function(el) {
if (el.nodeName == 'failure') {
if (el.xml)
this.oDbg.log("auth error: "+el.xml,1);
else
this.oDbg.log("auth error",1);
this._handleEvent('onerror',JSJaCError('401','auth','not-authorized'));
this.disconnect();
return;
}
var response = atob(el.firstChild.nodeValue);
this.oDbg.log("response: "+response,2);
var rspauth = response.substring(response.indexOf("rspauth=")+8);
this.oDbg.log("rspauth: "+rspauth,2);
var A1 = str_md5(this.username+':'+this.domain+':'+this.pass)+
':'+this._nonce+':'+this._cnonce;
var A2 = ':'+this._digest_uri;
var rsptest = hex_md5(hex_md5(A1)+':'+this._nonce+':'+this._nc+':'+
this._cnonce+':auth:'+hex_md5(A2));
this.oDbg.log("rsptest: "+rsptest,2);
if (rsptest != rspauth) {
this.oDbg.log("SASL Digest-MD5: server repsonse with wrong rspauth",1);
this.disconnect();
return;
}
if (el.nodeName == 'success')
this._reInitStream(this.domain, this._doStreamBind);
else // some extra turn
this._sendRaw("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>",
this._doSASLAuthDone);
};
/**
* @private
*/
JSJaCConnection.prototype._doSASLAuthDone = function (el) {
if (el.nodeName != 'success') {
this.oDbg.log("auth failed",1);
this._handleEvent('onerror',JSJaCError('401','auth','not-authorized'));
this.disconnect();
} else
this._reInitStream(this.domain, this._doStreamBind);
};
/**
* @private
*/
JSJaCConnection.prototype._doStreamBind = function() {
var iq = new JSJaCIQ();
iq.setIQ(null,'set','bind_1');
iq.appendNode("bind", {xmlns: "urn:ietf:params:xml:ns:xmpp-bind"},
[["resource", this.resource]]);
this.oDbg.log(iq.xml());
this.send(iq,this._doXMPPSess);
};
/**
* @private
*/
JSJaCConnection.prototype._doXMPPSess = function(iq) {
if (iq.getType() != 'result' || iq.getType() == 'error') { // failed
this.disconnect();
if (iq.getType() == 'error')
this._handleEvent('onerror',iq.getChild('error'));
return;
}
this.fulljid = iq.getChildVal("jid");
this.jid = this.fulljid.substring(0,this.fulljid.lastIndexOf('/'));
iq = new JSJaCIQ();
iq.setIQ(this.domain,'set','sess_1');
iq.appendNode("session", {xmlns: "urn:ietf:params:xml:ns:xmpp-session"},
[]);
this.oDbg.log(iq.xml());
this.send(iq,this._doXMPPSessDone);
};
/**
* @private
*/
JSJaCConnection.prototype._doXMPPSessDone = function(iq) {
if (iq.getType() != 'result' || iq.getType() == 'error') { // failed
this.disconnect();
if (iq.getType() == 'error')
this._handleEvent('onerror',iq.getChild('error'));
return;
} else
this._handleEvent('onconnect');
};
/**
* @private
*/
JSJaCConnection.prototype._handleEvent = function(event,arg) {
event = event.toLowerCase(); // don't be case-sensitive here
this.oDbg.log("incoming event '"+event+"'",3);
if (!this._events[event])
return;
this.oDbg.log("handling event '"+event+"'",2);
for (var i=0;i<this._events[event].length; i++) {
var aEvent = this._events[event][i];
if (typeof aEvent.handler == 'function') {
try {
if (arg) {
if (arg.pType) {
if ((!arg.getNode().hasChildNodes() && aEvent.childName != '*') ||
(arg.getNode().hasChildNodes() &&
!arg.getChild(aEvent.childName, aEvent.childNS)))
continue;
if (aEvent.type != '*' &&
arg.getType() != aEvent.type)
continue;
this.oDbg.log(aEvent.childName+"/"+aEvent.childNS+"/"+aEvent.type+" => match for handler "+aEvent.handler,3);
}
if (aEvent.handler(arg)) {
break;
}
}
else
if (aEvent.handler()) {
break;
}
} catch (e) {
if (e.fileName&&e.lineNumber) {
this.oDbg.log(aEvent.handler+"\n>>>"+e.name+": "+ e.message+' in '+e.fileName+' line '+e.lineNumber,1);
} else {
this.oDbg.log(aEvent.handler+"\n>>>"+e.name+": "+ e.message,1);
}
}
}
}
};
JSJaCConnection.prototype._handlePID = function(aJSJaCPacket) {
if (!aJSJaCPacket.getID())
return false;
for (var i in this._regIDs) {
if (this._regIDs.hasOwnProperty(i) &&
this._regIDs[i] && i == aJSJaCPacket.getID()) {
var pID = aJSJaCPacket.getID();
this.oDbg.log("handling "+pID,3);
try {
if (this._regIDs[i].cb.call(this, aJSJaCPacket, this._regIDs[i].arg) === false) {
return false;
} else {
this._unregisterPID(pID);
return true;
}
} catch (e) {
this.oDbg.log(e.name+": "+ e.message, 1);
this._unregisterPID(pID);
return true;
}
}
}
return false;
};
JSJaCConnection.prototype._handleResponse = function(req) {
var rootEl = this._parseResponse(req);
if (!rootEl)
return;
for (var i=0; i<rootEl.childNodes.length; i++) {
if (this._sendRawCallbacks.length) {
var cb = this._sendRawCallbacks[0];
this._sendRawCallbacks = this._sendRawCallbacks.slice(1, this._sendRawCallbacks.length);
cb.fn.call(this, rootEl.childNodes.item(i), cb.arg);
continue;
}
this._inQ = this._inQ.concat(rootEl.childNodes.item(i));
}
};
JSJaCConnection.prototype._parseStreamFeatures = function(doc) {
if (!doc) {
this.oDbg.log("nothing to parse ... aborting",1);
return false;
}
var errorTag;
if (doc.getElementsByTagNameNS)
errorTag = doc.getElementsByTagNameNS("http://etherx.jabber.org/streams", "error").item(0);
else {
var errors = doc.getElementsByTagName("error");
for (var i=0; i<errors.length; i++)
if (errors.item(i).namespaceURI == "http://etherx.jabber.org/streams") {
errorTag = errors.item(i);
break;
}
}
if (errorTag) {
this._setStatus("internal_server_error");
clearTimeout(this._timeout);
clearInterval(this._interval);
clearInterval(this._inQto);
this._handleEvent('onerror',JSJaCError('503','cancel','session-terminate'));
this._connected = false;
this.oDbg.log("Disconnected.",1);
this._handleEvent('ondisconnect');
return false;
}
this.mechs = new Object();
var lMec1 = doc.getElementsByTagName("mechanisms");
this.has_sasl = false;
for (var i=0; i<lMec1.length; i++)
if (lMec1.item(i).getAttribute("xmlns") ==
"urn:ietf:params:xml:ns:xmpp-sasl") {
this.has_sasl=true;
var lMec2 = lMec1.item(i).getElementsByTagName("mechanism");
for (var j=0; j<lMec2.length; j++)
this.mechs[lMec2.item(j).firstChild.nodeValue] = true;
break;
}
if (this.has_sasl)
this.oDbg.log("SASL detected",2);
else {
this.oDbg.log("No support for SASL detected",2);
return false;
}
return true;
};
JSJaCConnection.prototype._process = function(timerval) {
if (!this.connected()) {
this.oDbg.log("Connection lost ...",1);
if (this._interval)
clearInterval(this._interval);
return;
}
this.setPollInterval(timerval);
if (this._timeout)
clearTimeout(this._timeout);
var slot = this._getFreeSlot();
if (slot < 0)
return;
if (typeof(this._req[slot]) != 'undefined' &&
typeof(this._req[slot].r) != 'undefined' &&
this._req[slot].r.readyState != 4) {
this.oDbg.log("Slot "+slot+" is not ready");
return;
}
if (!this.isPolling() && this._pQueue.length == 0 &&
this._req[(slot+1)%2] && this._req[(slot+1)%2].r.readyState != 4) {
this.oDbg.log("all slots busy, standby ...", 2);
return;
}
if (!this.isPolling())
this.oDbg.log("Found working slot at "+slot,2);
this._req[slot] = this._setupRequest(true);
this._req[slot].r.onreadystatechange =
JSJaC.bind(function() {
if (!this.connected())
return;
if (this._req[slot].r.readyState == 4) {
this._setStatus('processing');
this.oDbg.log("async recv: "+this._req[slot].r.responseText,4);
this._handleResponse(this._req[slot]);
if (this._pQueue.length) {
this._timeout = setTimeout(JSJaC.bind(this._process, this),100);
} else {
this.oDbg.log("scheduling next poll in "+this.getPollInterval()+
" msec", 4);
this._timeout = setTimeout(JSJaC.bind(this._process, this),this.getPollInterval());
}
}
}, this);
try {
this._req[slot].r.onerror =
JSJaC.bind(function() {
if (!this.connected())
return;
this._errcnt++;
this.oDbg.log('XmlHttpRequest error ('+this._errcnt+')',1);
if (this._errcnt > JSJAC_ERR_COUNT) {
this._abort();
return false;
}
this._setStatus('onerror_fallback');
setTimeout(JSJaC.bind(this._resume, this),this.getPollInterval());
return false;
}, this);
} catch(e) { }
var reqstr = this._getRequestString();
if (typeof(this._rid) != 'undefined')
this._req[slot].rid = this._rid;
this.oDbg.log("sending: " + reqstr,4);
this._req[slot].r.send(reqstr);
};
JSJaCConnection.prototype._registerPID = function(pID,cb,arg) {
if (!pID || !cb)
return false;
this._regIDs[pID] = new Object();
this._regIDs[pID].cb = cb;
if (arg)
this._regIDs[pID].arg = arg;
this.oDbg.log("registered "+pID,3);
return true;
};
JSJaCConnection.prototype._sendEmpty = function JSJaCSendEmpty() {
var slot = this._getFreeSlot();
this._req[slot] = this._setupRequest(true);
this._req[slot].r.onreadystatechange =
JSJaC.bind(function() {
if (this._req[slot].r.readyState == 4) {
this.oDbg.log("async recv: "+this._req[slot].r.responseText,4);
this._getStreamID(slot);
}
},this);
if (typeof(this._req[slot].r.onerror) != 'undefined') {
this._req[slot].r.onerror =
JSJaC.bind(function(e) {
this.oDbg.log('XmlHttpRequest error',1);
return false;
}, this);
}
var reqstr = this._getRequestString();
this.oDbg.log("sending: " + reqstr,4);
this._req[slot].r.send(reqstr);
};
JSJaCConnection.prototype._sendRaw = function(xml,cb,arg) {
if (cb)
this._sendRawCallbacks.push({fn: cb, arg: arg});
this._pQueue.push(xml);
this._process();
return true;
};
JSJaCConnection.prototype._setStatus = function(status) {
if (!status || status == '')
return;
if (status != this._status) {
this._status = status;
this._handleEvent('onstatuschanged', status);
this._handleEvent('status_changed', status);
}
};
JSJaCConnection.prototype._unregisterPID = function(pID) {
if (!this._regIDs[pID])
return false;
this._regIDs[pID] = null;
this.oDbg.log("unregistered "+pID,3);
return true;
};
Documentation generated by
JSDoc on Thu Nov 19 06:28:32 2020