blob: 5dcfdd9651d1aebc644f40700df828ad37823fd4 [file] [log] [blame]
/*
Copyright (c) 2004-2007, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
modified BSD license. For more information on Dojo licensing, see:
http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing
*/
/*
This is a compiled version of Dojo, built for deployment and not for
development. To get an editable version, please visit:
http://dojotoolkit.org
for documentation and information on getting the source.
*/
if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.AdapterRegistry"] = true;
dojo.provide("dojo.AdapterRegistry");
dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){
// summary:
// A registry to make contextual calling/searching easier.
// description:
// Objects of this class keep list of arrays in the form [name, check,
// wrap, directReturn] that are used to determine what the contextual
// result of a set of checked arguments is. All check/wrap functions
// in this registry should be of the same arity.
// example:
// | // create a new registry
// | var reg = new dojo.AdapterRegistry();
// | reg.register("handleString",
// | dojo.isString,
// | function(str){
// | // do something with the string here
// | }
// | );
// | reg.register("handleArr",
// | dojo.isArray,
// | function(arr){
// | // do something with the array here
// | }
// | );
// |
// | // now we can pass reg.match() *either* an array or a string and
// | // the value we pass will get handled by the right function
// | reg.match("someValue"); // will call the first function
// | reg.match(["someValue"]); // will call the second
this.pairs = [];
this.returnWrappers = returnWrappers || false; // Boolean
}
dojo.extend(dojo.AdapterRegistry, {
register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){
// summary:
// register a check function to determine if the wrap function or
// object gets selected
// name:
// a way to identify this matcher.
// check:
// a function that arguments are passed to from the adapter's
// match() function. The check function should return true if the
// given arguments are appropriate for the wrap function.
// directReturn:
// If directReturn is true, the value passed in for wrap will be
// returned instead of being called. Alternately, the
// AdapterRegistry can be set globally to "return not call" using
// the returnWrappers property. Either way, this behavior allows
// the registry to act as a "search" function instead of a
// function interception library.
// override:
// If override is given and true, the check function will be given
// highest priority. Otherwise, it will be the lowest priority
// adapter.
this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]);
},
match: function(/* ... */){
// summary:
// Find an adapter for the given arguments. If no suitable adapter
// is found, throws an exception. match() accepts any number of
// arguments, all of which are passed to all matching functions
// from the registered pairs.
for(var i = 0; i < this.pairs.length; i++){
var pair = this.pairs[i];
if(pair[1].apply(this, arguments)){
if((pair[3])||(this.returnWrappers)){
return pair[2];
}else{
return pair[2].apply(this, arguments);
}
}
}
throw new Error("No match found");
},
unregister: function(name){
// summary: Remove a named adapter from the registry
// FIXME: this is kind of a dumb way to handle this. On a large
// registry this will be slow-ish and we can use the name as a lookup
// should we choose to trade memory for speed.
for(var i = 0; i < this.pairs.length; i++){
var pair = this.pairs[i];
if(pair[0] == name){
this.pairs.splice(i, 1);
return true;
}
}
return false;
}
});
}
if(!dojo._hasResource["dojo.io.script"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.io.script"] = true;
dojo.provide("dojo.io.script");
/*=====
dojo.io.script.__ioArgs = function(kwArgs){
// summary:
// All the properties described in the dojo.__ioArgs type, apply to this
// type as well, EXCEPT "handleAs". It is not applicable to
// dojo.io.script.get() calls, since it is implied by the usage of
// "callbackParamName" (response will be a JSONP call returning JSON)
// or "checkString" (response is pure JavaScript defined in
// the body of the script that was attached). The following additional
// properties are allowed for dojo.io.script.get():
// callbackParamName: String
// The URL parameter name that indicates the JSONP callback string.
// For instance, when using Yahoo JSONP calls it is normally,
// callbackParamName: "callback". For AOL JSONP calls it is normally
// callbackParamName: "c".
// checkString: String
// A string of JavaScript that when evaluated like so:
// "typeof(" + checkString + ") != 'undefined'"
// being true means that the script fetched has been loaded.
// Do not use this if doing a JSONP type of call (use callbackParamName instead).
}
=====*/
dojo.io.script = {
get: function(/*dojo.io.script.__ioArgs*/args){
// summary:
// sends a get request using a dynamically created script tag.
var dfd = this._makeScriptDeferred(args);
var ioArgs = dfd.ioArgs;
dojo._ioAddQueryToUrl(ioArgs);
this.attach(ioArgs.id, ioArgs.url);
dojo._ioWatch(dfd, this._validCheck, this._ioCheck, this._resHandle);
return dfd;
},
attach: function(/*String*/id, /*String*/url){
// summary:
// creates a new <script> tag pointing to the specified URL and
// adds it to the document.
// description:
// Attaches the script element to the DOM. Use this method if you
// just want to attach a script to the DOM and do not care when or
// if it loads.
var element = dojo.doc.createElement("script");
element.type = "text/javascript";
element.src = url;
element.id = id;
dojo.doc.getElementsByTagName("head")[0].appendChild(element);
},
remove: function(/*String*/id){
//summary: removes the script element with the given id.
dojo._destroyElement(dojo.byId(id));
//Remove the jsonp callback on dojo.io.script, if it exists.
if(this["jsonp_" + id]){
delete this["jsonp_" + id];
}
},
_makeScriptDeferred: function(/*Object*/args){
//summary:
// sets up a Deferred object for an IO request.
var dfd = dojo._ioSetArgs(args, this._deferredCancel, this._deferredOk, this._deferredError);
var ioArgs = dfd.ioArgs;
ioArgs.id = "dojoIoScript" + (this._counter++);
ioArgs.canDelete = false;
//Special setup for jsonp case
if(args.callbackParamName){
//Add the jsonp parameter.
ioArgs.query = ioArgs.query || "";
if(ioArgs.query.length > 0){
ioArgs.query += "&";
}
ioArgs.query += args.callbackParamName + "=dojo.io.script.jsonp_" + ioArgs.id + "._jsonpCallback";
//Setup the Deferred to have the jsonp callback.
ioArgs.canDelete = true;
dfd._jsonpCallback = this._jsonpCallback;
this["jsonp_" + ioArgs.id] = dfd;
}
return dfd; // dojo.Deferred
},
_deferredCancel: function(/*Deferred*/dfd){
//summary: canceller function for dojo._ioSetArgs call.
//DO NOT use "this" and expect it to be dojo.io.script.
dfd.canceled = true;
if(dfd.ioArgs.canDelete){
dojo.io.script._deadScripts.push(dfd.ioArgs.id);
}
},
_deferredOk: function(/*Deferred*/dfd){
//summary: okHandler function for dojo._ioSetArgs call.
//DO NOT use "this" and expect it to be dojo.io.script.
//Add script to list of things that can be removed.
if(dfd.ioArgs.canDelete){
dojo.io.script._deadScripts.push(dfd.ioArgs.id);
}
if(dfd.ioArgs.json){
//Make sure to *not* remove the json property from the
//Deferred, so that the Deferred can still function correctly
//after the response is received.
return dfd.ioArgs.json;
}else{
//FIXME: cannot return the dfd here, otherwise that stops
//the callback chain in Deferred. So return the ioArgs instead.
//This doesn't feel right.
return dfd.ioArgs;
}
},
_deferredError: function(/*Error*/error, /*Deferred*/dfd){
//summary: errHandler function for dojo._ioSetArgs call.
if(dfd.ioArgs.canDelete){
//DO NOT use "this" and expect it to be dojo.io.script.
if(error.dojoType == "timeout"){
//For timeouts, remove the script element immediately to
//avoid a response from it coming back later and causing trouble.
dojo.io.script.remove(dfd.ioArgs.id);
}else{
dojo.io.script._deadScripts.push(dfd.ioArgs.id);
}
}
console.debug("dojo.io.script error", error);
return error;
},
_deadScripts: [],
_counter: 1,
_validCheck: function(/*Deferred*/dfd){
//summary: inflight check function to see if dfd is still valid.
//Do script cleanup here. We wait for one inflight pass
//to make sure we don't get any weird things by trying to remove a script
//tag that is part of the call chain (IE 6 has been known to
//crash in that case).
var _self = dojo.io.script;
var deadScripts = _self._deadScripts;
if(deadScripts && deadScripts.length > 0){
for(var i = 0; i < deadScripts.length; i++){
//Remove the script tag
_self.remove(deadScripts[i]);
}
dojo.io.script._deadScripts = [];
}
return true;
},
_ioCheck: function(/*Deferred*/dfd){
//summary: inflight check function to see if IO finished.
//Check for finished jsonp
if(dfd.ioArgs.json){
return true;
}
//Check for finished "checkString" case.
var checkString = dfd.ioArgs.args.checkString;
if(checkString && eval("typeof(" + checkString + ") != 'undefined'")){
return true;
}
return false;
},
_resHandle: function(/*Deferred*/dfd){
//summary: inflight function to handle a completed response.
if(dojo.io.script._ioCheck(dfd)){
dfd.callback(dfd);
}else{
//This path should never happen since the only way we can get
//to _resHandle is if _ioCheck is true.
dfd.errback(new Error("inconceivable dojo.io.script._resHandle error"));
}
},
_jsonpCallback: function(/*JSON Object*/json){
//summary:
// generic handler for jsonp callback. A pointer to this function
// is used for all jsonp callbacks. NOTE: the "this" in this
// function will be the Deferred object that represents the script
// request.
this.ioArgs.json = json;
}
}
}
if(!dojo._hasResource["dojox.cometd._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.cometd._base"] = true;
dojo.provide("dojox.cometd._base");
/*
* this file defines Comet protocol client. Actual message transport is
* deferred to one of several connection type implementations. The default is a
* long-polling implementation. A single global object named "dojox.cometd" is
* used to mediate for these connection types in order to provide a stable
* interface.
*
* extensions modules may be loaded (eg "dojox.cometd.timestamp", that use
* the cometd._extendInList and cometd._extendOutList fields to provide functions
* that extend and handling incoming and outgoing messages.
*/
dojox.cometd = new function(){
// cometd states:
this.DISCONNECTED="DISCONNECTED"; // _initialized==false && _connected==false
this.CONNECTING="CONNECTING"; // _initialized==true && _connected==false (handshake sent)
this.CONNECTED="CONNECTED"; // _initialized==true && _connected==true (first successful connect)
this.DISCONNECTING="DISCONNECING"; // _initialized==false && _connected==true (disconnect sent)
this._initialized = false;
this._connected = false;
this._polling = false;
this.connectionTypes = new dojo.AdapterRegistry(true);
this.version="1.0";
this.minimumVersion="0.9";
this.clientId=null;
this.messageId=0;
this.batch=0;
this._isXD = false;
this.handshakeReturn=null;
this.currentTransport=null;
this.url = null;
this.lastMessage=null;
this._messageQ=[];
this.handleAs="json-comment-optional";
this._advice={};
this._maxInterval=30000;
this._backoffInterval=1000;
this._deferredSubscribes={};
this._deferredUnsubscribes={};
this._subscriptions=[];
this._extendInList=[]; // List of functions invoked before delivering messages
this._extendOutList=[]; // List of functions invoked before sending messages
this.state = function() {
return this._initialized?(this._connected?this.CONNECTED:this.CONNECTING):(this._connected?this.DISCONNECTING:this.DISCONNECTED);
}
this.init = function( /*String*/ root,
/*Object|null */ props,
/*Object|null */ bargs){ // return: dojo.Deferred
// summary:
// Initialize the cometd implementation of the Bayeux protocol
// description:
// Initialize the cometd implementation of the Bayeux protocol by
// sending a handshake message. The cometd state will be changed to CONNECTING
// until a handshake response is received and the first successful connect message
// has returned.
// The protocol state changes may be monitored
// by subscribing to the dojo topic "/cometd/meta" where events are
// published in the form {cometd:this,action:"handshake",successful:true,state:this.state()}
// root:
// The URL of the cometd server. If the root is absolute, the host
// is examined to determine if xd transport is needed. Otherwise the
// same domain is assumed.
// props:
// An optional object that is used as the basis of the handshake message
// bargs:
// An optional object of bind args mixed in with the send of the handshake
// example:
// | dojox.cometd.init("/cometd");
// | dojox.cometd.init("http://xdHost/cometd",{ext:{user:"fred",pwd:"secret"}});
// FIXME: if the root isn't from the same host, we should automatically
// try to select an XD-capable transport
props = props||{};
// go ask the short bus server what we can support
props.version = this.version;
props.minimumVersion = this.minimumVersion;
props.channel = "/meta/handshake";
props.id = ""+this.messageId++;
this.url = root||djConfig["cometdRoot"];
if(!this.url){
console.debug("no cometd root specified in djConfig and no root passed");
return null;
}
// Are we x-domain? borrowed from dojo.uri.Uri in lieu of fixed host and port properties
var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
var parts = (""+window.location).match(new RegExp(regexp));
if(parts[4]){
var tmp = parts[4].split(":");
var thisHost = tmp[0];
var thisPort = tmp[1]||"80"; // FIXME: match 443
parts = this.url.match(new RegExp(regexp));
if(parts[4]){
tmp = parts[4].split(":");
var urlHost = tmp[0];
var urlPort = tmp[1]||"80";
this._isXD = ((urlHost != thisHost)||(urlPort != thisPort));
}
}
if(!this._isXD){
if(props.ext){
if(props.ext["json-comment-filtered"]!==true && props.ext["json-comment-filtered"]!==false){
props.ext["json-comment-filtered"] = true;
}
}else{
props.ext = { "json-comment-filtered": true };
}
}
props=this._extendOut(props);
var bindArgs = {
url: this.url,
handleAs: this.handleAs,
content: { "message": dojo.toJson([props]) },
load: dojo.hitch(this,function(msg){
this._finishInit(msg);
}),
error: dojo.hitch(this,function(e){
console.debug("handshake error!:",e);
this._finishInit([{}]);
})
};
if(bargs){
dojo.mixin(bindArgs, bargs);
}
this._props=props;
for(var tname in this._subscriptions){
for(var sub in this._subscriptions[tname]){
if(this._subscriptions[tname][sub].topic){
dojo.unsubscribe(this._subscriptions[tname][sub].topic);
}
}
}
this._messageQ = [];
this._subscriptions = [];
this._initialized=true;
this.batch=0;
this.startBatch();
var r;
// if xdomain, then we assume jsonp for handshake
if(this._isXD){
bindArgs.callbackParamName="jsonp";
r= dojo.io.script.get(bindArgs);
} else
r = dojo.xhrPost(bindArgs);
dojo.publish("/cometd/meta", [{cometd:this,action:"handshake",successful:true,state:this.state()}]);
return r;
}
this.publish = function(/*String*/channel, /*Object */data, /*Object|null */properties){
// summary:
// publishes the passed message to the cometd server for delivery
// on the specified topic
// channel:
// the destination channel for the message
// data:
// a JSON object containing the message "payload"
// properties:
// Optional. Other meta-data to be mixed into the top-level of the
// message
var message = {
data: data,
channel: channel
};
if(properties){
dojo.mixin(message, properties);
}
this._sendMessage(message);
}
this.subscribe = function( /*String */ channel,
/*Object */ objOrFunc,
/*String */ funcName){ // return: dojo.Deferred
// summary:
// inform the server of this client's interest in channel
// channel:
// name of the cometd channel to subscribe to
// objOrFunc:
// an object scope for funcName or the name or reference to a
// function to be called when messages are delivered to the
// channel
// funcName:
// the second half of the objOrFunc/funcName pair for identifying
// a callback function to notifiy upon channel message delivery
if(objOrFunc){
var tname = "/cometd"+channel;
var subs=this._subscriptions[tname];
if(!subs || subs.length==0){
subs=[];
this._sendMessage({
channel: "/meta/subscribe",
subscription: channel
});
var _ds = this._deferredSubscribes;
_ds[channel] = new dojo.Deferred();
if(_ds[channel]){
_ds[channel].cancel();
delete _ds[channel];
}
}
for (var i in subs){
if (subs[i].objOrFunc===objOrFunc&&(!subs[i].funcName&&!funcName||subs[i].funcName==funcName))
return null;
}
var topic = dojo.subscribe(tname, objOrFunc, funcName);
subs.push({
topic: topic,
objOrFunc: objOrFunc,
funcName: funcName
});
this._subscriptions[tname] =subs;
}
return this._deferredSubscribes[channel];
}
this.unsubscribe = function( /*string*/ channel,
/*object|null*/ objOrFunc,
/*string|null*/ funcName){
// summary:
// inform the server of this client's disinterest in channel
// channel:
// name of the cometd channel to unsubscribe from
// objOrFunc:
// an object scope for funcName or the name or reference to a
// function to be called when messages are delivered to the
// channel. If null then all subscribers to the channel are unsubscribed.
// funcName:
// the second half of the objOrFunc/funcName pair for identifying
// a callback function to notifiy upon channel message delivery
var tname = "/cometd"+channel;
var subs=this._subscriptions[tname];
if(!subs || subs.length==0){
return null;
}
var s=0;
for(var i in subs){
var sb=subs[i];
if( (!objOrFunc) ||
(
sb.objOrFunc===objOrFunc &&
(!sb.funcName && !funcName || sb.funcName==funcName)
)
){
dojo.unsubscribe(subs[i].topic);
delete subs[i];
}else{
s++;
}
}
if(s==0){
delete this._subscriptions[tname];
this._sendMessage({
channel: "/meta/unsubscribe",
subscription: channel
});
this._deferredUnsubscribes[channel] = new dojo.Deferred();
if (this._deferredSubscribes[channel]){
this._deferredSubscribes[channel].cancel();
delete this._deferredSubscribes[channel];
}
}
return this._deferredUnsubscribes[channel];
}
this.disconnect = function(){
// summary:
// Disconnect from the server.
// description:
// Disconnect from the server by sending a disconnect message
// example:
// | dojox.cometd.disconnect();
for(var tname in this._subscriptions){
for(var sub in this._subscriptions[tname]){
if(this._subscriptions[tname][sub].topic){
dojo.unsubscribe(this._subscriptions[tname][sub].topic);
}
}
}
this._subscriptions = [];
this._messageQ = [];
if(this._initialized && this.currentTransport){
this._initialized=false;
this.currentTransport.disconnect();
}
if(!this._polling) {
this._connected=false;
dojo.publish("/cometd/meta", [{cometd:this,action:"connect",successful:false,state:this.state()}]);
}
this._initialized=false;
dojo.publish("/cometd/meta", [{cometd:this,action:"disconnect",successful:true,state:this.state()}]);
}
// public extension points
this.subscribed = function( /*String*/channel, /*Object*/message){ }
this.unsubscribed = function(/*String*/channel, /*Object*/message){ }
// private methods (TODO name all with leading _)
this.tunnelInit = function(childLocation, childDomain){
// placeholder - replaced by _finishInit
}
this.tunnelCollapse = function(){
// placeholder - replaced by _finishInit
}
this._backoff = function(){
if(!this._advice || !this._advice.interval){
this._advice={reconnect:"retry",interval:0}; // TODO Is this good advice?
}
if(this._advice.interval<this._maxInterval){
this._advice.interval+=this._backoffInterval;
}
}
this._finishInit = function(data){
// summary:
// Handle the handshake return from the server and initialize
// connection if all is OK
data = data[0];
this.handshakeReturn = data;
// remember any advice
if(data["advice"]){
this._advice = data.advice;
}
var successful=data.successful?data.successful:false;
// check version
if(data.version < this.minimumVersion){
console.debug("cometd protocol version mismatch. We wanted", this.minimumVersion, "but got", data.version);
successful=false;
this._advice.reconnect="none";
}
// If all OK
if(successful){
// pick a transport
this.currentTransport = this.connectionTypes.match(
data.supportedConnectionTypes,
data.version,
this._isXD
);
// initialize the transport
this.currentTransport._cometd = this;
this.currentTransport.version = data.version;
this.clientId = data.clientId;
this.tunnelInit = dojo.hitch(this.currentTransport, "tunnelInit");
this.tunnelCollapse = dojo.hitch(this.currentTransport, "tunnelCollapse");
this.currentTransport.startup(data);
}
dojo.publish("/cometd/meta", [{cometd:this,action:"handshook",successful:successful,state:this.state()}]);
// If there is a problem
if(!successful){
console.debug("cometd init failed");
this._backoff();
// follow advice
if(this._advice && this._advice["reconnect"]=="none"){
console.debug("cometd reconnect: none");
}else if(this._advice && this._advice["interval"] && this._advice.interval>0 ){
setTimeout(
dojo.hitch(this, function(){ this.init(cometd.url,this._props); }),
this._advice.interval
);
}else{
this.init(this.url,this._props);
}
}
}
this._extendIn = function(message){
// Handle extensions for inbound messages
var m=message;
dojo.forEach(dojox.cometd._extendInList, function(f){
var n=f(m);
if(n)m=n;
});
return m;
}
this._extendOut= function(message){
// Handle extensions for inbound messages
var m=message;
dojo.forEach(dojox.cometd._extendOutList, function(f){
var n=f(m);
if(n)m=n;
});
return m;
}
this.deliver = function(messages){
dojo.forEach(messages, this._deliver, this);
return messages;
}
this._deliver = function(message){
// dipatch events along the specified path
message=this._extendIn(message);
if(!message["channel"]){
if(message["success"] !== true){
console.debug("cometd error: no channel for message!", message);
return;
}
}
this.lastMessage = message;
if(message.advice){
this._advice = message.advice; // TODO maybe merge?
}
// check to see if we got a /meta channel message that we care about
var deferred=null;
if( (message["channel"]) &&
(message.channel.length > 5)&&
(message.channel.substr(0, 5) == "/meta")){
// check for various meta topic actions that we need to respond to
switch(message.channel){
case "/meta/connect":
if(message.successful && !this._connected){
this._connected = this._initialized;
this.endBatch();
} else if(!this._initialized){
this._connected = false; // finish disconnect
}
dojo.publish("/cometd/meta",[{cometd:this,action:"connect",successful:message.successful,state:this.state()}]);
break;
case "/meta/subscribe":
deferred = this._deferredSubscribes[message.subscription];
if(!message.successful){
if(deferred){
deferred.errback(new Error(message.error));
}
return;
}
dojox.cometd.subscribed(message.subscription, message);
if(deferred){
deferred.callback(true);
}
break;
case "/meta/unsubscribe":
deferred = this._deferredUnsubscribes[message.subscription];
if(!message.successful){
if(deferred){
deferred.errback(new Error(message.error));
}
return;
}
this.unsubscribed(message.subscription, message);
if(deferred){
deferred.callback(true);
}
break;
}
}
// send the message down for processing by the transport
this.currentTransport.deliver(message);
if(message.data){
// dispatch the message to any locally subscribed listeners
try {
var tname = "/cometd"+message.channel;
dojo.publish(tname, [ message ]);
}catch(e){
console.debug(e);
}
}
}
this._sendMessage = function(/* object */ message){
if(this.currentTransport && this._connected && this.batch==0){
return this.currentTransport.sendMessages([message]);
}
else{
this._messageQ.push(message);
return null;
}
}
this.startBatch = function(){
this.batch++;
}
this.endBatch = function(){
if(--this.batch <= 0 && this.currentTransport && this._connected){
this.batch=0;
var messages=this._messageQ;
this._messageQ=[];
if(messages.length>0){
this.currentTransport.sendMessages(messages);
}
}
}
this._onUnload = function(){
// make this the last of the onUnload method
dojo.addOnUnload(dojox.cometd,"disconnect");
}
}
/*
transport objects MUST expose the following methods:
- check
- startup
- sendMessages
- deliver
- disconnect
optional, standard but transport dependent methods are:
- tunnelCollapse
- tunnelInit
Transports SHOULD be namespaced under the cometd object and transports MUST
register themselves with cometd.connectionTypes
here's a stub transport defintion:
cometd.blahTransport = new function(){
this._connectionType="my-polling";
this._cometd=null;
this.lastTimestamp = null;
this.check = function(types, version, xdomain){
// summary:
// determines whether or not this transport is suitable given a
// list of transport types that the server supports
return dojo.lang.inArray(types, "blah");
}
this.startup = function(){
if(dojox.cometd._polling){ return; }
// FIXME: fill in startup routine here
dojox.cometd._polling = true;
}
this.sendMessages = function(message){
// FIXME: fill in message array sending logic
}
this.deliver = function(message){
}
this.disconnect = function(){
}
}
cometd.connectionTypes.register("blah", cometd.blahTransport.check, cometd.blahTransport);
*/
dojox.cometd.longPollTransport = new function(){
this._connectionType="long-polling";
this._cometd=null;
this.check = function(types, version, xdomain){
return ((!xdomain)&&(dojo.indexOf(types, "long-polling") >= 0));
}
this.tunnelInit = function(){
var message = {
channel: "/meta/connect",
clientId: this._cometd.clientId,
connectionType: this._connectionType,
id: ""+this._cometd.messageId++
};
message=this._cometd._extendOut(message);
this.openTunnelWith({message: dojo.toJson([message])});
}
this.tunnelCollapse = function(){
// TODO handle transport specific advice
if(!this._cometd._initialized){ return; }
if(this._cometd._advice){
if(this._cometd._advice["reconnect"]=="none"){
return;
}
if( (this._cometd._advice["interval"])&&
(this._cometd._advice.interval>0) ){
setTimeout(dojo.hitch(this,function(){ this._connect(); }),this._cometd._advice.interval);
}else{
this._connect();
}
}else{
this._connect();
}
}
this._connect = function(){
if(!this._cometd._initialized){ return; }
if(this._cometd._polling) {
console.debug("wait for poll to complete or fail");
return;
}
if( (this._cometd._advice) &&
(this._cometd._advice["reconnect"]=="handshake")
){
this._cometd._connected=false;
this._initialized = false;
this._cometd.init(this._cometd.url,this._cometd._props);
}else if(this._cometd._connected){
var message={
channel: "/meta/connect",
connectionType: this._connectionType,
clientId: this._cometd.clientId,
id: ""+this._cometd.messageId++
};
message=this._cometd._extendOut(message);
this.openTunnelWith({message: dojo.toJson([message])});
}
}
this.deliver = function(message){
// Nothing to do
}
this.openTunnelWith = function(content, url){
this._cometd._polling = true;
var d = dojo.xhrPost({
url: (url||this._cometd.url),
content: content,
handleAs: this._cometd.handleAs,
load: dojo.hitch(this, function(data){
this._cometd._polling = false;
this._cometd.deliver(data);
this.tunnelCollapse();
}),
error: dojo.hitch(this, function(err){
this._cometd._polling = false;
console.debug("tunnel opening failed:", err);
dojo.publish("/cometd/meta", [{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);
this._cometd._backoff();
this.tunnelCollapse();
})
});
}
this.sendMessages = function(messages){
for(var i=0; i<messages.length; i++){
messages[i].clientId = this._cometd.clientId;
messages[i].id = ""+this._cometd.messageId++;
messages[i]=this._cometd._extendOut(messages[i]);
}
return dojo.xhrPost({
url: this._cometd.url||djConfig["cometdRoot"],
handleAs: this._cometd.handleAs,
load: dojo.hitch(this._cometd, "deliver"),
content: {
message: dojo.toJson(messages)
}
});
}
this.startup = function(handshakeData){
if(this._cometd._connected){ return; }
this.tunnelInit();
}
this.disconnect = function(){
var message={
channel: "/meta/disconnect",
clientId: this._cometd.clientId,
id: ""+this._cometd.messageId++
};
message=this._cometd._extendOut(message);
dojo.xhrPost({
url: this._cometd.url||djConfig["cometdRoot"],
handleAs: this._cometd.handleAs,
content: {
message: dojo.toJson([message])
}
});
}
}
dojox.cometd.callbackPollTransport = new function(){
this._connectionType = "callback-polling";
this._cometd = null;
this.check = function(types, version, xdomain){
// we handle x-domain!
return (dojo.indexOf(types, "callback-polling") >= 0);
}
this.tunnelInit = function(){
var message = {
channel: "/meta/connect",
clientId: this._cometd.clientId,
connectionType: this._connectionType,
id: ""+this._cometd.messageId++
};
message = this._cometd._extendOut(message);
this.openTunnelWith({
message: dojo.toJson([message])
});
}
this.tunnelCollapse = dojox.cometd.longPollTransport.tunnelCollapse;
this._connect = dojox.cometd.longPollTransport._connect;
this.deliver = dojox.cometd.longPollTransport.deliver;
this.openTunnelWith = function(content, url){
this._cometd._polling = true;
dojo.io.script.get({
load: dojo.hitch(this, function(data){
this._cometd._polling = false;
this._cometd.deliver(data);
this.tunnelCollapse();
}),
error: dojo.hitch(this, function(err){
this._cometd._polling = false;
console.debug("tunnel opening failed:", err);
dojo.publish("/cometd/meta", [{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);
this._cometd._backoff();
this.tunnelCollapse();
}),
url: (url||this._cometd.url),
content: content,
callbackParamName: "jsonp"
});
}
this.sendMessages = function(/*array*/ messages){
for(var i=0; i<messages.length; i++){
messages[i].clientId = this._cometd.clientId;
messages[i].id = ""+this._cometd.messageId++;
messages[i]=this._cometd._extendOut(messages[i]);
}
var bindArgs = {
url: this._cometd.url||djConfig["cometdRoot"],
load: dojo.hitch(this._cometd, "deliver"),
callbackParamName: "jsonp",
content: { message: dojo.toJson( messages ) }
};
return dojo.io.script.get(bindArgs);
}
this.startup = function(handshakeData){
if(this._cometd._connected){ return; }
this.tunnelInit();
}
this.disconnect = dojox.cometd.longPollTransport.disconnect;
this.disconnect = function(){
var message={
channel:"/meta/disconnect",
clientId:this._cometd.clientId,
id:""+this._cometd.messageId++
};
message=this._cometd._extendOut(message);
dojo.io.script.get({
url: this._cometd.url||djConfig["cometdRoot"],
callbackParamName: "jsonp",
content: {
message: dojo.toJson([message])
}
});
}
}
dojox.cometd.connectionTypes.register("long-polling", dojox.cometd.longPollTransport.check, dojox.cometd.longPollTransport);
dojox.cometd.connectionTypes.register("callback-polling", dojox.cometd.callbackPollTransport.check, dojox.cometd.callbackPollTransport);
dojo.addOnUnload(dojox.cometd,"_onUnload");
}
if(!dojo._hasResource["dojox.cometd"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.cometd"] = true;
// stub loader for the cometd module since no implementation code is allowed to live in top-level files
dojo.provide("dojox.cometd");
}