You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by fh...@apache.org on 2008/09/02 22:00:38 UTC

svn commit: r691359 [8/8] - in /tomcat/trunk: ./ java/org/apache/cometd/ java/org/apache/cometd/bayeux/ java/org/apache/tomcat/bayeux/ java/org/apache/tomcat/bayeux/request/ test/org/apache/cometd/ test/org/apache/cometd/bayeux/ test/org/apache/cometd/...

Added: tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd.xd.js.uncompressed.js
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd.xd.js.uncompressed.js?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd.xd.js.uncompressed.js (added)
+++ tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd.xd.js.uncompressed.js Tue Sep  2 13:00:36 2008
@@ -0,0 +1,1149 @@
+dojo._xdResourceLoaded({
+depends: [["provide", "dojo.AdapterRegistry"],
+["provide", "dojo.io.script"],
+["provide", "dojox.cometd._base"],
+["provide", "dojox.cometd"]],
+defineResource: function(dojo){/*
+	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");
+
+
+}
+
+
+}});

Added: tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/session.js
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/session.js?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/session.js (added)
+++ tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/session.js Tue Sep  2 13:00:36 2008
@@ -0,0 +1,27 @@
+dojo.provide("dojox.cometd.session");
+dojo.require("dojox.cometd");
+
+dojox.cometd.session= new function(){
+        this._session=null;
+
+        this._in=function(msg){
+                var channel=msg.channel;
+                if (channel=="/service/ext/session"){
+                        this._session=msg.data;
+                }
+                return msg;
+        }
+
+        this._out=function(msg){
+                var channel=msg.channel;
+                if (channel=="/meta/handshake" && this._session!=null){
+                        if (!msg.ext)
+                                msg.ext={};
+                        msg.ext.session=this._session;
+                }
+                return msg;
+        }
+};
+
+dojox.cometd._extendInList.push(dojo.hitch(dojox.cometd.session,"_in"));
+dojox.cometd._extendOutList.push(dojo.hitch(dojox.cometd.session,"_out"));
\ No newline at end of file

Added: tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/timestamp.js
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/timestamp.js?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/timestamp.js (added)
+++ tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/timestamp.js Tue Sep  2 13:00:36 2008
@@ -0,0 +1,9 @@
+if(!dojo._hasResource["dojox.cometd.timestamp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.cometd.timestamp"] = true;
+dojo.provide("dojox.cometd.timestamp");
+dojo.require("dojox.cometd");
+
+// A cometd extension that adds a timestamp to every message
+dojox.cometd._extendOutList.push(function(msg){msg.timestamp=new Date().toUTCString();return msg});
+
+}

Added: tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/timesync.js
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/timesync.js?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/timesync.js (added)
+++ tomcat/trunk/webapps/cometd/examples/dojo/dojox/cometd/timesync.js Tue Sep  2 13:00:36 2008
@@ -0,0 +1,114 @@
+dojo.provide("dojox.cometd.timesync");
+dojo.require("dojox.cometd");
+
+/**
+ * this file provides the time synchronization extension to cometd.
+ * Timesync allows the client and server to exchange time information on every 
+ * handshake and connect message so that the client may calculate an approximate
+ * offset from it's own clock epoch to that of the server.
+ *
+ * With each handshake or connect, the extension sends timestamps within the 
+ * ext field like: {ext:{timesync:{tc:12345567890},...},...}
+ * where ts is the timestamp in ms since 1970 of when the message was sent.
+ *
+ * A cometd server that supports timesync, should respond with and ext field
+ * like: {ext:{timesync:{tc:12345567890,ts:1234567900,p:123},...},...}
+ * where ts is the timestamp sent by the client, te is the timestamp on the server
+ * of when the message was received and p is the poll duration in ms - ie the
+ * time the server took before sending the response.
+ *
+ * On receipt of the response, the client is able to use current time to determine
+ * the total trip time, from which p is subtracted to determine an approximate
+ * two way network traversal time. The assumption is made that the network is
+ * symmetric for traversal time, so the offset between the two clocks is 
+ * ts-tc-(now-tc-p)/2. In practise networks (and the cometd client/server software)
+ * is never perfectly symmetric, so accuracy is limited by the difference, which 
+ * can be 10s of milliseconds.
+ *
+ * In order to smooth over any transient fluctuations, the extension keeps a sliding
+ * average of the offsets received. By default this is over 10 messages, but this can
+ * be changed with the dojox.cometd.timesync._window element.
+ */
+dojox.cometd.timesync= new function(){
+
+	this._window=10;// The window size for the sliding average of offset samples.
+	this.offset=0;	// The offset in ms between the clients clock and the servers clock. Add this to the local
+			// time epoch to obtain server time.
+	this.samples=0; // The number of samples used to calculate the offset. If 0, the offset is not valid.
+	
+	this.getServerTime=function(){ // return: long
+		// Summary:
+		//	Calculate the current time on the server
+		// 
+		return new Date().getTime()+this.offset;
+	}
+	
+	this.getServerDate=function(){ // return: Date
+		// Summary:
+		//	Calculate the current time on the server
+		// 
+		return new Date(this.getServerTime());
+	}
+	
+	this.setTimeout=function(/*function*/call,/*long|Date*/atTimeOrDate){
+		// Summary:
+		//	Set a timeout function relative to server time
+		// call:
+		//	the function to call when the timeout occurs
+		// atTimeOrTime: 
+		//	a long timestamp or a Date representing the server time at
+		//	which the timeout should occur.
+		
+		var ts=(atTimeOrDate instanceof Date)?atTimeOrDate.getTime():(0+atTimeOrDate);
+		var tc=ts-this.offset;
+		var interval=tc-new Date().getTime();
+		if (interval<=0)
+			interval=1;
+		return setTimeout(call,interval);
+	}
+
+	this._in=function(/*Object*/msg){
+		// Summary:
+		//	Handle incoming messages for the timesync extension.
+		// description:
+		//	Look for ext:{timesync:{}} field and calculate offset if present.
+		// msg: 
+		//	The incoming bayeux message
+		
+		var channel=msg.channel;
+		if (channel=="/meta/handshake" || channel=="/meta/connect"){
+			if (msg.ext && msg.ext.timesync){
+				var sync=msg.ext.timesync;
+				var now=new Date().getTime();
+				var offset=sync.ts-sync.tc-(now-sync.tc-sync.p)/2;
+				
+				if (this.samples++==0)
+					this.offset=offset;
+				else
+					this.offset=(this.offset*(this._window-1)+offset)/this._window;
+			}
+		}
+		return msg;
+	}
+
+	this._out=function(msg){
+		// Summary:
+		//	Handle outgoing messages for the timesync extension.
+		// description:
+		//	Look for handshake and connect messages and add the ext:{timesync:{}} fields
+		// msg: 
+		//	The outgoing bayeux message
+		
+		var channel=msg.channel;
+		if (channel=="/meta/handshake" || channel=="/meta/connect"){
+			var now=new Date().getTime();
+			if (!msg.ext)
+				msg.ext={};
+			msg.ext.timesync={tc: now};
+		}
+		return msg;
+	}
+};
+
+dojox.cometd._extendInList.push(dojo.hitch(dojox.cometd.timesync,"_in"));
+dojox.cometd._extendOutList.push(dojo.hitch(dojox.cometd.timesync,"_out"));

Added: tomcat/trunk/webapps/cometd/examples/echo/index.html
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/echo/index.html?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/echo/index.html (added)
+++ tomcat/trunk/webapps/cometd/examples/echo/index.html Tue Sep  2 13:00:36 2008
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+    "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+    <head>
+        <title>Comet echo RPC</title>
+        <link rel="stylesheet" type="text/css" href="chat/chat.css"></link>
+        <script type="text/javascript" src="../dojo/dojo/dojo.js.uncompressed.js"></script>
+        <script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>
+        <script type="text/javascript">
+            dojo.require("dojox.cometd");
+            dojo.require("dojox.cometd.timestamp");
+            $ = dojo.byId;
+
+            var echoBehaviours = { 
+                '#phrase': {
+                    "found": function(e){
+                        e.setAttribute("autocomplete","OFF");
+                    },
+                    "onkeyup": function(e){
+                        if(e.keyCode == dojo.keys.ENTER){
+                            echoRpc($('phrase').value);
+                            $('phrase').value='';
+                            return false;
+                        }
+                        return true;
+                    }
+                },
+
+                '#sendB': {
+                    "onclick": function(e){
+                        echoRpc($('phrase').value);
+                        $('phrase').value='';
+                        return false;
+                    }
+                }
+            };
+
+
+            function setUp(){
+
+              var element=dojo.byId('phrase');
+              element.setAttribute("autocomplete","OFF");
+              dojo.connect(element, "onkeyup", function(e){   
+                        if(e.keyCode == dojo.keys.ENTER){
+                            echoRpc($('phrase').value);
+                            $('phrase').value='';
+                            return false;
+                        }
+                        return true;
+                    });
+              element=dojo.byId('sendB');
+              dojo.connect(element, "onclick", function(e){   
+                        echoRpc($('phrase').value);
+                        $('phrase').value='';
+                        return false;
+                    });
+
+
+
+              dojox.cometd.init(new String(document.location).replace(/http:\/\/[^\/]*/,'').replace(/\/examples\/.*$/,'')+"/cometd");
+              dojox.cometd.subscribe("/service/echo",echoRpcReturn);
+            }
+
+            function echoRpc(msg){
+                console.debug(msg);
+                dojox.cometd.publish("/service/echo", { msg: msg });
+            }
+            
+            function echoRpcReturn(msg){
+                dojo.byId("responses").innerHTML += (msg.timestamp?msg.timestamp:"")+" "+msg.channel+": "+msg.data.msg+"\n";
+            }
+
+            dojo.addOnLoad(setUp);
+        </script>
+    </head>
+    <body>
+        
+        <h1>Echo test</h1>
+        <p>
+            Echo data to ONLY this client using RPC style messaging over
+            cometd. Requires a server side component at /service/echo which echos
+            responses directly to the client.
+        </p>
+        <div>
+            Echo: <input id="phrase" type="text"></input> <input id="sendB" class="button" type="submit" name="join" value="Send"/>
+        </div>
+        <pre id="responses"></pre>
+    </body>
+</html>

Added: tomcat/trunk/webapps/cometd/examples/simplechat/cometdchat.htm
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/simplechat/cometdchat.htm?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/simplechat/cometdchat.htm (added)
+++ tomcat/trunk/webapps/cometd/examples/simplechat/cometdchat.htm Tue Sep  2 13:00:36 2008
@@ -0,0 +1,113 @@
+<html>
+<header><title>Comet Simple Chat Application</title>
+
+<script type="text/javascript" src="../dojo/dojo/dojo.js"></script>
+<script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>
+
+<script type="text/javascript">
+
+dojo.require("dojox.cometd");
+
+dojo.addOnLoad(function() {
+  dojo.byId("message").style.visibility='hidden';
+  dojox.cometd.init("/cometd/cometd");
+});
+
+
+function trim(str) {
+    return str.replace(/(^\s+|\s+$)/g,'');
+}
+
+
+function clear() {
+  dojo.byId("msgtext").value = "";
+  dojo.byId("msgtext").focus();
+}
+
+
+function enterKeyHandler(e) {
+if (!e) e = window.event;
+   if (e.keyCode == 13) {
+      send(trim(dojo.byId("msgtext").value));
+      clear();
+   }
+
+}
+
+
+function connect() {
+  dojox.cometd.subscribe("/chat/demo", onMsgEvent);
+  dojo.byId("login").style.visibility='hidden';
+  dojo.byId("message").style.visibility='visible';
+  send("Has joined the chat room");
+  clear();
+  dojo.byId("msgtext").onkeydown = enterKeyHandler;
+  dojo.byId("myname").appendChild(document.createTextNode("-> " + dojo.byId("scrname").value + " <-"));
+}
+
+
+function onMsgEvent(event) {
+
+   // Break apart the text string into screen name and message parts.
+   var str = trim(event.data.msg);
+   var scrname = ""; 
+   if (str.match(/^.*[|].*$/)) {
+       var spl = str.split("|");
+       scrname = spl[0];
+       str = " - " + spl[1];
+   }
+
+   // Insert the screen name in red and the message black into the DOM
+   var newP = document.createElement("p");
+   var fnt1 = document.createElement("font");
+   var attr1 = document.createAttribute("color");
+   attr1.nodeValue = "red";
+   fnt1.setAttributeNode(attr1);
+
+   var newT = document.createTextNode(scrname);
+   fnt1.appendChild(newT);
+
+   newP.appendChild(fnt1);  
+
+   var fnt2 = document.createElement("font");
+   var attr2 = document.createAttribute("color");
+   attr2.nodeValue = "black";
+   fnt2.setAttributeNode(attr2);
+
+   var newT2 = document.createTextNode(str);
+   fnt2.appendChild(newT2);
+
+   newP.appendChild(fnt2);  
+
+   dojo.byId("dialog").appendChild(newP)
+}
+
+
+function send(msg) {
+  var scrname = dojo.byId("scrname").value;
+  var evt = {'data': { 'msg': trim(scrname) + '|' + msg }};
+  onMsgEvent(evt);  // Echo local
+  dojox.cometd.publish("/chat/demo", evt.data);
+}
+
+
+</script>
+
+</head>
+</header>
+<body>
+<form>
+<div id="login">
+Screen name:&nbsp;<input type="text" id="scrname">
+<input type=Button Id=logbtn value=Connect onClick=connect()><br/>Type a screen name and click 'Connect'
+</div>
+
+<div id="dialog"></div>
+<hr/>
+<div id="message">
+<div id="myname"></div> Is my screen name<br/>
+<textarea rows="3" cols="50" id="msgtext"></textarea>[ENTER] sends message</div>
+</form>
+</body>
+</html>
+

Added: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/0.gif
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/tictactoe/resources/0.gif?rev=691359&view=auto
==============================================================================
Binary file - no diff available.

Propchange: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/0.gif
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/1.gif
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/tictactoe/resources/1.gif?rev=691359&view=auto
==============================================================================
Binary file - no diff available.

Propchange: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/1.gif
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/10.gif
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/tictactoe/resources/10.gif?rev=691359&view=auto
==============================================================================
Binary file - no diff available.

Propchange: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/10.gif
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/blank.gif
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/tictactoe/resources/blank.gif?rev=691359&view=auto
==============================================================================
Binary file - no diff available.

Propchange: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/blank.gif
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: tomcat/trunk/webapps/cometd/examples/tictactoe/resources/tictactoe.css
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/tictactoe/resources/tictactoe.css?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/tictactoe/resources/tictactoe.css (added)
+++ tomcat/trunk/webapps/cometd/examples/tictactoe/resources/tictactoe.css Tue Sep  2 13:00:36 2008
@@ -0,0 +1,74 @@
+
+body {
+  background-color : #ffffcc;
+  text-align: center;
+}
+a:link {
+  color : black;
+}
+a:visited {
+  color : black;
+}
+a:hover {
+  color : yellow;
+}
+p.instructions {
+  background-color : #ccffcc;
+  padding-left : 20%;
+  padding-right : 20%;
+}
+table {
+  border: none;
+  margin: 0 auto;
+}
+td#cell0 {
+  padding: 10px ;
+  border-bottom: thin solid black;
+  border-right: thin solid black;
+}
+td#cell1 {
+  padding: 10px ;
+  border-bottom: thin solid black;
+  border-right: thin solid black;
+  border-left: thin solid black;
+}
+td#cell2 {
+  padding: 10px ;
+  border-left: thin solid black;
+  border-bottom: thin solid black;
+}
+td#cell3 {
+  padding: 10px ;
+  border-top: thin solid black;
+  border-right: thin solid black;
+  border-bottom: thin solid black;
+}
+td#cell4 {
+  padding: 10px ;
+  border-top: thin solid black;
+  border-right: thin solid black;
+  border-left: thin solid black;
+  border-bottom: thin solid black;
+}
+td#cell5 {
+  padding: 10px ;
+  border-top: thin solid black;
+  border-left: thin solid black;
+  border-bottom: thin solid black;
+}
+td#cell6 {
+  padding: 10px ;
+  border-top: thin solid black;
+  border-right: thin solid black;
+}
+td#cell7 {
+  padding: 10px ;
+  border-top: thin solid black;
+  border-right: thin solid black;
+  border-left: thin solid black;
+}
+td#cell8 {
+  padding: 10px ;
+  border-top: thin solid black;
+  border-left: thin solid black;
+}

Added: tomcat/trunk/webapps/cometd/examples/tictactoe/ttt1.htm
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/tictactoe/ttt1.htm?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/tictactoe/ttt1.htm (added)
+++ tomcat/trunk/webapps/cometd/examples/tictactoe/ttt1.htm Tue Sep  2 13:00:36 2008
@@ -0,0 +1,252 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+    <head>
+        <title>Tic Tac Toe - Cometd Example</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <link rel="stylesheet" type="text/css" href="resources/tictactoe.css">
+
+        <script type="text/javascript" src="../dojo/dojo/dojo.js"></script>
+        <script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>
+
+        <script type="text/javascript">
+
+            dojo.require("dojox.cometd");
+
+            var X = 10;
+            var O = 1;
+   
+            var board = new Array(9);
+            var wins = new Array(8);
+
+            var turnNum = 0;
+            var winner = -1;
+
+            var iam = 0;
+
+            dojo.addOnLoad(function() {
+                initArrays();
+                dojox.cometd.init("/cometd/cometd");
+                dojox.cometd.subscribe("/demo/tictactoe", onMsgEvent);
+            });
+
+            function postMe(arg) {
+                var move = eval(arg);
+                if (iam == 0 && this.win() != -1) {
+                    dojo.byId("myrole").innerHTML = "Game is over.";
+                    return;
+                }
+                if (iam == 0 && turnNum > 1) {
+                    dojo.byId("gstatus").innerHTML = "Sorry, you will have to wait until this game ends.";
+                    return;
+                }
+                if (iam != 0 && this.whoseTurn() != iam) {
+                    dojo.byId("gstatus").innerHTML = "Not your turn.";
+                    return;
+                }
+                if (! this.turn(move)) {
+                    dojo.byId("gstatus").innerHTML = "Not a valid move try again";
+                    return;
+                }
+                var win = this.win();
+                var turn = this.whoseTurn();
+
+                var data = { 'move': move, 'win': win, 'turn': turn, 'board': board, 'turnNum': turnNum };
+                dojox.cometd.publish("/demo/tictactoe", data);
+                onMsgEvent({ 'data': data});
+                
+            };
+
+            function onMsgEvent(event) {
+                
+                var data = event.data
+
+                for (i = 0; i < 9; i++) {
+                    // 0 is blank, 10 is x, 1 is o
+                    dojo.byId("img" + i).src = "resources/" + data.board[i] + ".gif";
+                }
+                
+                var statusMsg;  
+                // -1 is unfinished, 0 is tie, 1 is X win, 2 is Y win
+                if (data.win == 0) {
+                    statusMsg = "It's a tie!";
+                } else if (data.win == 1) {
+                    statusMsg = "X wins!";
+                } else if (data.win == 2) {
+                    statusMsg = "O wins!";
+                } else if (data.win == -1 && data.turn == 10) {  
+                    statusMsg = "It's X's Turn";
+                } else if (data.win == -1 && data.turn == 1) {
+                    statusMsg = "It's O's Turn";
+                } else {
+                    statusMsg = "That's odd, it shouldn't get here";
+                }
+
+                if (iam == 0) {
+                    if (turnNum == 0) {
+                        dojo.byId("myrole").innerHTML = "You can join the game if you'd like.";
+                    } else {
+                        dojo.byId("myrole").innerHTML = "You are currently a spectator.";
+                    }
+                } else if (iam == X) {
+                    dojo.byId("myrole").innerHTML = "You are currently player 'X'.";
+                } else {
+                    dojo.byId("myrole").innerHTML = "You are currently player 'O'.";
+                }
+                
+                if (data.win != -1) {
+                    statusMsg = statusMsg + '<br><a href="ttt1.html">Restart the game</a>';
+                }
+        
+                // And write the status message out here -
+                dojo.byId("gstatus").innerHTML = statusMsg;
+
+                board = data.board;
+                winner = data.win;
+                turnNum = data.turnNum;
+            }
+
+            //return false if cell is an invalid move
+            function turn(cell) {
+                if (cell < 0 || cell > 8) return false; // invalid move
+                if (winner != -1) return false;
+                if (iam == 0) {
+                    if (iam == 0 && turnNum == 0) {
+                        iam = X;
+                    }
+                    else {
+                        iam = O;
+                    }
+                }
+                if (board[cell] != 0) {
+                    return false; // invalid move
+                }
+                turnNum++;
+                if (turnNum % 2 == 1) { //then X
+                    board[cell] = X;
+                } else { // else O
+                    board[cell] = O;
+                }
+                return true;
+            }
+
+            function whoseTurn() {
+                if (turnNum == 0 || turnNum % 2 == 0 ) {
+                    return X;
+                } else return O;
+            }
+
+    
+            function done() {
+                return (turnNum > 8);
+            }
+            
+            
+            // return -1 for no win, 0 for tie, 1 for x win, 2 for o win
+            function win() {
+                if (winner != -1) {
+                    return winner;
+                }
+                for (i = 0 ; i < 8; i++) {
+                    var winSum = board[wins[i][0]] + board[wins[i][1]] + board[wins[i][2]];
+                    if (winSum == 3) {
+                        winner = 2;
+                    } else if (winSum == 30) {
+                        winner = 1;
+                    }
+                }
+                if (winner == -1 && turnNum > 8) {
+                    winner = 0;
+                } 
+                return winner;
+            }
+ 
+
+            function initArrays() {
+
+                wins[0] = new Array(3);
+                wins[0][0] = 0;
+                wins[0][1] = 1;
+                wins[0][2] = 2;
+
+                wins[1] = new Array(3);
+                wins[1][0] = 3;
+                wins[1][1] = 4;
+                wins[1][2] = 5;
+
+                wins[2] = new Array(3);
+                wins[2][0] = 6;
+                wins[2][1] = 7;
+                wins[2][2] = 8;
+
+                wins[3] = new Array(3);
+                wins[3][0] = 0;
+                wins[3][1] = 3;
+                wins[3][2] = 6;
+
+                wins[4] = new Array(3);
+                wins[4][0] = 1;
+                wins[4][1] = 4;
+                wins[4][2] = 7;
+
+                wins[5] = new Array(3);
+                wins[5][0] = 2;
+                wins[5][1] = 5;
+                wins[5][2] = 8;
+
+                wins[6] = new Array(3);
+                wins[6][0] = 0;
+                wins[6][1] = 4;
+                wins[6][2] = 8;
+
+                wins[7] = new Array(3);
+                wins[7][0] = 2;
+                wins[7][1] = 4;
+                wins[7][2] = 6;
+
+                board[0] = 0;
+                board[1] = 0;
+                board[2] = 0;
+                board[3] = 0;
+                board[4] = 0;
+                board[5] = 0;
+                board[6] = 0;
+                board[7] = 0;
+                board[8] = 0;
+            }
+
+        </script>
+        
+    </head>
+    <body>
+        
+        <h1>Tic Tac Toe</h1>
+        <table>
+            <tr>
+                <td id="cell0"><img id="img0" src="resources/0.gif" 
+                                    onclick=postMe("0")></td>
+                <td id="cell1"><img id="img1" src="resources/0.gif" 
+                                    onclick=postMe("1")></td>
+                <td id="cell2"><img id="img2" src="resources/0.gif" 
+                                    onclick=postMe("2")></td>
+            </tr>
+            <tr>
+                <td id="cell3"><img id="img3" src="resources/0.gif" 
+                                    onclick=postMe("3")></td>
+                <td id="cell4"><img id="img4" src="resources/0.gif" 
+                                    onclick=postMe("4")></td>
+                <td id="cell5"><img id="img5" src="resources/0.gif" 
+                                    onclick=postMe("5")></td>
+            </tr>
+            <tr>
+                <td id="cell6"><img id="img6" src="resources/0.gif" 
+                                    onclick=postMe("6")></td>
+                <td id="cell7"><img id="img7" src="resources/0.gif" 
+                                    onclick=postMe("7")></td>
+                <td id="cell8"><img id="img8" src="resources/0.gif" 
+                                    onclick=postMe("8")></td>
+            </tr>      
+        </table>
+        <h2 id="gstatus">New game, click to start.</h2><br/>
+        <h2 id="myrole"></h2>
+    </body>
+</html>

Added: tomcat/trunk/webapps/cometd/examples/timesync/index.html
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/examples/timesync/index.html?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/examples/timesync/index.html (added)
+++ tomcat/trunk/webapps/cometd/examples/timesync/index.html Tue Sep  2 13:00:36 2008
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+    "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+    <head>
+        <title>Comet echo RPC</title>
+        <link rel="stylesheet" type="text/css" href="chat/chat.css"></link>
+        <script type="text/javascript" src="../dojo/dojo/dojo.js.uncompressed.js"></script>
+        <script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>
+        <script type="text/javascript">
+            dojo.require("dojox.cometd");
+            dojo.require("dojox.cometd.timesync");
+
+            dojox.cometd.init(new String(document.location).replace(/http:\/\/[^\/]*/,'').replace(/\/examples\/.*$/,'')+"/cometd");
+
+            var next=0;
+            var tick=function(){
+	    	var now=dojox.cometd.timesync.getServerDate();
+	    	dojo.byId("time").innerHTML=now.toUTCString();
+		
+		if (next==0)
+		    next=1000*(1.5+now.getTime()/1000).toFixed();
+		else
+		    next=next+1000;
+		dojox.cometd.timesync.setTimeout(tick,next+25);
+	    }
+
+            setTimeout(tick,1000);
+        </script>
+    </head>
+    <body>
+        
+        <h1>TimeSync test</h1>
+        <p>
+            This test uses the timesync extension to calculate a timeoffset with the server.
+	    The time displayed below is the server time and should be the same for all clients no matter what their local clocks are set to. 
+	    The accuracy should improve as each connect response is received.
+        </p>
+        <div id="time">
+        </div>
+    </body>
+</html>

Added: tomcat/trunk/webapps/cometd/index.html
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/cometd/index.html?rev=691359&view=auto
==============================================================================
--- tomcat/trunk/webapps/cometd/index.html (added)
+++ tomcat/trunk/webapps/cometd/index.html Tue Sep  2 13:00:36 2008
@@ -0,0 +1,7 @@
+
+<h1>Cometd demo</h1>
+
+<p>
+Try the <a href="examples/simplechat/cometdchat.htm">Simple Chat Demo</a>.</br>
+Try the <a href="examples/tictactoe/ttt1.htm">Tic-Tac-Toe Demo</a>.</br>
+</p>

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=691359&r1=691358&r2=691359&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Tue Sep  2 13:00:36 2008
@@ -33,7 +33,7 @@
 
 <body>
 <section name="Tomcat Trunk ">
-
+  
   <subsection name="ha">
     <changelog>
      <fix><rev>671650</rev><br/><bug>45279</bug><br/>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org