(function($) { var msgHandshake = { version: '1.0', minimumVersion: '0.9', channel: '/meta/handshake' }; var oTransport = function() { this._bXD = (($.comet._sUrl.substring(0,4) == 'http') && ($.comet._sUrl.substr(7,location.href.length).replace(/\/.*/, '') != location.host)) ? true :false; this.connectionType = (this._bXD) ? 'callback-polling' : 'long-polling'; this.startup = function(oReturn) { if(this._comet._bConnected) return; this.tunnelInit(); }; this.tunnelInit = function() { var msgConnect = { channel: '/meta/connect', clientId: $.comet.clientId, id: String($.comet._nNextId++), connectionType: $.comet._oTransport.connectionType }; this.openTunnel(msgConnect); }; this.openTunnel = function(oMsg) { $.comet._bPolling = true; this._send($.comet._sUrl, oMsg, function(sReturn) { var oReturn = (typeof sReturn != "object") ? (eval('(' + sReturn + ')')) : sReturn; $.comet._bPolling = false; $.comet.deliver(oReturn); $.comet._oTransport.closeTunnel(); }); }; this.closeTunnel = function() { if(!$.comet._bInitialized) return; if($.comet._advice) { if($.comet._advice.reconnect == 'none') return; if($.comet._advice.interval > 0) { setTimeout($.comet._oTransport._connect, $.comet._advice.interval); } else { $.comet._oTransport._connect(); } } else { $.comet._oTransport._connect(); } }; this._connect = function() { if(!$.comet._bInitialized) return; if($.comet._bPolling) return; if($.comet._advice && $.comet._advice.reconnect == 'handshake') { $.comet._bConnected = false; $.comet.init($.comet._sUrl); } else if($.comet._bConnected) { var msgConnect = { //jsonp: 'test', clientId: $.comet.clientId, id: String($.comet._nNextId++), channel: '/meta/connect', connectionType: $.comet._oTransport.connectionType }; $.comet._oTransport.openTunnel(msgConnect); } }; this._send = function(sUrl, oMsg, fCallback) { //default callback will check advice, deliver messages, and reconnect var fCallback = (fCallback) ? fCallback : function(sReturn) { var oReturn = (typeof sReturn != "object") ? (eval('(' + sReturn + ')')) : sReturn; $.comet.deliver(oReturn); if($.comet._advice) { if($.comet._advice.reconnect == 'none') return; if($.comet._advice.interval > 0) { setTimeout($.comet._oTransport._connect, $.comet._advice.interval); } else { $.comet._oTransport._connect(); } } else { $.comet._oTransport._connect(); } }; //regular AJAX for same domain calls if((!this._bXD) && (this.connectionType == 'long-polling')) { this._pollRequest = $.ajax({ url: sUrl, type: 'post', beforeSend: function(oXhr) { oXhr.setRequestHeader('Connection', 'Keep-Alive'); }, data: { message: JSON.stringify(oMsg) }, success: fCallback }); } else // JSONP callback for cross domain { this._pollRequest = $.ajax({ url: sUrl, dataType: 'jsonp', jsonp: 'jsonp', beforeSend: function(oXhr) { oXhr.setRequestHeader('Connection', 'Keep-Alive'); }, data: { message: JSON.stringify($.extend(oMsg,{connectionType: 'callback-polling' })) }, success: fCallback }); } } }; $.comet = new function() { this.CONNECTED = 'CONNECTED'; this.CONNECTING = 'CONNECTING'; this.DISCONNECTED = 'DISCONNECTED'; this.DISCONNECTING = 'DISCONNECTING'; this._aMessageQueue = []; this._aSubscriptions = []; this._aSubscriptionCallbacks = []; this._bInitialized = false; this._bConnected = false; this._nBatch = 0; this._nNextId = 0; // just define the transport, do not assign it yet. this._oTransport = ''; //oTransport; this._sUrl = ''; this.supportedConectionTypes = [ 'long-polling', 'callback-polling' ]; this.clientId = ''; this._bTrigger = true; // this sends $.event.trigger(channel, data) this.init = function(sUrl) { this._sUrl = (sUrl) ? sUrl : '/cometd'; this._oTransport = new oTransport(); this._aMessageQueue = []; this._aSubscriptions = []; this._bInitialized = true; this.startBatch(); var oMsg = $.extend(msgHandshake, {id: String(this._nNextId++)}); this._oTransport._send(this._sUrl, oMsg, $.comet._finishInit); }; this._finishInit = function(sReturn) { var oReturn = (typeof sReturn != "object") ? (eval('(' + sReturn + ')')[0]) : sReturn[0]; if(oReturn.advice) $.comet._advice = oReturn.advice; var bSuccess = (oReturn.successful) ? oReturn.successful : false; // do version check if(bSuccess) { // pick transport ? // ...... $.comet._oTransport._comet = $.comet; $.comet._oTransport.version = $.comet.version; $.comet.clientId = oReturn.clientId; $.comet._oTransport.startup(oReturn); $.comet.endBatch(); } }; this._sendMessage = function(oMsg) { if($.comet._nBatch <= 0) { if(oMsg.length > 0) for(var i in oMsg) { oMsg[i].clientId = String($.comet.clientId); oMsg[i].id = String($.comet._nNextId++); } else { oMsg.clientId = String($.comet.clientId); oMsg.id = String($.comet._nNextId++); } $.comet._oTransport._send($.comet._sUrl, oMsg); } else { $.comet._aMessageQueue.push(oMsg); } }; this.startBatch = function() { this._nBatch++ }; this.endBatch = function() { if(--this._nBatch <= 0) { this._nBatch = 0; if(this._aMessageQueue.length > 0) { this._sendMessage(this._aMessageQueue); this._aMessageQueue = []; } } }; this.subscribe = function(sSubscription, fCallback) { // if this topic has not been subscribed to yet, send the message now if(!this._aSubscriptions[sSubscription]) { this._aSubscriptions.push(sSubscription) if (fCallback) { this._aSubscriptionCallbacks[sSubscription] = fCallback; } this._sendMessage({ channel: '/meta/subscribe', subscription: sSubscription }); } //$.event.add(window, sSubscription, fCallback); }; this.unsubscribe = function(sSubscription) { $.comet._sendMessage({ channel: '/meta/unsubscribe', subscription: sSubscription }); }; this.publish = function(sChannel, oData) { $.comet._sendMessage({channel: sChannel, data: oData}); }; this.deliver = function(sReturn) { var oReturn = sReturn;//eval(sReturn); $(oReturn).each(function() { $.comet._deliver(this); }); }; this.disconnect = function() { $($.comet._aSubscriptions).each(function(i) { $.comet.unsubscribe($.comet._aSubscriptions[i]); }); $.comet._sendMessage({channel:'/meta/disconnect'}); $.comet._bInitialized = false; } this._deliver = function(oMsg,oData) { if(oMsg.advice) { $.comet._advice = oMsg.advice; } switch(oMsg.channel) { case '/meta/connect': if(oMsg.successful && !$.comet._bConnected) { $.comet._bConnected = $.comet._bInitialized; $.comet.endBatch(); /* $.comet._sendMessage(msgConnect); */ } else {} //$.comet._bConnected = false; break; // add in subscription handling stuff case '/meta/subscribe': if(!oMsg.successful) { $.comet._oTransport._cancelConnect(); return; } break; case '/meta/unsubscribe': if(!oMsg.successful) { $.comet._oTransport._cancelConnect(); return; } break; } if(oMsg.data) { if($.comet._bTrigger) { $.event.trigger(oMsg.channel, [oMsg]); } var cb = $.comet._aSubscriptionCallbacks[oMsg.channel]; if (cb) { cb(oMsg); } } }; }; })(jQuery);