]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojox/cometd/_base.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dojox / cometd / _base.js
1 if(!dojo._hasResource["dojox.cometd._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojox.cometd._base"] = true;
3 dojo.provide("dojox.cometd._base");
4 dojo.require("dojo.AdapterRegistry");
5 dojo.require("dojo.io.script");
6
7
8 /*
9  * this file defines Comet protocol client. Actual message transport is
10  * deferred to one of several connection type implementations. The default is a
11  * long-polling implementation. A single global object named "dojox.cometd" is
12  * used to mediate for these connection types in order to provide a stable
13  * interface.
14  *
15  * extensions modules may be loaded (eg "dojox.cometd.timestamp", that use
16  * the cometd._extendInList and cometd._extendOutList fields to provide functions
17  * that extend and handling incoming and outgoing messages.
18  */
19
20 dojox.cometd = new function(){
21         
22         // cometd states:
23
24         // alex; OMG, these "constants" need to die. Java truly is a degenerative disease.
25         this.DISCONNECTED = "DISCONNECTED";             // _initialized==false  && _connected==false
26         this.CONNECTING = "CONNECTING";                 // _initialized==true   && _connected==false (handshake sent)
27         this.CONNECTED = "CONNECTED";                   // _initialized==true   && _connected==true (first successful connect)
28         this.DISCONNECTING = "DISCONNECING";    // _initialized==false  && _connected==true (disconnect sent)
29         
30         this._initialized = false;
31         this._connected = false;
32         this._polling = false;
33
34         this.expectedNetworkDelay = 5000; // expected max network delay
35         this.connectTimeout = 0;    // If set, used as ms to wait for a connect response and sent as the advised timeout
36
37         this.connectionTypes = new dojo.AdapterRegistry(true);
38
39         this.version =  "1.0";
40         this.minimumVersion = "0.9";
41         this.clientId = null;
42         this.messageId = 0;
43         this.batch = 0;
44
45         this._isXD = false;
46         this.handshakeReturn = null;
47         this.currentTransport = null;
48         this.url = null;
49         this.lastMessage = null;
50         this._messageQ = [];
51         this.handleAs = "json-comment-optional";
52         this._advice = {};
53         this._backoffInterval = 0;
54         this._backoffIncrement = 1000;
55         this._backoffMax = 60000;
56         this._deferredSubscribes = {};
57         this._deferredUnsubscribes = {};
58         this._subscriptions = [];
59         this._extendInList = [];        // List of functions invoked before delivering messages
60         this._extendOutList = [];       // List of functions invoked before sending messages
61
62         this.state = function() {
63                 return this._initialized ? 
64                         (this._connected ? "CONNECTED" : "CONNECTING") : 
65                         ( this._connected ? "DISCONNECTING" : "DISCONNECTED");
66         }
67
68         this.init = function(   /*String*/      root,
69                                 /*Object?*/ props,
70                                 /*Object?*/ bargs){     // return: dojo.Deferred
71                 //      summary:
72                 //              Initialize the cometd implementation of the Bayeux protocol
73                 //      description:
74                 //              Initialize the cometd implementation of the Bayeux protocol by
75                 //              sending a handshake message. The cometd state will be changed to CONNECTING
76                 //              until a handshake response is received and the first successful connect message
77                 //              has returned.
78                 //              The protocol state changes may be monitored
79                 //              by subscribing to the dojo topic "/cometd/meta" where events are
80                 //              published in the form {cometd:this,action:"handshake",successful:true,state:this.state()}
81                 //      root:
82                 //              The URL of the cometd server. If the root is absolute, the host
83                 //              is examined to determine if xd transport is needed. Otherwise the
84                 //              same domain is assumed.
85                 //      props:
86                 //              An optional object that is used as the basis of the handshake message
87                 //      bargs:
88                 //              An optional object of bind args mixed in with the send of the handshake
89                 //      example:
90                 //      |       dojox.cometd.init("/cometd");
91                 //      |       dojox.cometd.init("http://xdHost/cometd",{ext:{user:"fred",pwd:"secret"}});
92
93
94                 // FIXME: if the root isn't from the same host, we should automatically
95                 // try to select an XD-capable transport
96                 props = props||{};
97                 // go ask the short bus server what we can support
98                 props.version = this.version;
99                 props.minimumVersion = this.minimumVersion;
100                 props.channel = "/meta/handshake";
101                 props.id = ""+this.messageId++;
102
103                 this.url = root||dojo.config["cometdRoot"];
104                 if(!this.url){
105                         console.debug("no cometd root specified in djConfig and no root passed");
106                         return null;
107                 }
108
109                 // Are we x-domain? borrowed from dojo.uri.Uri in lieu of fixed host and port properties
110                 var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
111                 var parts = (""+window.location).match(new RegExp(regexp));
112                 if(parts[4]){
113                         var tmp = parts[4].split(":");
114                         var thisHost = tmp[0];
115                         var thisPort = tmp[1]||"80"; // FIXME: match 443
116
117                         parts = this.url.match(new RegExp(regexp));
118                         if(parts[4]){
119                                 tmp = parts[4].split(":");
120                                 var urlHost = tmp[0];
121                                 var urlPort = tmp[1]||"80";
122                                 this._isXD = ((urlHost != thisHost)||(urlPort != thisPort));
123                         }
124                 }
125
126                 if(!this._isXD){
127                         if(props.ext){
128                                 if(props.ext["json-comment-filtered"]!==true && props.ext["json-comment-filtered"]!==false){
129                                         props.ext["json-comment-filtered"] = true;
130                                 }
131                         }else{
132                                 props.ext = { "json-comment-filtered": true };
133                         }
134                         props.supportedConnectionTypes = dojo.map(this.connectionTypes.pairs, "return item[0]");
135                 }
136
137                 props = this._extendOut(props);
138
139                 var bindArgs = {
140                         url: this.url,
141                         handleAs: this.handleAs,
142                         content: { "message": dojo.toJson([props]) },
143                         load: dojo.hitch(this,function(msg){
144                                 this._backon();
145                                 this._finishInit(msg);
146                         }),
147                         error: dojo.hitch(this,function(e){
148                                 console.debug("handshake error!:",e);
149                                 this._backoff();
150                                 this._finishInit([{}]);
151                         })
152                 };
153
154                 if(bargs){
155                         dojo.mixin(bindArgs, bargs);
156                 }
157                 this._props = props;
158                 for(var tname in this._subscriptions){
159                         for(var sub in this._subscriptions[tname]){
160                                 if(this._subscriptions[tname][sub].topic){
161                                         dojo.unsubscribe(this._subscriptions[tname][sub].topic);
162                                 }
163                         }
164                 }
165                 this._messageQ = [];
166                 this._subscriptions = [];
167                 this._initialized = true;
168                 this.batch = 0;
169                 this.startBatch();
170                 
171                 var r;
172                 // if xdomain, then we assume jsonp for handshake
173                 if(this._isXD){
174                         bindArgs.callbackParamName = "jsonp";
175                         r = dojo.io.script.get(bindArgs);
176                 }else{
177                         r = dojo.xhrPost(bindArgs);
178                 }
179                 dojo.publish("/cometd/meta", [
180                         {
181                                 cometd: this,
182                                 action: "handshake",
183                                 successful: true,
184                                 state: this.state()
185                         }
186                 ]);
187                 return r;
188         }
189         
190         
191         this.publish = function(/*String*/ channel, /*Object*/ data, /*Object?*/ props){
192                 // summary:
193                 //              publishes the passed message to the cometd server for delivery
194                 //              on the specified topic
195                 // channel:
196                 //              the destination channel for the message
197                 // data:
198                 //              a JSON object containing the message "payload"
199                 // properties:
200                 //              Optional. Other meta-data to be mixed into the top-level of the
201                 //              message
202                 var message = {
203                         data: data,
204                         channel: channel
205                 };
206                 if(props){
207                         dojo.mixin(message, props);
208                 }
209                 this._sendMessage(message);
210         }
211
212         
213         this.subscribe = function(      /*String */     channel,
214                                         /*Object */     objOrFunc,
215                                         /*String */     funcName,
216                                         /*Object?*/ props){ // return: dojo.Deferred
217                 //      summary:
218                 //              inform the server of this client's interest in channel
219                 //      description:
220                 //              `dojox.cometd.subscribe()` handles all the hard work of telling
221                 //              the server that we want to be notified when events are
222                 //              published on a particular topic. `subscribe` accepts a function
223                 //              to handle messages and returns a `dojo.Deferred` object which
224                 //              has an extra property added to it which makes it suitable for
225                 //              passing to `dojox.cometd.unsubscribe()` as a "subscription
226                 //              handle" (much like the handle object that `dojo.connect()`
227                 //              produces and which `dojo.disconnect()` expects).
228                 //              
229                 //              Note that of a subscription is registered before a connection
230                 //              with the server is established, events sent before the
231                 //              connection is established will not be delivered to this client.
232                 //              The deferred object which `subscribe` returns will callback
233                 //              when the server successfuly acknolwedges receipt of our
234                 //              "subscribe" request.
235                 //      channel:
236                 //              name of the cometd channel to subscribe to
237                 //      objOrFunc:
238                 //              an object scope for funcName or the name or reference to a
239                 //              function to be called when messages are delivered to the
240                 //              channel
241                 //      funcName:
242                 //              the second half of the objOrFunc/funcName pair for identifying
243                 //              a callback function to notifiy upon channel message delivery
244                 //      example:
245                 //              Simple subscribe use-case
246                 //      |       dojox.cometd.init("http://myserver.com:8080/cometd");
247                 //      |       // log out all incoming messages on /foo/bar
248                 //      |       dojox.cometd.subscribe("/foo/bar", console, "debug");
249                 //      example:
250                 //              Subscribe before connection is initialized
251                 //      |       dojox.cometd.subscribe("/foo/bar", console, "debug");
252                 //      |       dojox.cometd.init("http://myserver.com:8080/cometd");
253                 //      example:
254                 //              Subscribe an unsubscribe
255                 //      |       dojox.cometd.init("http://myserver.com:8080/cometd");
256                 //      |       var h = dojox.cometd.subscribe("/foo/bar", console, "debug");
257                 //      |       dojox.cometd.unsubscribe(h);
258                 //      example:
259                 //              Listen for successful subscription:
260                 //      |       dojox.cometd.init("http://myserver.com:8080/cometd");
261                 //      |       var h = dojox.cometd.subscribe("/foo/bar", console, "debug");
262                 //      |       h.addCallback(function(){
263                 //      |               console.debug("subscription to /foo/bar established");
264                 //      |       });
265
266                 props = props||{};
267                 if(objOrFunc){
268                         var tname = "/cometd"+channel;
269                         var subs = this._subscriptions[tname];
270                         if(!subs || subs.length==0){
271                                 subs = [];
272                                 props.channel = "/meta/subscribe";
273                                 props.subscription = channel;
274                                 this._sendMessage(props);
275                                 
276                                 var _ds = this._deferredSubscribes;
277                                 if(_ds[channel]){
278                                         _ds[channel].cancel();
279                                         delete _ds[channel];
280                                 }
281                                 _ds[channel] = new dojo.Deferred();
282                         }
283                         
284                         for(var i in subs){
285                                 if( subs[i].objOrFunc === objOrFunc && (!subs[i].funcName&&!funcName||subs[i].funcName==funcName) ){
286                                         return null;
287                                 }
288                         }
289                         
290                         var topic = dojo.subscribe(tname, objOrFunc, funcName);
291                         subs.push({ 
292                                 topic: topic, 
293                                 objOrFunc: objOrFunc, 
294                                 funcName: funcName
295                         });
296                         this._subscriptions[tname] = subs;
297                 }
298                 var ret = this._deferredSubscribes[channel]||{};
299                 ret.args = dojo._toArray(arguments);
300                 return ret; // dojo.Deferred
301         }
302
303
304
305         this.unsubscribe = function(    /*String*/      channel,
306                                         /*Object?*/ objOrFunc,
307                                         /*String?*/ funcName,
308                                         /*Object?*/ props){
309                 // summary:
310                 //              inform the server of this client's disinterest in channel
311                 // channel:
312                 //              name of the cometd channel to unsubscribe from
313                 // objOrFunc:
314                 //              an object scope for funcName or the name or reference to a
315                 //              function to be called when messages are delivered to the
316                 //              channel. If null then all subscribers to the channel are unsubscribed.
317                 // funcName:
318                 //              the second half of the objOrFunc/funcName pair for identifying
319                 //              a callback function to notifiy upon channel message delivery
320
321                 if(
322                         (arguments.length == 1) &&
323                         (!dojo.isString(channel)) &&
324                         (channel.args)
325                 ){
326                         // it's a subscription handle, unroll
327                         return this.unsubscribe.apply(this, channel.args);
328                 }
329                 
330                 var tname = "/cometd"+channel;
331                 var subs = this._subscriptions[tname];
332                 if(!subs || subs.length==0){
333                         return null;
334                 }
335
336                 var s=0;
337                 for(var i in subs){
338                         var sb = subs[i];
339                         if( (!objOrFunc) ||
340                                 (
341                                         sb.objOrFunc===objOrFunc &&
342                                         (!sb.funcName && !funcName || sb.funcName==funcName)
343                                 )
344                         ){
345                                 dojo.unsubscribe(subs[i].topic);
346                                 delete subs[i];
347                         }else{
348                                 s++;
349                         }
350                 }
351                 
352                 if(s==0){
353                         props = props||{};
354                         props.channel = "/meta/unsubscribe";
355                         props.subscription = channel;
356                         delete this._subscriptions[tname];
357                         this._sendMessage(props);
358                         this._deferredUnsubscribes[channel] = new dojo.Deferred();
359                         if (this._deferredSubscribes[channel]){
360                                 this._deferredSubscribes[channel].cancel();
361                                 delete this._deferredSubscribes[channel];
362                         }
363                 }
364                 return this._deferredUnsubscribes[channel]; // dojo.Deferred
365         }
366         
367         
368         this.disconnect = function(){
369                 //      summary:
370                 //              Disconnect from the server.
371                 //      description:
372                 //              Disconnect from the server by sending a disconnect message
373                 //      example:
374                 //      |       dojox.cometd.disconnect();
375
376                 for(var tname in this._subscriptions){
377                         for(var sub in this._subscriptions[tname]){
378                                 if(this._subscriptions[tname][sub].topic){
379                                         dojo.unsubscribe(this._subscriptions[tname][sub].topic);
380                                 }
381                         }
382                 }
383                 this._subscriptions = [];
384                 this._messageQ = [];
385                 if(this._initialized && this.currentTransport){
386                         this._initialized=false;
387                         this.currentTransport.disconnect();
388                 }
389                 if(!this._polling) {
390                         this._connected=false;
391                         dojo.publish("/cometd/meta", [{cometd:this,action:"connect",successful:false,state:this.state()}]);
392                 }
393                 this._initialized=false;
394                 dojo.publish("/cometd/meta", [{cometd:this,action:"disconnect",successful:true,state:this.state()}]);
395         }
396
397         
398         // public extension points
399         
400         this.subscribed = function(     /*String*/channel, /*Object*/message){ }
401
402         this.unsubscribed = function(/*String*/channel, /*Object*/message){ }
403
404
405         // private methods (TODO name all with leading _)
406
407         this.tunnelInit = function(childLocation, childDomain){
408                 // placeholder - replaced by _finishInit
409         }
410         
411         this.tunnelCollapse = function(){
412                 // placeholder - replaced by _finishInit
413         }
414         
415         this._backoff = function(){
416                 if(!this._advice){
417                         this._advice={reconnect:"retry",interval:0};
418                 }
419                 else if(!this._advice.interval){
420                         this._advice.interval=0;
421                 }
422                 if(this._backoffInterval<this._backoffMax){
423                         this._backoffInterval+=this._backoffIncrement;
424                 }
425         }
426         
427         this._backon = function(){
428                 this._backoffInterval=0;
429         }
430
431         this._interval = function(){
432                 var i=this._backoffInterval+(this._advice?(this._advice.interval?this._advice.interval:0):0);
433                 if (i>0)
434                         console.debug("Retry in interval+backoff="+this._advice.interval+"+"+this._backoffInterval+"="+i+"ms");
435                 return i;
436         }
437
438         this._finishInit = function(data){
439                 //      summary:
440                 //              Handle the handshake return from the server and initialize
441                 //              connection if all is OK
442                 data = data[0];
443                 this.handshakeReturn = data;
444                 
445                 // remember any advice
446                 if(data["advice"]){
447                         this._advice = data.advice;
448                 }
449
450                 var successful=data.successful?data.successful:false;
451                 
452                 // check version
453                 if(data.version < this.minimumVersion){
454                         console.debug("cometd protocol version mismatch. We wanted", this.minimumVersion, "but got", data.version);
455                         successful=false;
456                         this._advice.reconnect="none";
457                 }
458                 
459                 // If all OK
460                 if(successful){
461                         // pick a transport
462                         this.currentTransport = this.connectionTypes.match(
463                                 data.supportedConnectionTypes,
464                                 data.version,
465                                 this._isXD
466                         );
467                         // initialize the transport
468                         this.currentTransport._cometd = this;
469                         this.currentTransport.version = data.version;
470                         this.clientId = data.clientId;
471                         this.tunnelInit = dojo.hitch(this.currentTransport, "tunnelInit");
472                         this.tunnelCollapse = dojo.hitch(this.currentTransport, "tunnelCollapse");
473                         this.currentTransport.startup(data);
474                 }
475
476                 dojo.publish("/cometd/meta", [{cometd:this,action:"handshook",successful:successful,state:this.state()}]);
477
478                 // If there is a problem
479                 if(!successful){
480                         console.debug("cometd init failed");
481                         // follow advice
482                         if(this._advice && this._advice["reconnect"]=="none"){
483                                 console.debug("cometd reconnect: none");
484                         }else{
485                                 setTimeout(dojo.hitch(this, "init", this.url, this._props),this._interval());
486                         }
487                 }
488         }
489
490         // fixme: lots of repeated code...why?
491
492         this._extendIn = function(message){
493                 // Handle extensions for inbound messages
494                 dojo.forEach(dojox.cometd._extendInList, function(f){
495                         message = f(message)||message;
496                 });
497                 return message;
498         }
499
500         this._extendOut = function(message){
501                 // Handle extensions for inbound messages
502                 dojo.forEach(dojox.cometd._extendOutList, function(f){
503                         message = f(message)||message;
504                 });
505                 return message;
506         }
507
508
509         this.deliver = function(messages){
510                 dojo.forEach(messages, this._deliver, this);
511                 return messages;
512         }
513
514         this._deliver = function(message){
515                 // dipatch events along the specified path
516                 
517                 message=this._extendIn(message);
518
519                 if(!message["channel"]){
520                         if(message["success"] !== true){
521                                 console.debug("cometd error: no channel for message!", message);
522                                 return;
523                         }
524                 }
525                 this.lastMessage = message;
526
527                 if(message.advice){
528                         this._advice = message.advice; // TODO maybe merge?
529                 }
530
531                 // check to see if we got a /meta channel message that we care about
532                 var deferred=null;
533                 if(     (message["channel"]) &&
534                         (message.channel.length > 5)&&
535                         (message.channel.substr(0, 5) == "/meta")){
536                         // check for various meta topic actions that we need to respond to
537                         switch(message.channel){
538                                 case "/meta/connect":
539                                         if(message.successful && !this._connected){
540                                                 this._connected = this._initialized;
541                                                 this.endBatch();
542                                         }else if(!this._initialized){
543                                                 this._connected = false; // finish disconnect
544                                         }
545                                         dojo.publish("/cometd/meta",[{cometd:this,action:"connect",successful:message.successful,state:this.state()}]);
546                                         break;
547                                 case "/meta/subscribe":
548                                         deferred = this._deferredSubscribes[message.subscription];
549                                         if(!message.successful){
550                                                 if(deferred){
551                                                         deferred.errback(new Error(message.error));
552                                                 }
553                                                 this.currentTransport.cancelConnect();
554                                                 return;
555                                         }
556                                         dojox.cometd.subscribed(message.subscription, message);
557                                         if(deferred){
558                                                 deferred.callback(true);
559                                         }
560                                         break;
561                                 case "/meta/unsubscribe":
562                                         deferred = this._deferredUnsubscribes[message.subscription];
563                                         if(!message.successful){
564                                                 if(deferred){
565                                                         deferred.errback(new Error(message.error));
566                                                 }
567                                                 this.currentTransport.cancelConnect();
568                                                 return;
569                                         }
570                                         this.unsubscribed(message.subscription, message);
571                                         if(deferred){
572                                                 deferred.callback(true);
573                                         }
574                                         break;
575                                 default:
576                                         if(message.successful && !message.successful){
577                                                 this.currentTransport.cancelConnect();
578                                                 return;
579                                         }
580                         }
581                 }
582                 
583                 // send the message down for processing by the transport
584                 this.currentTransport.deliver(message);
585
586                 if(message.data){
587                         // dispatch the message to any locally subscribed listeners
588                         try {
589                                 var messages=[message];
590
591                                 // Determine target topic
592                                 var tname="/cometd"+message.channel;
593
594                                 // Deliver to globs that apply to target topic
595                                 var tnameParts=message.channel.split("/");
596                                 var tnameGlob="/cometd";
597                                 for (var i=1;i<tnameParts.length-1;i++) {
598                                         dojo.publish(tnameGlob+"/**",messages);
599                                         tnameGlob+="/"+tnameParts[i];
600                                 }
601                                 dojo.publish(tnameGlob+"/**",messages);
602                                 dojo.publish(tnameGlob+"/*",messages);
603         
604                                 // deliver to target topic
605                                 dojo.publish(tname,messages);
606                         }catch(e){
607                                 console.debug(e);
608                         }
609                 }
610         }
611
612         this._sendMessage = function(/* object */ message){
613                 // console.debug(this.currentTransport, this._connected, this.batch);
614                 // if(this.currentTransport && this._connected && !this.batch){
615                 if(this.currentTransport && !this.batch){
616                         return this.currentTransport.sendMessages([message]);
617                 }else{
618                         this._messageQ.push(message);
619                         return null;
620                 }
621         }
622
623         this.startBatch = function(){
624                 this.batch++;
625         }
626
627         this.endBatch = function(){
628                 if(--this.batch <= 0 && this.currentTransport && this._connected){
629                         this.batch=0;
630
631                         var messages=this._messageQ;
632                         this._messageQ=[];
633                         if(messages.length>0){
634                                 this.currentTransport.sendMessages(messages);
635                         }
636                 }
637         }
638         
639         this._onUnload = function(){
640                 // make this the last of the onUnload method
641                 dojo.addOnUnload(dojox.cometd,"disconnect");
642         }
643
644         this._connectTimeout = function(){
645                 // return the connect timeout in ms, calculated as the minimum of the advised timeout
646                 // and the configured timeout.  Else 0 to indicate no client side timeout
647                 var _advised=0;
648                 if (this._advice && this._advice.timeout && this.expectedNetworkDelay>0)
649                         _advised=this._advice.timeout + this.expectedNetworkDelay;
650                 
651                 if (this.connectTimeout>0 && this.connectTimeout<_advised)
652                         return this.connectTimeout;
653                 
654                 return 0;
655         }
656 }
657
658 /*
659 transport objects MUST expose the following methods:
660         - check
661         - startup
662         - sendMessages
663         - deliver
664         - disconnect
665 optional, standard but transport dependent methods are:
666         - tunnelCollapse
667         - tunnelInit
668
669 Transports SHOULD be namespaced under the cometd object and transports MUST
670 register themselves with cometd.connectionTypes
671
672 here's a stub transport defintion:
673
674 cometd.blahTransport = new function(){
675         this._connectionType="my-polling";
676         this._cometd=null;
677         this.lastTimestamp = null;
678
679         this.check = function(types, version, xdomain){
680                 // summary:
681                 //              determines whether or not this transport is suitable given a
682                 //              list of transport types that the server supports
683                 return dojo.lang.inArray(types, "blah");
684         }
685
686         this.startup = function(){
687                 if(dojox.cometd._polling){ return; }
688                 // FIXME: fill in startup routine here
689                 dojox.cometd._polling = true;
690         }
691
692         this.sendMessages = function(message){
693                 // FIXME: fill in message array sending logic
694         }
695
696         this.deliver = function(message){
697         }
698
699         this.disconnect = function(){
700                 // send orderly disconnect message
701         }
702
703         this.cancelConnect = function(){
704                 // cancel the current connection
705         }
706 }
707 cometd.connectionTypes.register("blah", cometd.blahTransport.check, cometd.blahTransport);
708 */
709
710 dojox.cometd.longPollTransport = new function(){
711         this._connectionType="long-polling";
712         this._cometd=null;
713
714         this.check = function(types, version, xdomain){
715                 return ((!xdomain)&&(dojo.indexOf(types, "long-polling") >= 0));
716         }
717
718         this.tunnelInit = function(){
719                 var message = {
720                         channel:        "/meta/connect",
721                         clientId:       this._cometd.clientId,
722                         connectionType: this._connectionType,
723                         id:     ""+this._cometd.messageId++
724                 };
725                 message=this._cometd._extendOut(message);
726                 this.openTunnelWith({message: dojo.toJson([message])});
727         }
728
729         this.tunnelCollapse = function(){
730                 // TODO handle transport specific advice
731                 
732                 if(!this._cometd._initialized){ return; }
733                         
734                 if(this._cometd._advice && this._cometd._advice["reconnect"]=="none"){
735                         console.debug("cometd reconnect: none");
736                         return;
737                 }
738                 setTimeout(dojo.hitch(this,function(){ this._connect(); }),this._cometd._interval());
739         }
740
741         this._connect = function(){
742                 if(!this._cometd._initialized){ return; }
743                 if(this._cometd._polling) {
744                         console.debug("wait for poll to complete or fail");
745                         return;
746                 }
747                         
748                 if((this._cometd._advice) && (this._cometd._advice["reconnect"]=="handshake")){
749                         this._cometd._connected=false;
750                         this._initialized = false;
751                         this._cometd.init(this._cometd.url,this._cometd._props);
752                 }else if(this._cometd._connected){
753                         var message={
754                                 channel:        "/meta/connect",
755                                 connectionType: this._connectionType,
756                                 clientId:       this._cometd.clientId,
757                                 id:     ""+this._cometd.messageId++
758                         };
759                         if (this._cometd.connectTimeout>this._cometd.expectedNetworkDelay)
760                                 message.advice={timeout:(this._cometd.connectTimeout-this._cometd.expectedNetworkDelay)};
761                                             
762                         message=this._cometd._extendOut(message);
763                         this.openTunnelWith({message: dojo.toJson([message])});
764                 }
765         }
766
767         this.deliver = function(message){
768                 // Nothing to do
769         }
770
771         this.openTunnelWith = function(content, url){
772                 this._cometd._polling = true;
773                 var post = {
774                         url: (url||this._cometd.url),
775                         content: content,
776                         handleAs: this._cometd.handleAs,
777                         load: dojo.hitch(this, function(data){
778                                 this._cometd._polling=false;
779                                 this._cometd.deliver(data);
780                                 this._cometd._backon();
781                                 this.tunnelCollapse();
782                         }),
783                         error: dojo.hitch(this, function(err){
784                                 this._cometd._polling=false;
785                                 console.debug("tunnel opening failed:", err);
786                                 dojo.publish("/cometd/meta", [{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);
787                                 this._cometd._backoff();
788                                 this.tunnelCollapse();
789                         })
790                 };
791
792                 var connectTimeout=this._cometd._connectTimeout();
793                 if (connectTimeout>0)
794                         post.timeout=connectTimeout;
795
796                 this._poll = dojo.xhrPost(post);
797         }
798
799         this.sendMessages = function(messages){
800                 for(var i=0; i<messages.length; i++){
801                         messages[i].clientId = this._cometd.clientId;
802                         messages[i].id = ""+this._cometd.messageId++;
803                         messages[i]=this._cometd._extendOut(messages[i]);
804                 }
805                 return dojo.xhrPost({
806                         url: this._cometd.url||dojo.config["cometdRoot"],
807                         handleAs: this._cometd.handleAs,
808                         load: dojo.hitch(this._cometd, "deliver"),
809                         error: dojo.hitch(this, function(err){
810                                 console.debug('dropped messages: ',messages);
811                         }),
812                         content: {
813                                 message: dojo.toJson(messages)
814                         }
815                 });
816         }
817
818         this.startup = function(handshakeData){
819                 if(this._cometd._connected){ return; }
820                 this.tunnelInit();
821         }
822
823         this.disconnect = function(){
824                 var message={
825                         channel:        "/meta/disconnect",
826                         clientId:       this._cometd.clientId,
827                         id:     ""+this._cometd.messageId++
828                 };
829                 message=this._cometd._extendOut(message);
830                 dojo.xhrPost({
831                         url: this._cometd.url||dojo.config["cometdRoot"],
832                         handleAs: this._cometd.handleAs,
833                         content: {
834                                 message: dojo.toJson([message])
835                         }
836                 });
837         }
838
839         this.cancelConnect = function(){
840                 if (this._poll) {
841                         this._poll.cancel();
842                         this._cometd._polling=false;
843                         dojo.debug("tunnel opening cancelled");
844                         dojo.event.topic.publish("/cometd/meta", {cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state(),cancel:true});
845                         this._cometd._backoff();
846                         this.disconnect();
847                         this.tunnelCollapse();
848                 }
849         }
850
851 }
852
853 dojox.cometd.callbackPollTransport = new function(){
854         this._connectionType = "callback-polling";
855         this._cometd = null;
856
857         this.check = function(types, version, xdomain){
858                 // we handle x-domain!
859                 return (dojo.indexOf(types, "callback-polling") >= 0);
860         }
861
862         this.tunnelInit = function(){
863                 var message = {
864                         channel:        "/meta/connect",
865                         clientId:       this._cometd.clientId,
866                         connectionType: this._connectionType,
867                         id:     ""+this._cometd.messageId++
868                 };
869                 message = this._cometd._extendOut(message);             
870                 this.openTunnelWith({
871                         message: dojo.toJson([message])
872                 });
873         }
874
875         this.tunnelCollapse = dojox.cometd.longPollTransport.tunnelCollapse;
876         this._connect = dojox.cometd.longPollTransport._connect;
877         this.deliver = dojox.cometd.longPollTransport.deliver;
878
879         this.openTunnelWith = function(content, url){
880                 this._cometd._polling = true;
881                 var script = {
882                         load: dojo.hitch(this, function(data){
883                                 this._cometd._polling=false;
884                                 this._cometd.deliver(data);
885                                 this._cometd._backon();
886                                 this.tunnelCollapse();
887                         }),
888                         error: dojo.hitch(this, function(err){
889                                 this._cometd._polling=false;
890                                 console.debug("tunnel opening failed:", err);
891                                 dojo.publish("/cometd/meta", [{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);
892                                 this._cometd._backoff();
893                                 this.tunnelCollapse();
894                         }),
895                         url: (url||this._cometd.url),
896                         content: content,
897                         callbackParamName: "jsonp"
898                 };
899                 var connectTimeout=this._cometd._connectTimeout();
900                 if (connectTimeout>0)
901                         script.timeout=connectTimeout;
902                 dojo.io.script.get(script);
903         }
904
905         this.sendMessages = function(/*array*/ messages){
906                 for(var i=0; i<messages.length; i++){
907                         messages[i].clientId = this._cometd.clientId;
908                         messages[i].id = ""+this._cometd.messageId++;
909                         messages[i]=this._cometd._extendOut(messages[i]);
910                 }
911                 var bindArgs = {
912                         url: this._cometd.url||dojo.config["cometdRoot"],
913                         load: dojo.hitch(this._cometd, "deliver"),
914                         callbackParamName: "jsonp",
915                         content: { message: dojo.toJson( messages ) }
916                 };
917                 return dojo.io.script.get(bindArgs);
918         }
919
920         this.startup = function(handshakeData){
921                 if(this._cometd._connected){ return; }
922                 this.tunnelInit();
923         }
924
925         this.disconnect = dojox.cometd.longPollTransport.disconnect;
926         
927         this.disconnect = function(){
928                 var message={
929                         channel:"/meta/disconnect",
930                         clientId:this._cometd.clientId,
931                         id:""+this._cometd.messageId++
932                 };
933                 message=this._cometd._extendOut(message);               
934                 dojo.io.script.get({
935                         url: this._cometd.url||dojo.config["cometdRoot"],
936                         callbackParamName: "jsonp",
937                         content: {
938                                 message: dojo.toJson([message])
939                         }
940                 });
941         }
942
943         this.cancelConnect = function(){}
944 }
945 dojox.cometd.connectionTypes.register("long-polling", dojox.cometd.longPollTransport.check, dojox.cometd.longPollTransport);
946 dojox.cometd.connectionTypes.register("callback-polling", dojox.cometd.callbackPollTransport.check, dojox.cometd.callbackPollTransport);
947
948 dojo.addOnUnload(dojox.cometd,"_onUnload");
949
950 }