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");
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
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.
20 dojox.cometd = new function(){
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)
30 this._initialized = false;
31 this._connected = false;
32 this._polling = false;
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
37 this.connectionTypes = new dojo.AdapterRegistry(true);
40 this.minimumVersion = "0.9";
46 this.handshakeReturn = null;
47 this.currentTransport = null;
49 this.lastMessage = null;
51 this.handleAs = "json-comment-optional";
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
62 this.state = function() {
63 return this._initialized ?
64 (this._connected ? "CONNECTED" : "CONNECTING") :
65 ( this._connected ? "DISCONNECTING" : "DISCONNECTED");
68 this.init = function( /*String*/ root,
70 /*Object?*/ bargs){ // return: dojo.Deferred
72 // Initialize the cometd implementation of the Bayeux protocol
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
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()}
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.
86 // An optional object that is used as the basis of the handshake message
88 // An optional object of bind args mixed in with the send of the handshake
90 // | dojox.cometd.init("/cometd");
91 // | dojox.cometd.init("http://xdHost/cometd",{ext:{user:"fred",pwd:"secret"}});
94 // FIXME: if the root isn't from the same host, we should automatically
95 // try to select an XD-capable transport
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++;
103 this.url = root||dojo.config["cometdRoot"];
105 console.debug("no cometd root specified in djConfig and no root passed");
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));
113 var tmp = parts[4].split(":");
114 var thisHost = tmp[0];
115 var thisPort = tmp[1]||"80"; // FIXME: match 443
117 parts = this.url.match(new RegExp(regexp));
119 tmp = parts[4].split(":");
120 var urlHost = tmp[0];
121 var urlPort = tmp[1]||"80";
122 this._isXD = ((urlHost != thisHost)||(urlPort != thisPort));
128 if(props.ext["json-comment-filtered"]!==true && props.ext["json-comment-filtered"]!==false){
129 props.ext["json-comment-filtered"] = true;
132 props.ext = { "json-comment-filtered": true };
134 props.supportedConnectionTypes = dojo.map(this.connectionTypes.pairs, "return item[0]");
137 props = this._extendOut(props);
141 handleAs: this.handleAs,
142 content: { "message": dojo.toJson([props]) },
143 load: dojo.hitch(this,function(msg){
145 this._finishInit(msg);
147 error: dojo.hitch(this,function(e){
148 console.debug("handshake error!:",e);
150 this._finishInit([{}]);
155 dojo.mixin(bindArgs, bargs);
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);
166 this._subscriptions = [];
167 this._initialized = true;
172 // if xdomain, then we assume jsonp for handshake
174 bindArgs.callbackParamName = "jsonp";
175 r = dojo.io.script.get(bindArgs);
177 r = dojo.xhrPost(bindArgs);
179 dojo.publish("/cometd/meta", [
191 this.publish = function(/*String*/ channel, /*Object*/ data, /*Object?*/ props){
193 // publishes the passed message to the cometd server for delivery
194 // on the specified topic
196 // the destination channel for the message
198 // a JSON object containing the message "payload"
200 // Optional. Other meta-data to be mixed into the top-level of the
207 dojo.mixin(message, props);
209 this._sendMessage(message);
213 this.subscribe = function( /*String */ channel,
214 /*Object */ objOrFunc,
215 /*String */ funcName,
216 /*Object?*/ props){ // return: dojo.Deferred
218 // inform the server of this client's interest in channel
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).
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.
236 // name of the cometd channel to subscribe to
238 // an object scope for funcName or the name or reference to a
239 // function to be called when messages are delivered to the
242 // the second half of the objOrFunc/funcName pair for identifying
243 // a callback function to notifiy upon channel message delivery
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");
250 // Subscribe before connection is initialized
251 // | dojox.cometd.subscribe("/foo/bar", console, "debug");
252 // | dojox.cometd.init("http://myserver.com:8080/cometd");
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);
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");
268 var tname = "/cometd"+channel;
269 var subs = this._subscriptions[tname];
270 if(!subs || subs.length==0){
272 props.channel = "/meta/subscribe";
273 props.subscription = channel;
274 this._sendMessage(props);
276 var _ds = this._deferredSubscribes;
278 _ds[channel].cancel();
281 _ds[channel] = new dojo.Deferred();
285 if( subs[i].objOrFunc === objOrFunc && (!subs[i].funcName&&!funcName||subs[i].funcName==funcName) ){
290 var topic = dojo.subscribe(tname, objOrFunc, funcName);
293 objOrFunc: objOrFunc,
296 this._subscriptions[tname] = subs;
298 var ret = this._deferredSubscribes[channel]||{};
299 ret.args = dojo._toArray(arguments);
300 return ret; // dojo.Deferred
305 this.unsubscribe = function( /*String*/ channel,
306 /*Object?*/ objOrFunc,
307 /*String?*/ funcName,
310 // inform the server of this client's disinterest in channel
312 // name of the cometd channel to unsubscribe from
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.
318 // the second half of the objOrFunc/funcName pair for identifying
319 // a callback function to notifiy upon channel message delivery
322 (arguments.length == 1) &&
323 (!dojo.isString(channel)) &&
326 // it's a subscription handle, unroll
327 return this.unsubscribe.apply(this, channel.args);
330 var tname = "/cometd"+channel;
331 var subs = this._subscriptions[tname];
332 if(!subs || subs.length==0){
341 sb.objOrFunc===objOrFunc &&
342 (!sb.funcName && !funcName || sb.funcName==funcName)
345 dojo.unsubscribe(subs[i].topic);
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];
364 return this._deferredUnsubscribes[channel]; // dojo.Deferred
368 this.disconnect = function(){
370 // Disconnect from the server.
372 // Disconnect from the server by sending a disconnect message
374 // | dojox.cometd.disconnect();
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);
383 this._subscriptions = [];
385 if(this._initialized && this.currentTransport){
386 this._initialized=false;
387 this.currentTransport.disconnect();
390 this._connected=false;
391 dojo.publish("/cometd/meta", [{cometd:this,action:"connect",successful:false,state:this.state()}]);
393 this._initialized=false;
394 dojo.publish("/cometd/meta", [{cometd:this,action:"disconnect",successful:true,state:this.state()}]);
398 // public extension points
400 this.subscribed = function( /*String*/channel, /*Object*/message){ }
402 this.unsubscribed = function(/*String*/channel, /*Object*/message){ }
405 // private methods (TODO name all with leading _)
407 this.tunnelInit = function(childLocation, childDomain){
408 // placeholder - replaced by _finishInit
411 this.tunnelCollapse = function(){
412 // placeholder - replaced by _finishInit
415 this._backoff = function(){
417 this._advice={reconnect:"retry",interval:0};
419 else if(!this._advice.interval){
420 this._advice.interval=0;
422 if(this._backoffInterval<this._backoffMax){
423 this._backoffInterval+=this._backoffIncrement;
427 this._backon = function(){
428 this._backoffInterval=0;
431 this._interval = function(){
432 var i=this._backoffInterval+(this._advice?(this._advice.interval?this._advice.interval:0):0);
434 console.debug("Retry in interval+backoff="+this._advice.interval+"+"+this._backoffInterval+"="+i+"ms");
438 this._finishInit = function(data){
440 // Handle the handshake return from the server and initialize
441 // connection if all is OK
443 this.handshakeReturn = data;
445 // remember any advice
447 this._advice = data.advice;
450 var successful=data.successful?data.successful:false;
453 if(data.version < this.minimumVersion){
454 console.debug("cometd protocol version mismatch. We wanted", this.minimumVersion, "but got", data.version);
456 this._advice.reconnect="none";
462 this.currentTransport = this.connectionTypes.match(
463 data.supportedConnectionTypes,
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);
476 dojo.publish("/cometd/meta", [{cometd:this,action:"handshook",successful:successful,state:this.state()}]);
478 // If there is a problem
480 console.debug("cometd init failed");
482 if(this._advice && this._advice["reconnect"]=="none"){
483 console.debug("cometd reconnect: none");
485 setTimeout(dojo.hitch(this, "init", this.url, this._props),this._interval());
490 // fixme: lots of repeated code...why?
492 this._extendIn = function(message){
493 // Handle extensions for inbound messages
494 dojo.forEach(dojox.cometd._extendInList, function(f){
495 message = f(message)||message;
500 this._extendOut = function(message){
501 // Handle extensions for inbound messages
502 dojo.forEach(dojox.cometd._extendOutList, function(f){
503 message = f(message)||message;
509 this.deliver = function(messages){
510 dojo.forEach(messages, this._deliver, this);
514 this._deliver = function(message){
515 // dipatch events along the specified path
517 message=this._extendIn(message);
519 if(!message["channel"]){
520 if(message["success"] !== true){
521 console.debug("cometd error: no channel for message!", message);
525 this.lastMessage = message;
528 this._advice = message.advice; // TODO maybe merge?
531 // check to see if we got a /meta channel message that we care about
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;
542 }else if(!this._initialized){
543 this._connected = false; // finish disconnect
545 dojo.publish("/cometd/meta",[{cometd:this,action:"connect",successful:message.successful,state:this.state()}]);
547 case "/meta/subscribe":
548 deferred = this._deferredSubscribes[message.subscription];
549 if(!message.successful){
551 deferred.errback(new Error(message.error));
553 this.currentTransport.cancelConnect();
556 dojox.cometd.subscribed(message.subscription, message);
558 deferred.callback(true);
561 case "/meta/unsubscribe":
562 deferred = this._deferredUnsubscribes[message.subscription];
563 if(!message.successful){
565 deferred.errback(new Error(message.error));
567 this.currentTransport.cancelConnect();
570 this.unsubscribed(message.subscription, message);
572 deferred.callback(true);
576 if(message.successful && !message.successful){
577 this.currentTransport.cancelConnect();
583 // send the message down for processing by the transport
584 this.currentTransport.deliver(message);
587 // dispatch the message to any locally subscribed listeners
589 var messages=[message];
591 // Determine target topic
592 var tname="/cometd"+message.channel;
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];
601 dojo.publish(tnameGlob+"/**",messages);
602 dojo.publish(tnameGlob+"/*",messages);
604 // deliver to target topic
605 dojo.publish(tname,messages);
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]);
618 this._messageQ.push(message);
623 this.startBatch = function(){
627 this.endBatch = function(){
628 if(--this.batch <= 0 && this.currentTransport && this._connected){
631 var messages=this._messageQ;
633 if(messages.length>0){
634 this.currentTransport.sendMessages(messages);
639 this._onUnload = function(){
640 // make this the last of the onUnload method
641 dojo.addOnUnload(dojox.cometd,"disconnect");
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
648 if (this._advice && this._advice.timeout && this.expectedNetworkDelay>0)
649 _advised=this._advice.timeout + this.expectedNetworkDelay;
651 if (this.connectTimeout>0 && this.connectTimeout<_advised)
652 return this.connectTimeout;
659 transport objects MUST expose the following methods:
665 optional, standard but transport dependent methods are:
669 Transports SHOULD be namespaced under the cometd object and transports MUST
670 register themselves with cometd.connectionTypes
672 here's a stub transport defintion:
674 cometd.blahTransport = new function(){
675 this._connectionType="my-polling";
677 this.lastTimestamp = null;
679 this.check = function(types, version, xdomain){
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");
686 this.startup = function(){
687 if(dojox.cometd._polling){ return; }
688 // FIXME: fill in startup routine here
689 dojox.cometd._polling = true;
692 this.sendMessages = function(message){
693 // FIXME: fill in message array sending logic
696 this.deliver = function(message){
699 this.disconnect = function(){
700 // send orderly disconnect message
703 this.cancelConnect = function(){
704 // cancel the current connection
707 cometd.connectionTypes.register("blah", cometd.blahTransport.check, cometd.blahTransport);
710 dojox.cometd.longPollTransport = new function(){
711 this._connectionType="long-polling";
714 this.check = function(types, version, xdomain){
715 return ((!xdomain)&&(dojo.indexOf(types, "long-polling") >= 0));
718 this.tunnelInit = function(){
720 channel: "/meta/connect",
721 clientId: this._cometd.clientId,
722 connectionType: this._connectionType,
723 id: ""+this._cometd.messageId++
725 message=this._cometd._extendOut(message);
726 this.openTunnelWith({message: dojo.toJson([message])});
729 this.tunnelCollapse = function(){
730 // TODO handle transport specific advice
732 if(!this._cometd._initialized){ return; }
734 if(this._cometd._advice && this._cometd._advice["reconnect"]=="none"){
735 console.debug("cometd reconnect: none");
738 setTimeout(dojo.hitch(this,function(){ this._connect(); }),this._cometd._interval());
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");
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){
754 channel: "/meta/connect",
755 connectionType: this._connectionType,
756 clientId: this._cometd.clientId,
757 id: ""+this._cometd.messageId++
759 if (this._cometd.connectTimeout>this._cometd.expectedNetworkDelay)
760 message.advice={timeout:(this._cometd.connectTimeout-this._cometd.expectedNetworkDelay)};
762 message=this._cometd._extendOut(message);
763 this.openTunnelWith({message: dojo.toJson([message])});
767 this.deliver = function(message){
771 this.openTunnelWith = function(content, url){
772 this._cometd._polling = true;
774 url: (url||this._cometd.url),
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();
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();
792 var connectTimeout=this._cometd._connectTimeout();
793 if (connectTimeout>0)
794 post.timeout=connectTimeout;
796 this._poll = dojo.xhrPost(post);
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]);
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);
813 message: dojo.toJson(messages)
818 this.startup = function(handshakeData){
819 if(this._cometd._connected){ return; }
823 this.disconnect = function(){
825 channel: "/meta/disconnect",
826 clientId: this._cometd.clientId,
827 id: ""+this._cometd.messageId++
829 message=this._cometd._extendOut(message);
831 url: this._cometd.url||dojo.config["cometdRoot"],
832 handleAs: this._cometd.handleAs,
834 message: dojo.toJson([message])
839 this.cancelConnect = function(){
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();
847 this.tunnelCollapse();
853 dojox.cometd.callbackPollTransport = new function(){
854 this._connectionType = "callback-polling";
857 this.check = function(types, version, xdomain){
858 // we handle x-domain!
859 return (dojo.indexOf(types, "callback-polling") >= 0);
862 this.tunnelInit = function(){
864 channel: "/meta/connect",
865 clientId: this._cometd.clientId,
866 connectionType: this._connectionType,
867 id: ""+this._cometd.messageId++
869 message = this._cometd._extendOut(message);
870 this.openTunnelWith({
871 message: dojo.toJson([message])
875 this.tunnelCollapse = dojox.cometd.longPollTransport.tunnelCollapse;
876 this._connect = dojox.cometd.longPollTransport._connect;
877 this.deliver = dojox.cometd.longPollTransport.deliver;
879 this.openTunnelWith = function(content, url){
880 this._cometd._polling = true;
882 load: dojo.hitch(this, function(data){
883 this._cometd._polling=false;
884 this._cometd.deliver(data);
885 this._cometd._backon();
886 this.tunnelCollapse();
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();
895 url: (url||this._cometd.url),
897 callbackParamName: "jsonp"
899 var connectTimeout=this._cometd._connectTimeout();
900 if (connectTimeout>0)
901 script.timeout=connectTimeout;
902 dojo.io.script.get(script);
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]);
912 url: this._cometd.url||dojo.config["cometdRoot"],
913 load: dojo.hitch(this._cometd, "deliver"),
914 callbackParamName: "jsonp",
915 content: { message: dojo.toJson( messages ) }
917 return dojo.io.script.get(bindArgs);
920 this.startup = function(handshakeData){
921 if(this._cometd._connected){ return; }
925 this.disconnect = dojox.cometd.longPollTransport.disconnect;
927 this.disconnect = function(){
929 channel:"/meta/disconnect",
930 clientId:this._cometd.clientId,
931 id:""+this._cometd.messageId++
933 message=this._cometd._extendOut(message);
935 url: this._cometd.url||dojo.config["cometdRoot"],
936 callbackParamName: "jsonp",
938 message: dojo.toJson([message])
943 this.cancelConnect = function(){}
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);
948 dojo.addOnUnload(dojox.cometd,"_onUnload");