You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by jk...@apache.org on 2006/09/23 01:22:51 UTC

svn commit: r449122 [17/40] - in /tapestry/tapestry4/trunk/tapestry-framework/src: java/org/apache/tapestry/ java/org/apache/tapestry/dojo/ java/org/apache/tapestry/dojo/form/ java/org/apache/tapestry/dojo/html/ java/org/apache/tapestry/form/ java/org/...

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/ScriptSrcIO.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/ScriptSrcIO.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/ScriptSrcIO.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/ScriptSrcIO.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,458 @@
+/*
+	Copyright (c) 2004-2006, 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/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io.ScriptSrcIO");
+dojo.require("dojo.io.BrowserIO");
+dojo.require("dojo.undo.browser");
+
+//FIXME: should constantParams be JS object?
+//FIXME: check dojo.io calls. Can we move the BrowserIO defined calls somewhere
+//       else so that we don't depend on BrowserIO at all? The dependent calls
+//       have to do with dealing with forms and making query params from JS object.
+/**
+ * See test_ScriptSrcIO.html for usage information.
+ * Notes:
+ * - The watchInFlight timer is set to 100 ms instead of 10ms (which is what BrowserIO.js uses).
+ */
+dojo.io.ScriptSrcTransport = new function(){
+	this.preventCache = false; // if this is true, we'll always force GET requests to not cache
+	this.maxUrlLength = 1000; //Used to calculate if script request should be multipart.
+	this.inFlightTimer = null;
+
+	this.DsrStatusCodes = {
+		Continue: 100,
+		Ok: 200,
+		Error: 500
+	};
+
+	this.startWatchingInFlight = function(){
+		if(!this.inFlightTimer){
+			this.inFlightTimer = setInterval("dojo.io.ScriptSrcTransport.watchInFlight();", 100);
+		}
+	}
+
+	this.watchInFlight = function(){
+		var totalCount = 0;
+		var doneCount = 0;
+		for(var param in this._state){
+			totalCount++;
+			var currentState = this._state[param];
+			if(currentState.isDone){
+				doneCount++;
+				delete this._state[param];
+			}else if(!currentState.isFinishing){
+				var listener = currentState.kwArgs;
+				try{
+					if(currentState.checkString && eval("typeof(" + currentState.checkString + ") != 'undefined'")){
+						currentState.isFinishing = true;
+						this._finish(currentState, "load");
+						doneCount++;
+						delete this._state[param];
+					}else if(listener.timeoutSeconds && listener.timeout){
+						if(currentState.startTime + (listener.timeoutSeconds * 1000) < (new Date()).getTime()){
+							currentState.isFinishing = true;
+							this._finish(currentState, "timeout");
+							doneCount++;
+							delete this._state[param];
+						}
+					}else if(!listener.timeoutSeconds){
+						//Increment the done count if no timeout is specified, so
+						//that we turn off the timer if all that is left in the state
+						//list are things we can't clean up because they fail without
+						//getting a callback.
+						doneCount++;
+					}
+				}catch(e){
+					currentState.isFinishing = true;
+					this._finish(currentState, "error", {status: this.DsrStatusCodes.Error, response: e});
+				}
+			}
+		}
+	
+		if(doneCount >= totalCount){
+			clearInterval(this.inFlightTimer);
+			this.inFlightTimer = null;
+		}
+	}
+
+	this.canHandle = function(kwArgs){
+		return dojo.lang.inArray(["text/javascript", "text/json"], (kwArgs["mimetype"].toLowerCase()))
+			&& (kwArgs["method"].toLowerCase() == "get")
+			&& !(kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]))
+			&& (!kwArgs["sync"] || kwArgs["sync"] == false)
+			&& !kwArgs["file"]
+			&& !kwArgs["multipart"];
+	}
+
+	/**
+	 * Removes any script tags from the DOM that may have been added by ScriptSrcTransport.
+	 * Be careful though, by removing them from the script, you may invalidate some
+	 * script objects that were defined by the js file that was pulled in as the
+	 * src of the script tag. Test carefully if you decide to call this method.
+	 * 
+	 * In MSIE 6 (and probably 5.x), if you removed the script element while 
+	 * part of the script is still executing, the browser will crash.
+	 */
+	this.removeScripts = function(){
+		var scripts = document.getElementsByTagName("script");
+		for(var i = 0; scripts && i < scripts.length; i++){
+			var scriptTag = scripts[i];
+			if(scriptTag.className == "ScriptSrcTransport"){
+				var parent = scriptTag.parentNode;
+				parent.removeChild(scriptTag);
+				i--; //Set the index back one since we removed an item.
+			}
+		}
+	}
+
+	this.bind = function(kwArgs){
+		//START duplication from BrowserIO.js (some changes made)
+		var url = kwArgs.url;
+		var query = "";
+		
+		if(kwArgs["formNode"]){
+			var ta = kwArgs.formNode.getAttribute("action");
+			if((ta)&&(!kwArgs["url"])){ url = ta; }
+			var tp = kwArgs.formNode.getAttribute("method");
+			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
+			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
+		}
+
+		if(url.indexOf("#") > -1) {
+			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
+			url = url.split("#")[0];
+		}
+
+		//Break off the domain/path of the URL.
+		var urlParts = url.split("?");
+		if(urlParts && urlParts.length == 2){
+			url = urlParts[0];
+			query += (query ? "&" : "") + urlParts[1];
+		}
+
+		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
+			dojo.undo.browser.addToHistory(kwArgs);
+		}
+
+		//Create an ID for the request.
+		var id = kwArgs["apiId"] ? kwArgs["apiId"] : "id" + this._counter++;
+
+		//Fill out any other content pieces.
+		var content = kwArgs["content"];
+		var jsonpName = kwArgs.jsonParamName;
+		if(kwArgs.sendTransport || jsonpName) {
+			if (!content){
+				content = {};
+			}
+			if(kwArgs.sendTransport){
+				content["dojo.transport"] = "scriptsrc";
+			}
+
+			if(jsonpName){
+				content[jsonpName] = "dojo.io.ScriptSrcTransport._state." + id + ".jsonpCall";
+			}
+		}
+
+		if(kwArgs.postContent){
+			query = kwArgs.postContent;
+		}else if(content){
+			query += ((query) ? "&" : "") + dojo.io.argsFromMap(content, kwArgs.encoding, jsonpName);
+		}
+		//END duplication from BrowserIO.js
+
+		//START DSR
+
+		//If an apiId is specified, then we want to make sure useRequestId is true.
+		if(kwArgs["apiId"]){
+			kwArgs["useRequestId"] = true;
+		}
+
+		//Set up the state for this request.
+		var state = {
+			"id": id,
+			"idParam": "_dsrid=" + id,
+			"url": url,
+			"query": query,
+			"kwArgs": kwArgs,
+			"startTime": (new Date()).getTime(),
+			"isFinishing": false
+		};
+
+		if(!url){
+			//Error. An URL is needed.
+			this._finish(state, "error", {status: this.DsrStatusCodes.Error, statusText: "url.none"});
+			return;
+		}
+
+		//If this is a jsonp request, intercept the jsonp callback
+		if(content && content[jsonpName]){
+			state.jsonp = content[jsonpName];
+			state.jsonpCall = function(data){
+				if(data["Error"]||data["error"]){
+					if(dojo["json"] && dojo["json"]["serialize"]){
+						dojo.debug(dojo.json.serialize(data));
+					}
+					dojo.io.ScriptSrcTransport._finish(this, "error", data);
+				}else{
+					dojo.io.ScriptSrcTransport._finish(this, "load", data);
+				}
+			};
+		}
+
+		//Only store the request state on the state tracking object if a callback
+		//is expected or if polling on a checkString will be done.
+		if(kwArgs["useRequestId"] || kwArgs["checkString"] || state["jsonp"]){
+			this._state[id] = state;
+		}
+
+		//A checkstring is a string that if evaled will not be undefined once the
+		//script src loads. Used as an alternative to depending on a callback from
+		//the script file. If this is set, then multipart is not assumed to be used,
+		//since multipart requires a specific callback. With checkString we will be doing
+		//polling.
+		if(kwArgs["checkString"]){
+			state.checkString = kwArgs["checkString"];
+		}
+
+		//Constant params are parameters that should always be sent with each
+		//part of a multipart URL.
+		state.constantParams = (kwArgs["constantParams"] == null ? "" : kwArgs["constantParams"]);
+	
+		if(kwArgs["preventCache"] ||
+			(this.preventCache == true && kwArgs["preventCache"] != false)){
+			state.nocacheParam = "dojo.preventCache=" + new Date().valueOf();
+		}else{
+			state.nocacheParam = "";
+		}
+
+		//Get total length URL, if we were to do it as one URL.
+		//Add some padding, extra & separators.
+		var urlLength = state.url.length + state.query.length + state.constantParams.length 
+				+ state.nocacheParam.length + this._extraPaddingLength;
+
+		if(kwArgs["useRequestId"]){
+			urlLength += state.idParam.length;
+		}
+		
+		if(!kwArgs["checkString"] && kwArgs["useRequestId"] 
+			&& !state["jsonp"] && !kwArgs["forceSingleRequest"]
+			&& urlLength > this.maxUrlLength){
+			if(url > this.maxUrlLength){
+				//Error. The URL domain and path are too long. We can't
+				//segment that, so return an error.
+				this._finish(state, "error", {status: this.DsrStatusCodes.Error, statusText: "url.tooBig"});
+				return;
+			}else{
+				//Start the multiple requests.
+				this._multiAttach(state, 1);
+			}
+		}else{
+			//Send one URL.
+			var queryParams = [state.constantParams, state.nocacheParam, state.query];
+			if(kwArgs["useRequestId"] && !state["jsonp"]){
+				queryParams.unshift(state.idParam);
+			}
+			var finalUrl = this._buildUrl(state.url, queryParams);
+
+			//Track the final URL in case we need to use that instead of api ID when receiving
+			//the load callback.
+			state.finalUrl = finalUrl;
+			
+			this._attach(state.id, finalUrl);
+		}
+		//END DSR
+
+		this.startWatchingInFlight();
+	}
+	
+	//Private properties/methods
+	this._counter = 1;
+	this._state = {};
+	this._extraPaddingLength = 16;
+
+	//Is there a dojo function for this already?
+	this._buildUrl = function(url, nameValueArray){
+		var finalUrl = url;
+		var joiner = "?";
+		for(var i = 0; i < nameValueArray.length; i++){
+			if(nameValueArray[i]){
+				finalUrl += joiner + nameValueArray[i];
+				joiner = "&";
+			}
+		}
+
+		return finalUrl;
+	}
+
+	this._attach = function(id, url){
+		//Attach the script to the DOM.
+		var element = document.createElement("script");
+		element.type = "text/javascript";
+		element.src = url;
+		element.id = id;
+		element.className = "ScriptSrcTransport";
+		document.getElementsByTagName("head")[0].appendChild(element);
+	}
+
+	this._multiAttach = function(state, part){
+		//Check to make sure we still have a query to send up. This is mostly
+		//a protection from a goof on the server side when it sends a part OK
+		//response instead of a final response.
+		if(state.query == null){
+			this._finish(state, "error", {status: this.DsrStatusCodes.Error, statusText: "query.null"});
+			return;
+		}
+
+		if(!state.constantParams){
+			state.constantParams = "";
+		}
+
+		//How much of the query can we take?
+		//Add a padding constant to account for _part and a couple extra amperstands.
+		//Also add space for id since we'll need it now.
+		var queryMax = this.maxUrlLength - state.idParam.length
+					 - state.constantParams.length - state.url.length
+					 - state.nocacheParam.length - this._extraPaddingLength;
+		
+		//Figure out if this is the last part.
+		var isDone = state.query.length < queryMax;
+	
+		//Break up the query string if necessary.
+		var currentQuery;
+		if(isDone){
+			currentQuery = state.query;
+			state.query = null;
+		}else{
+			//Find the & or = nearest the max url length.
+			var ampEnd = state.query.lastIndexOf("&", queryMax - 1);
+			var eqEnd = state.query.lastIndexOf("=", queryMax - 1);
+
+			//See if & is closer, or if = is right at the edge,
+			//which means we should put it on the next URL.
+			if(ampEnd > eqEnd || eqEnd == queryMax - 1){
+				//& is nearer the end. So just chop off from there.
+				currentQuery = state.query.substring(0, ampEnd);
+				state.query = state.query.substring(ampEnd + 1, state.query.length) //strip off amperstand with the + 1.
+			}else{
+				//= is nearer the end. Take the max amount possible. 
+				currentQuery = state.query.substring(0, queryMax);
+			 
+				//Find the last query name in the currentQuery so we can prepend it to
+				//ampEnd. Could be -1 (not there), so account for that.
+				var queryName = currentQuery.substring((ampEnd == -1 ? 0 : ampEnd + 1), eqEnd);
+				state.query = queryName + "=" + state.query.substring(queryMax, state.query.length);
+			}
+		}
+		
+		//Now send a part of the script
+		var queryParams = [currentQuery, state.idParam, state.constantParams, state.nocacheParam];
+		if(!isDone){
+			queryParams.push("_part=" + part);
+		}
+
+		var url = this._buildUrl(state.url, queryParams);
+
+		this._attach(state.id + "_" + part, url);
+	}
+
+	this._finish = function(state, callback, event){
+		if(callback != "partOk" && !state.kwArgs[callback] && !state.kwArgs["handle"]){
+			//Ignore "partOk" because that is an internal callback.
+			if(callback == "error"){
+				state.isDone = true;
+				throw event;
+			}
+		}else{
+			switch(callback){
+				case "load":
+					var response = event ? event.response : null;
+					if(!response){
+						response = event;
+					}
+					state.kwArgs[(typeof state.kwArgs.load == "function") ? "load" : "handle"]("load", response, event, state.kwArgs);
+					state.isDone = true;
+					break;
+				case "partOk":
+					var part = parseInt(event.response.part, 10) + 1;
+					//Update the constant params, if any.
+					if(event.response.constantParams){
+						state.constantParams = event.response.constantParams;
+					}
+					this._multiAttach(state, part);
+					state.isDone = false;
+					break;
+				case "error":
+					state.kwArgs[(typeof state.kwArgs.error == "function") ? "error" : "handle"]("error", event.response, event, state.kwArgs);
+					state.isDone = true;
+					break;
+				default:
+					state.kwArgs[(typeof state.kwArgs[callback] == "function") ? callback : "handle"](callback, event, event, state.kwArgs);
+					state.isDone = true;
+			}
+		}
+	}
+
+	dojo.io.transports.addTransport("ScriptSrcTransport");
+}
+
+//Define callback handler.
+window.onscriptload = function(event){
+	var state = null;
+	var transport = dojo.io.ScriptSrcTransport;
+	
+	//Find the matching state object for event ID.
+	if(transport._state[event.id]){
+		state = transport._state[event.id];
+	}else{
+		//The ID did not match directly to an entry in the state list.
+		//Try searching the state objects for a matching original URL.
+		var tempState;
+		for(var param in transport._state){
+			tempState = transport._state[param];
+			if(tempState.finalUrl && tempState.finalUrl == event.id){
+				state = tempState;
+				break;
+			}
+		}
+
+		//If no matching original URL is found, then use the URL that was actually used
+		//in the SCRIPT SRC attribute.
+		if(state == null){
+			var scripts = document.getElementsByTagName("script");
+			for(var i = 0; scripts && i < scripts.length; i++){
+				var scriptTag = scripts[i];
+				if(scriptTag.getAttribute("class") == "ScriptSrcTransport"
+					&& scriptTag.src == event.id){
+					state = transport._state[scriptTag.id];
+					break;
+				}
+			}
+		}
+		
+		//If state is still null, then throw an error.
+		if(state == null){
+			throw "No matching state for onscriptload event.id: " + event.id;
+		}
+	}
+
+	var callbackName = "error";
+	switch(event.status){
+		case dojo.io.ScriptSrcTransport.DsrStatusCodes.Continue:
+			//A part of a multipart request.
+			callbackName = "partOk";
+			break;
+		case dojo.io.ScriptSrcTransport.DsrStatusCodes.Ok:
+			//Successful reponse.
+			callbackName = "load";
+			break;
+	}
+
+	transport._finish(state, callbackName, event);
+};

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/ScriptSrcIO.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/XhrIframeProxy.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/XhrIframeProxy.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/XhrIframeProxy.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/XhrIframeProxy.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,212 @@
+/*
+	Copyright (c) 2004-2006, 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/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io.XhrIframeProxy");
+dojo.provide("dojo.io.XhrIframeFacade");
+
+dojo.require("dojo.experimental");
+dojo.experimental("dojo.io.XhrIframeProxy");
+
+dojo.require("dojo.io.IframeIO");
+dojo.require("dojo.html.iframe");
+dojo.require("dojo.dom");
+dojo.require("dojo.uri.Uri");
+
+/*
+TODO: This page might generate a "loading unsecure items on a secure page"
+popup in browsers if it is served on a https URL, given that we are not
+setting a src on the iframe element.
+//TODO: Document that it doesn't work from local disk in Safari.
+
+*/
+
+dojo.io.XhrIframeProxy = new function(){
+	this.xipClientUrl = dojo.uri.dojoUri("src/io/xip_client.html");
+
+	this._state = {};
+	this._stateIdCounter = 0;
+
+	this.send = function(facade){		
+		var stateId = "XhrIframeProxy" + (this._stateIdCounter++);
+		facade._stateId = stateId;
+
+		this._state[stateId] = {
+			facade: facade,
+			stateId: stateId,
+			clientFrame: dojo.io.createIFrame(stateId,
+				"dojo.io.XhrIframeProxy.clientFrameLoaded('" + stateId + "');",
+				this.xipClientUrl)
+		};
+	}
+	
+	this.receive = function(stateId, urlEncodedData){
+		/* urlEncodedData should have the following params:
+				- responseHeaders
+				- status
+				- statusText
+				- responseText
+		*/
+		//Decode response data.
+		var response = {};
+		var nvPairs = urlEncodedData.split("&");
+		for(var i = 0; i < nvPairs.length; i++){
+			if(nvPairs[i]){
+				var nameValue = nvPairs[i].split("=");
+				response[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]);
+			}
+		}
+
+		//Set data on facade object.
+		var state = this._state[stateId];
+		var facade = state.facade;
+
+		facade._setResponseHeaders(response.responseHeaders);
+		if(response.status == 0 || response.status){
+			facade.status = parseInt(response.status, 10);
+		}
+		if(response.statusText){
+			facade.statusText = response.statusText;
+		}
+		if(response.responseText){
+			facade.responseText = response.responseText;
+			
+			//Fix responseXML.
+			var contentType = facade.getResponseHeader("Content-Type");
+			if(contentType && (contentType == "application/xml" || contentType == "text/xml")){
+				facade.responseXML = dojo.dom.createDocumentFromText(response.responseText, contentType);
+			}
+		}
+		facade.readyState = 4;
+		
+		this.destroyState(stateId);
+	}
+
+	this.clientFrameLoaded = function(stateId){
+		var state = this._state[stateId];
+		var facade = state.facade;
+		var clientWindow = dojo.html.iframeContentWindow(state.clientFrame);
+		
+		var reqHeaders = [];
+		for(var param in facade._requestHeaders){
+			reqHeaders.push(param + ": " + facade._requestHeaders[param]);
+		}
+		
+		var requestData = {
+			uri: facade._uri
+		};
+		if(reqHeaders.length > 0){
+			requestData.requestHeaders = reqHeaders.join("\r\n");		
+		}
+		if(facade._method){
+			requestData.method = facade._method;
+		}
+		if(facade._bodyData){
+			requestData.data = facade._bodyData;
+		}
+
+		clientWindow.send(stateId, facade._ifpServerUrl, dojo.io.argsFromMap(requestData, "utf8"));
+	}
+	
+	this.destroyState = function(stateId){
+		var state = this._state[stateId];
+		if(state){
+			delete this._state[stateId];
+			var parentNode = state.clientFrame.parentNode;
+			parentNode.removeChild(state.clientFrame);
+			state.clientFrame = null;
+			state = null;
+		}
+	}
+
+	this.createFacade = function(){
+		if(arguments && arguments[0] && arguments[0]["iframeProxyUrl"]){
+			return new dojo.io.XhrIframeFacade(arguments[0]["iframeProxyUrl"]);
+		}else{
+			return dojo.io.XhrIframeProxy.oldGetXmlhttpObject.apply(dojo.hostenv, arguments);
+		}
+	}
+}
+
+//Replace the normal XHR factory with the proxy one.
+dojo.io.XhrIframeProxy.oldGetXmlhttpObject = dojo.hostenv.getXmlhttpObject;
+dojo.hostenv.getXmlhttpObject = dojo.io.XhrIframeProxy.createFacade;
+
+/**
+	Using this a reference: http://www.w3.org/TR/XMLHttpRequest/
+
+	Does not implement the onreadystate callback since dojo.io.BrowserIO does
+	not use it.
+*/
+dojo.io.XhrIframeFacade = function(ifpServerUrl){
+	this._requestHeaders = {};
+	this._allResponseHeaders = null;
+	this._responseHeaders = {};
+	this._method = null;
+	this._uri = null;
+	this._bodyData = null;
+	this.responseText = null;
+	this.responseXML = null;
+	this.status = null;
+	this.statusText = null;
+	this.readyState = 0;
+	
+	this._ifpServerUrl = ifpServerUrl;
+	this._stateId = null;
+}
+
+dojo.lang.extend(dojo.io.XhrIframeFacade, {
+	//The open method does not properly reset since Dojo does not reuse XHR objects.
+	open: function(method, uri){
+		this._method = method;
+		this._uri = uri;
+
+		this.readyState = 1;
+	},
+	
+	setRequestHeader: function(header, value){
+		this._requestHeaders[header] = value;
+	},
+	
+	send: function(stringData){
+		this._bodyData = stringData;
+		
+		dojo.io.XhrIframeProxy.send(this);
+		
+		this.readyState = 2;
+	},
+	abort: function(){
+		dojo.io.XhrIframeProxy.destroyState(this._stateId);
+	},
+	
+	getAllResponseHeaders: function(){
+		return this._allResponseHeaders;
+
+	},
+	
+	getResponseHeader: function(header){
+		return this._responseHeaders[header];
+	},
+	
+	_setResponseHeaders: function(allHeaders){
+		if(allHeaders){
+			this._allResponseHeaders = allHeaders;
+			
+			//Make sure ther are now CR characters in the headers.
+			allHeaders = allHeaders.replace(/\r/g, "");
+			var nvPairs = allHeaders.split("\n");
+			for(var i = 0; i < nvPairs.length; i++){
+				if(nvPairs[i]){
+					var nameValue = nvPairs[i].split(": ");
+					this._responseHeaders[nameValue[0]] = nameValue[1];
+				}
+			}
+		}
+	}
+});

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/XhrIframeProxy.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/__package__.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/__package__.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/__package__.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/__package__.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,17 @@
+/*
+	Copyright (c) 2004-2006, 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/community/licensing.shtml
+*/
+
+dojo.kwCompoundRequire({
+	common: ["dojo.io.common"],
+	rhino: ["dojo.io.RhinoIO"],
+	browser: ["dojo.io.BrowserIO", "dojo.io.cookie"],
+	dashboard: ["dojo.io.BrowserIO", "dojo.io.cookie"]
+});
+dojo.provide("dojo.io.*");

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/__package__.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cometd.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cometd.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cometd.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cometd.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,926 @@
+/*
+	Copyright (c) 2004-2006, 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/community/licensing.shtml
+*/
+
+dojo.require("dojo.io.common"); // io/common.js provides setIFrameSrc and the IO namespace
+dojo.provide("dojo.io.cometd");
+dojo.provide("cometd");
+dojo.require("dojo.AdapterRegistry");
+dojo.require("dojo.json");
+dojo.require("dojo.io.BrowserIO"); // we need XHR for the handshake, etc.
+// FIXME: determine if we can use XMLHTTP to make x-domain posts despite not
+//        being able to hear back about the result
+dojo.require("dojo.io.IframeIO");
+dojo.require("dojo.io.ScriptSrcIO"); // for x-domain long polling
+dojo.require("dojo.io.cookie"); // for peering
+dojo.require("dojo.event.*");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.lang.func");
+
+/*
+ * this file defines Comet protocol client. Actual message transport is
+ * deferred to one of several connection type implementations. The default is a
+ * forever-frame implementation. A single global object named "cometd" is
+ * used to mediate for these connection types in order to provide a stable
+ * interface.
+ */
+
+// TODO: the auth handling in this file is a *mess*. It should probably live in
+// the cometd object with the ability to mix in or call down to an auth-handler
+// object, the prototypical variant of which is a no-op
+
+cometd = new function(){
+
+	this.initialized = false;
+	this.connected = false;
+
+	this.connectionTypes = new dojo.AdapterRegistry(true);
+
+	this.version = 0.1;
+	this.minimumVersion = 0.1;
+	this.clientId = null;
+
+	this.isXD = false;
+	this.handshakeReturn = null;
+	this.currentTransport = null;
+	this.url = null;
+	this.lastMessage = null;
+	this.globalTopicChannels = {};
+	this.backlog = [];
+
+	this.tunnelInit = function(childLocation, childDomain){
+		// placeholder
+	}
+
+	this.tunnelCollapse = function(){
+		dojo.debug("tunnel collapsed!");
+		// placeholder
+	}
+
+	this.init = function(props, root, bargs){
+		// 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";
+		// FIXME: do we just assume that the props knows
+		// everything we care about WRT to auth? Should we be trying to
+		// call back into it for subsequent auth actions? Should we fire
+		// local auth functions to ask for/get auth data?
+
+		// FIXME: what about ScriptSrcIO for x-domain comet?
+		this.url = root||djConfig["cometdRoot"];
+		if(!this.url){
+			dojo.debug("no cometd root specified in djConfig and no root passed");
+			return;
+		}
+		
+		// FIXME: we need to select a way to handle JSONP-style stuff
+		// generically here. We already know if the server is gonna be on
+		// another domain (or can know it), so we should select appropriate
+		// negotiation methods here as well as in final transport type
+		// selection.
+		var bindArgs = {
+			url: this.url,
+			method: "POST",
+			mimetype: "text/json",
+			load: dojo.lang.hitch(this, "finishInit"),
+			content: { "message": dojo.json.serialize([props]) }
+		};
+
+		// borrowed from dojo.uri.Uri in lieu of fixed host and port properties
+        var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
+		var r = (""+window.location).match(new RegExp(regexp));
+		if(r[4]){
+			var tmp = r[4].split(":");
+			var thisHost = tmp[0];
+			var thisPort = tmp[1]||"80"; // FIXME: match 443
+
+			r = this.url.match(new RegExp(regexp));
+			if(r[4]){
+				tmp = r[4].split(":");
+				var urlHost = tmp[0];
+				var urlPort = tmp[1]||"80";
+				if(	(urlHost != thisHost)||
+					(urlPort != thisPort) ){
+					dojo.debug(thisHost, urlHost);
+					dojo.debug(thisPort, urlPort);
+
+					this.isXD = true;
+					bindArgs.transport = "ScriptSrcTransport";
+					bindArgs.jsonParamName = "jsonp";
+				}
+			}
+		}
+		if(bargs){
+			dojo.lang.mixin(bindArgs, bargs);
+		}
+		return dojo.io.bind(bindArgs);
+	}
+
+	this.finishInit = function(type, data, evt, request){
+		this.handshakeReturn = data;
+		// pick a transport
+		if(data["authSuccessful"] == false){
+			dojo.debug("cometd authentication failed");
+			return;
+		}
+		if(data.version < this.minimumVersion){
+			dojo.debug("cometd protocol version mismatch. We wanted", this.minimumVersion, "but got", data.version);
+			return;
+		}
+		this.currentTransport = this.connectionTypes.match(
+			data.supportedConnectionTypes,
+			data.version,
+			this.isXD
+		);
+		this.currentTransport.version = data.version;
+		this.clientId = data.clientId;
+		this.tunnelInit = dojo.lang.hitch(this.currentTransport, "tunnelInit");
+		this.tunnelCollapse = dojo.lang.hitch(this.currentTransport, "tunnelCollapse");
+		this.initialized = true;
+		this.currentTransport.startup(data);
+		while(this.backlog.length != 0){
+			var cur = this.backlog.shift();
+			var fn = cur.shift();
+			this[fn].apply(this, cur);
+		}
+	}
+
+	this.getRandStr = function(){
+		return Math.random().toString().substring(2, 10);
+	}
+
+	// public API functions called by cometd or by the transport classes
+	this.deliver = function(messages){
+		dojo.lang.forEach(messages, this._deliver, this);
+	}
+
+	this._deliver = function(message){
+		// dipatch events along the specified path
+		if(!message["channel"]){
+			dojo.debug("cometd error: no channel for message!");
+			return;
+		}
+		if(!this.currentTransport){
+			this.backlog.push(["deliver", message]);
+			return;
+		}
+		this.lastMessage = message;
+		// check to see if we got a /meta channel message that we care about
+		if(	(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/subscribe":
+					if(!message.successful){
+						dojo.debug("cometd subscription error for channel", message.channel, ":", message.error);
+						return;
+					}
+					this.subscribed(message.subscription, message);
+					break;
+				case "/meta/unsubscribe":
+					if(!message.successful){
+						dojo.debug("cometd unsubscription error for channel", message.channel, ":", message.error);
+						return;
+					}
+					this.unsubscribed(message.subscription, message);
+					break;
+			}
+		}
+		// send the message down for processing by the transport
+		this.currentTransport.deliver(message);
+
+		// dispatch the message to any locally subscribed listeners
+		var tname = (this.globalTopicChannels[message.channel]) ? message.channel : "/cometd"+message.channel;
+		dojo.event.topic.publish(tname, message);
+	}
+
+	this.disconnect = function(){
+		if(!this.currentTransport){
+			dojo.debug("no current transport to disconnect from");
+			return;
+		}
+		this.currentTransport.disconnect();
+	}
+
+	// public API functions called by end users
+	this.publish = function(/*string*/channel, /*object*/data, /*object*/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
+		if(!this.currentTransport){
+			this.backlog.push(["publish", channel, data, properties]);
+			return;
+		}
+		var message = {
+			data: data,
+			channel: channel
+		};
+		if(properties){
+			dojo.lang.mixin(message, properties);
+		}
+		return this.currentTransport.sendMessage(message);
+	}
+
+	this.subscribe = function(	/*string*/				channel, 
+								/*boolean, optional*/	useLocalTopics, 
+								/*object, optional*/	objOrFunc, 
+								/*string, optional*/	funcName){ // return: boolean
+		// summary:
+		//		inform the server of this client's interest in channel
+		// channel:
+		//		name of the cometd channel to subscribe to
+		// useLocalTopics:
+		//		Determines if up a local event topic subscription to the passed
+		//		function using the channel name that was passed is constructed,
+		//		or if the topic name will be prefixed with some other
+		//		identifier for local message distribution. Setting this to
+		//		"true" is a good way to hook up server-sent message delivery to
+		//		pre-existing local topics.
+		// 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(!this.currentTransport){
+			this.backlog.push(["subscribe", channel, useLocalTopics, objOrFunc, funcName]);
+			return;
+		}
+		if(objOrFunc){
+			var tname = (useLocalTopics) ? channel : "/cometd"+channel;
+			if(useLocalTopics){
+				this.globalTopicChannels[channel] = true;
+			}
+			dojo.event.topic.subscribe(tname, objOrFunc, funcName);
+		}
+		// FIXME: would we handle queuing of the subscription if not connected?
+		// Or should the transport object?
+		return this.currentTransport.sendMessage({
+			channel: "/meta/subscribe",
+			subscription: channel
+		});
+	}
+
+	this.subscribed = function(	/*string*/				channel, 
+								/*obj*/					message){
+		dojo.debug(channel);
+		dojo.debugShallow(message);
+	}
+
+	this.unsubscribe = function(/*string*/				channel, 
+								/*boolean, optional*/	useLocalTopics, 
+								/*object, optional*/	objOrFunc, 
+								/*string, optional*/	funcName){ // return: boolean
+		// summary:
+		//		inform the server of this client's disinterest in channel
+		// channel:
+		//		name of the cometd channel to subscribe to
+		// useLocalTopics:
+		//		Determines if up a local event topic subscription to the passed
+		//		function using the channel name that was passed is destroyed,
+		//		or if the topic name will be prefixed with some other
+		//		identifier for stopping message distribution.
+		// 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
+		if(!this.currentTransport){
+			this.backlog.push(["unsubscribe", channel, useLocalTopics, objOrFunc, funcName]);
+			return;
+		}
+		//		a callback function to notifiy upon channel message delivery
+		if(objOrFunc){
+			// FIXME: should actual local topic unsubscription be delayed for
+			// successful unsubcribe notices from the other end? (guessing "no")
+			// FIXME: if useLocalTopics is false, should we go ahead and
+			// destroy the local topic?
+			var tname = (useLocalTopics) ? channel : "/cometd"+channel;
+			dojo.event.topic.unsubscribe(tname, objOrFunc, funcName);
+		}
+		return this.currentTransport.sendMessage({
+			channel: "/meta/unsubscribe",
+			subscription: channel
+		});
+	}
+
+	this.unsubscribed = function(/*string*/				channel, 
+								/*obj*/					message){
+		dojo.debug(channel);
+		dojo.debugShallow(message);
+	}
+
+	// FIXME: add an "addPublisher" function
+
+}
+
+/*
+transport objects MUST expose the following methods:
+	- check
+	- startup
+	- sendMessage
+	- 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.connected = false;
+	this.connectionId = null;
+	this.authToken = null;
+	this.lastTimestamp = null;
+	this.lastId = 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(this.connected){ return; }
+		// FIXME: fill in startup routine here
+		this.connected = true;
+	}
+
+	this.sendMessage = function(message){
+		// FIXME: fill in message sending logic
+	}
+
+	this.deliver = function(message){
+		if(message["timestamp"]){
+			this.lastTimestamp = message.timestamp;
+		}
+		if(message["id"]){
+			this.lastId = message.id;
+		}
+		if(	(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":
+			//		// FIXME: fill in logic here
+			//		break;
+			//	// case ...: ...
+			//	}
+		}
+	}
+
+	this.disconnect = function(){
+		if(!this.connected){ return; }
+		// FIXME: fill in shutdown routine here
+		this.connected = false;
+	}
+}
+cometd.connectionTypes.register("blah", cometd.blahTransport.check, cometd.blahTransport);
+*/
+
+cometd.iframeTransport = new function(){
+	this.connected = false;
+	this.connectionId = null;
+
+	this.rcvNode = null;
+	this.rcvNodeName = "";
+	this.phonyForm = null;
+	this.authToken = null;
+	this.lastTimestamp = null;
+	this.lastId = null;
+	this.backlog = [];
+
+	this.check = function(types, version, xdomain){
+		return ((!xdomain)&&
+				(!dojo.render.html.safari)&&
+				(dojo.lang.inArray(types, "iframe")));
+	}
+
+	this.tunnelInit = function(){
+		// we've gotten our initialization document back in the iframe, so
+		// now open up a connection and start passing data!
+		this.postToIframe({
+			message: dojo.json.serialize([
+				{
+					channel:	"/meta/connect",
+					clientId:	cometd.clientId,
+					connectionType: "iframe"
+					// FIXME: auth not passed here!
+					// "authToken": this.authToken
+				}
+			])
+		});
+	}
+
+	this.tunnelCollapse = function(){
+		if(this.connected){
+			// try to restart the tunnel
+			this.connected = false;
+
+			this.postToIframe({
+				message: dojo.json.serialize([
+					{
+						channel:	"/meta/reconnect",
+						clientId:	cometd.clientId,
+						connectionId:	this.connectionId,
+						timestamp:	this.lastTimestamp,
+						id:			this.lastId
+						// FIXME: no authToken provision!
+					}
+				])
+			});
+		}
+	}
+
+	this.deliver = function(message){
+		// handle delivery details that this transport particularly cares
+		// about. Most functions of should be handled by the main cometd object
+		// with only transport-specific details and state being tracked here.
+		if(message["timestamp"]){
+			this.lastTimestamp = message.timestamp;
+		}
+		if(message["id"]){
+			this.lastId = message.id;
+		}
+		// check to see if we got a /meta channel message that we care about
+		if(	(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){
+						dojo.debug("cometd connection error:", message.error);
+						return;
+					}
+					this.connectionId = message.connectionId;
+					this.connected = true;
+					this.processBacklog();
+					break;
+				case "/meta/reconnect":
+					if(!message.successful){
+						dojo.debug("cometd reconnection error:", message.error);
+						return;
+					}
+					this.connected = true;
+					break;
+				case "/meta/subscribe":
+					if(!message.successful){
+						dojo.debug("cometd subscription error for channel", message.channel, ":", message.error);
+						return;
+					}
+					// this.subscribed(message.channel);
+					dojo.debug(message.channel);
+					break;
+			}
+		}
+	}
+
+	this.widenDomain = function(domainStr){
+		// allow us to make reqests to the TLD
+		var cd = domainStr||document.domain;
+		if(cd.indexOf(".")==-1){ return; } // probably file:/// or localhost
+		var dps = cd.split(".");
+		if(dps.length<=2){ return; } // probably file:/// or an RFC 1918 address
+		dps = dps.slice(dps.length-2);
+		document.domain = dps.join(".");
+		return document.domain;
+	}
+
+	this.postToIframe = function(content, url){
+		if(!this.phonyForm){
+			if(dojo.render.html.ie){
+				this.phonyForm = document.createElement("<form enctype='application/x-www-form-urlencoded' method='POST' style='display: none;'>");
+				dojo.body().appendChild(this.phonyForm);
+			}else{
+				this.phonyForm = document.createElement("form");
+				this.phonyForm.style.display = "none"; // FIXME: will this still work?
+				dojo.body().appendChild(this.phonyForm);
+				this.phonyForm.enctype = "application/x-www-form-urlencoded";
+				this.phonyForm.method = "POST";
+			}
+		}
+
+		this.phonyForm.action = url||cometd.url;
+		this.phonyForm.target = this.rcvNodeName;
+		this.phonyForm.setAttribute("target", this.rcvNodeName);
+
+		while(this.phonyForm.firstChild){
+			this.phonyForm.removeChild(this.phonyForm.firstChild);
+		}
+
+		for(var x in content){
+			var tn;
+			if(dojo.render.html.ie){
+				tn = document.createElement("<input type='hidden' name='"+x+"' value='"+content[x]+"'>");
+				this.phonyForm.appendChild(tn);
+			}else{
+				tn = document.createElement("input");
+				this.phonyForm.appendChild(tn);
+				tn.type = "hidden";
+				tn.name = x;
+				tn.value = content[x];
+			}
+		}
+		this.phonyForm.submit();
+	}
+
+	this.processBacklog = function(){
+		while(this.backlog.length > 0){
+			this.sendMessage(this.backlog.shift(), true);
+		}
+	}
+
+	this.sendMessage = function(message, bypassBacklog){
+		// FIXME: what about auth fields?
+		if((bypassBacklog)||(this.connected)){
+			message.connectionId = this.connectionId;
+			message.clientId = cometd.clientId;
+			var bindArgs = {
+				url: cometd.url||djConfig["cometdRoot"],
+				method: "POST",
+				mimetype: "text/json",
+				// FIXME: we should be able to do better than this given that we're sending an array!
+				content: { message: dojo.json.serialize([ message ]) }
+			};
+			return dojo.io.bind(bindArgs);
+		}else{
+			this.backlog.push(message);
+		}
+	}
+
+	this.startup = function(handshakeData){
+		dojo.debug("startup!");
+		dojo.debug(dojo.json.serialize(handshakeData));
+
+		if(this.connected){ return; }
+
+		// this.widenDomain();
+
+		// NOTE: we require the server to cooperate by hosting
+		// cometdInit.html at the designated endpoint
+		this.rcvNodeName = "cometdRcv_"+cometd.getRandStr();
+		// the "forever frame" approach
+
+		var initUrl = cometd.url+"/?tunnelInit=iframe"; // &domain="+document.domain;
+		if(false && dojo.render.html.ie){ // FIXME: DISALBED FOR NOW
+			// use the "htmlfile hack" to prevent the background click junk
+			this.rcvNode = new ActiveXObject("htmlfile");
+			this.rcvNode.open();
+			this.rcvNode.write("<html>");
+			this.rcvNode.write("<script>document.domain = '"+document.domain+"'");
+			this.rcvNode.write("</html>");
+			this.rcvNode.close();
+
+			var ifrDiv = this.rcvNode.createElement("div");
+			this.rcvNode.appendChild(ifrDiv);
+			this.rcvNode.parentWindow.dojo = dojo;
+			ifrDiv.innerHTML = "<iframe src='"+initUrl+"'></iframe>"
+		}else{
+			this.rcvNode = dojo.io.createIFrame(this.rcvNodeName, "", initUrl);
+			// dojo.io.setIFrameSrc(this.rcvNode, initUrl);
+			// we're still waiting on the iframe to call back up to use and
+			// advertise that it's been initialized via tunnelInit
+		}
+	}
+}
+
+cometd.mimeReplaceTransport = new function(){
+	this.connected = false;
+	this.connectionId = null;
+	this.xhr = null;
+
+	this.authToken = null;
+	this.lastTimestamp = null;
+	this.lastId = null;
+	this.backlog = [];
+
+	this.check = function(types, version, xdomain){
+		return ((!xdomain)&&
+				(dojo.render.html.mozilla)&& // seems only Moz really supports this right now = (
+				(dojo.lang.inArray(types, "mime-message-block")));
+	}
+
+	this.tunnelInit = function(){
+		if(this.connected){ return; }
+		// FIXME: open up the connection here
+		this.openTunnelWith({
+			message: dojo.json.serialize([
+				{
+					channel:	"/meta/connect",
+					clientId:	cometd.clientId,
+					connectionType: "mime-message-block"
+					// FIXME: auth not passed here!
+					// "authToken": this.authToken
+				}
+			])
+		});
+		this.connected = true;
+	}
+
+	this.tunnelCollapse = function(){
+		if(this.connected){
+			// try to restart the tunnel
+			this.connected = false;
+			this.openTunnelWith({
+				message: dojo.json.serialize([
+					{
+						channel:	"/meta/reconnect",
+						clientId:	cometd.clientId,
+						connectionId:	this.connectionId,
+						timestamp:	this.lastTimestamp,
+						id:			this.lastId
+						// FIXME: no authToken provision!
+					}
+				])
+			});
+		}
+	}
+
+	this.deliver = cometd.iframeTransport.deliver;
+	// the logic appears to be the same
+
+	this.handleOnLoad = function(resp){
+		cometd.deliver(dojo.json.evalJson(this.xhr.responseText));
+	}
+
+	this.openTunnelWith = function(content, url){
+		// set up the XHR object and register the multipart callbacks
+		this.xhr = dojo.hostenv.getXmlhttpObject();
+		this.xhr.multipart = true; // FIXME: do Opera and Safari support this flag?
+		if(dojo.render.html.mozilla){
+			this.xhr.addEventListener("load", dojo.lang.hitch(this, "handleOnLoad"), false);
+		}else if(dojo.render.html.safari){
+			// Blah. WebKit doesn't actually populate responseText and/or responseXML. Useless.
+			dojo.debug("Webkit is broken with multipart responses over XHR = (");
+			this.xhr.onreadystatechange = dojo.lang.hitch(this, "handleOnLoad");
+		}else{
+			this.xhr.onload = dojo.lang.hitch(this, "handleOnLoad");
+		}
+		this.xhr.open("POST", (url||cometd.url), true); // async post
+		this.xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+		dojo.debug(dojo.json.serialize(content));
+		this.xhr.send(dojo.io.argsFromMap(content, "utf8"));
+	}
+
+	this.processBacklog = function(){
+		while(this.backlog.length > 0){
+			this.sendMessage(this.backlog.shift(), true);
+		}
+	}
+
+	this.sendMessage = function(message, bypassBacklog){
+		// FIXME: what about auth fields?
+		if((bypassBacklog)||(this.connected)){
+			message.connectionId = this.connectionId;
+			message.clientId = cometd.clientId;
+			var bindArgs = {
+				url: cometd.url||djConfig["cometdRoot"],
+				method: "POST",
+				mimetype: "text/json",
+				content: { message: dojo.json.serialize([ message ]) }
+			};
+			return dojo.io.bind(bindArgs);
+		}else{
+			this.backlog.push(message);
+		}
+	}
+
+	this.startup = function(handshakeData){
+		dojo.debugShallow(handshakeData);
+		if(this.connected){ return; }
+		this.tunnelInit();
+	}
+}
+
+cometd.longPollTransport = new function(){
+	this.connected = false;
+	this.connectionId = null;
+
+	this.authToken = null;
+	this.lastTimestamp = null;
+	this.lastId = null;
+	this.backlog = [];
+
+	this.check = function(types, version, xdomain){
+		return ((!xdomain)&&(dojo.lang.inArray(types, "long-polling")));
+	}
+
+	this.tunnelInit = function(){
+		if(this.connected){ return; }
+		// FIXME: open up the connection here
+		this.openTunnelWith({
+			message: dojo.json.serialize([
+				{
+					channel:	"/meta/connect",
+					clientId:	cometd.clientId,
+					connectionType: "long-polling"
+					// FIXME: auth not passed here!
+					// "authToken": this.authToken
+				}
+			])
+		});
+		this.connected = true;
+	}
+
+	this.tunnelCollapse = function(){
+		if(!this.connected){
+			// try to restart the tunnel
+			this.connected = false;
+			dojo.debug("clientId:", cometd.clientId);
+			this.openTunnelWith({
+				message: dojo.json.serialize([
+					{
+						channel:	"/meta/reconnect",
+						connectionType: "long-polling",
+						clientId:	cometd.clientId,
+						connectionId:	this.connectionId,
+						timestamp:	this.lastTimestamp,
+						id:			this.lastId
+						// FIXME: no authToken provision!
+					}
+				])
+			});
+		}
+	}
+
+	this.deliver = cometd.iframeTransport.deliver;
+	// the logic appears to be the same
+
+	this.openTunnelWith = function(content, url){
+		dojo.io.bind({
+			url: (url||cometd.url),
+			method: "post",
+			content: content,
+			mimetype: "text/json",
+			load: dojo.lang.hitch(this, function(type, data, evt, args){
+				// dojo.debug(evt.responseText);
+				cometd.deliver(data);
+				this.connected = false;
+				this.tunnelCollapse();
+			}),
+			error: function(){ dojo.debug("tunnel opening failed"); }
+		});
+		this.connected = true;
+	}
+
+	this.processBacklog = function(){
+		while(this.backlog.length > 0){
+			this.sendMessage(this.backlog.shift(), true);
+		}
+	}
+
+	this.sendMessage = function(message, bypassBacklog){
+		// FIXME: what about auth fields?
+		if((bypassBacklog)||(this.connected)){
+			message.connectionId = this.connectionId;
+			message.clientId = cometd.clientId;
+			var bindArgs = {
+				url: cometd.url||djConfig["cometdRoot"],
+				method: "post",
+				mimetype: "text/json",
+				content: { message: dojo.json.serialize([ message ]) }
+			};
+			return dojo.io.bind(bindArgs);
+		}else{
+			this.backlog.push(message);
+		}
+	}
+
+	this.startup = function(handshakeData){
+		if(this.connected){ return; }
+		this.tunnelInit();
+	}
+}
+
+cometd.callbackPollTransport = new function(){
+	this.connected = false;
+	this.connectionId = null;
+
+	this.authToken = null;
+	this.lastTimestamp = null;
+	this.lastId = null;
+	this.backlog = [];
+
+	this.check = function(types, version, xdomain){
+		// we handle x-domain!
+		return dojo.lang.inArray(types, "callback-polling");
+	}
+
+	this.tunnelInit = function(){
+		if(this.connected){ return; }
+		// FIXME: open up the connection here
+		this.openTunnelWith({
+			message: dojo.json.serialize([
+				{
+					channel:	"/meta/connect",
+					clientId:	cometd.clientId,
+					connectionType: "callback-polling"
+					// FIXME: auth not passed here!
+					// "authToken": this.authToken
+				}
+			])
+		});
+		this.connected = true;
+	}
+
+	this.tunnelCollapse = function(){
+		if(!this.connected){
+			// try to restart the tunnel
+			this.connected = false;
+			this.openTunnelWith({
+				message: dojo.json.serialize([
+					{
+						channel:	"/meta/reconnect",
+						connectionType: "long-polling",
+						clientId:	cometd.clientId,
+						connectionId:	this.connectionId,
+						timestamp:	this.lastTimestamp,
+						id:			this.lastId
+						// FIXME: no authToken provision!
+					}
+				])
+			});
+		}
+	}
+
+	this.deliver = cometd.iframeTransport.deliver;
+	// the logic appears to be the same
+
+	this.openTunnelWith = function(content, url){
+		// create a <script> element to generate the request
+		dojo.io.bind({
+			url: (url||cometd.url),
+			content: content,
+			transport: "ScriptSrcTransport",
+			jsonParamName: "jsonp",
+			load: dojo.lang.hitch(this, function(type, data, evt, args){
+				dojo.debug(dojo.json.serialize(data));
+				cometd.deliver(data);
+				this.connected = false;
+				this.tunnelCollapse();
+			}),
+			error: function(){ dojo.debug("tunnel opening failed"); }
+		});
+		this.connected = true;
+	}
+
+	this.processBacklog = function(){
+		while(this.backlog.length > 0){
+			this.sendMessage(this.backlog.shift(), true);
+		}
+	}
+
+	this.sendMessage = function(message, bypassBacklog){
+		// FIXME: what about auth fields?
+		if((bypassBacklog)||(this.connected)){
+			message.connectionId = this.connectionId;
+			message.clientId = cometd.clientId;
+			var bindArgs = {
+				url: cometd.url||djConfig["cometdRoot"],
+				transport: "ScriptSrcTransport",
+				jsonParamName: "jsonp",
+				content: { message: dojo.json.serialize([ message ]) }
+			};
+			return dojo.io.bind(bindArgs);
+		}else{
+			this.backlog.push(message);
+		}
+	}
+
+	this.startup = function(handshakeData){
+		if(this.connected){ return; }
+		this.tunnelInit();
+	}
+}
+
+cometd.connectionTypes.register("mime-message-block", cometd.mimeReplaceTransport.check, cometd.mimeReplaceTransport);
+cometd.connectionTypes.register("long-polling", cometd.longPollTransport.check, cometd.longPollTransport);
+cometd.connectionTypes.register("callback-polling", cometd.callbackPollTransport.check, cometd.callbackPollTransport);
+cometd.connectionTypes.register("iframe", cometd.iframeTransport.check, cometd.iframeTransport);
+
+// FIXME: need to implement fallback-polling, IE XML block
+
+dojo.io.cometd = cometd;

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cometd.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/common.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/common.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/common.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/common.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,376 @@
+/*
+	Copyright (c) 2004-2006, 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/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io.common");
+dojo.require("dojo.string");
+dojo.require("dojo.lang.extras");
+
+/******************************************************************************
+ *	Notes about dojo.io design:
+ *	
+ *	The dojo.io.* package has the unenviable task of making a lot of different
+ *	types of I/O feel natural, despite a universal lack of good (or even
+ *	reasonable!) I/O capability in the host environment. So lets pin this down
+ *	a little bit further.
+ *
+ *	Rhino:
+ *		perhaps the best situation anywhere. Access to Java classes allows you
+ *		to do anything one might want in terms of I/O, both synchronously and
+ *		async. Can open TCP sockets and perform low-latency client/server
+ *		interactions. HTTP transport is available through Java HTTP client and
+ *		server classes. Wish it were always this easy.
+ *
+ *	xpcshell:
+ *		XPCOM for I/O. A cluster-fuck to be sure.
+ *
+ *	spidermonkey:
+ *		S.O.L.
+ *
+ *	Browsers:
+ *		Browsers generally do not provide any useable filesystem access. We are
+ *		therefore limited to HTTP for moving information to and from Dojo
+ *		instances living in a browser.
+ *
+ *		XMLHTTP:
+ *			Sync or async, allows reading of arbitrary text files (including
+ *			JS, which can then be eval()'d), writing requires server
+ *			cooperation and is limited to HTTP mechanisms (POST and GET).
+ *
+ *		<iframe> hacks:
+ *			iframe document hacks allow browsers to communicate asynchronously
+ *			with a server via HTTP POST and GET operations. With significant
+ *			effort and server cooperation, low-latency data transit between
+ *			client and server can be acheived via iframe mechanisms (repubsub).
+ *
+ *		SVG:
+ *			Adobe's SVG viewer implements helpful primitives for XML-based
+ *			requests, but receipt of arbitrary text data seems unlikely w/o
+ *			<![CDATA[]]> sections.
+ *
+ *
+ *	A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
+ *	the IO API interface. A transcript of it can be found at:
+ *		http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
+ *	
+ *	Also referenced in the design of the API was the DOM 3 L&S spec:
+ *		http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
+ ******************************************************************************/
+
+// a map of the available transport options. Transports should add themselves
+// by calling add(name)
+dojo.io.transports = [];
+dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now
+
+dojo.io.Request = function(url, mimetype, transport, changeUrl){
+	if((arguments.length == 1)&&(arguments[0].constructor == Object)){
+		this.fromKwArgs(arguments[0]);
+	}else{
+		this.url = url;
+		if(mimetype){ this.mimetype = mimetype; }
+		if(transport){ this.transport = transport; }
+		if(arguments.length >= 4){ this.changeUrl = changeUrl; }
+	}
+}
+
+dojo.lang.extend(dojo.io.Request, {
+
+	/** The URL to hit */
+	url: "",
+	
+	/** The mime type used to interrpret the response body */
+	mimetype: "text/plain",
+	
+	/** The HTTP method to use */
+	method: "GET",
+	
+	/** An Object containing key-value pairs to be included with the request */
+	content: undefined, // Object
+	
+	/** The transport medium to use */
+	transport: undefined, // String
+	
+	/** If defined the URL of the page is physically changed */
+	changeUrl: undefined, // String
+	
+	/** A form node to use in the request */
+	formNode: undefined, // HTMLFormElement
+	
+	/** Whether the request should be made synchronously */
+	sync: false,
+	
+	bindSuccess: false,
+
+	/** Cache/look for the request in the cache before attempting to request?
+	 *  NOTE: this isn't a browser cache, this is internal and would only cache in-page
+	 */
+	useCache: false,
+
+	/** Prevent the browser from caching this by adding a query string argument to the URL */
+	preventCache: false,
+	
+	// events stuff
+	load: function(type, data, evt){ },
+	error: function(type, error){ },
+	timeout: function(type){ },
+	handle: function(){ },
+
+	//FIXME: change BrowserIO.js to use timeouts? IframeIO?
+	// The number of seconds to wait until firing a timeout callback.
+	// If it is zero, that means, don't do a timeout check.
+	timeoutSeconds: 0,
+	
+	// the abort method needs to be filled in by the transport that accepts the
+	// bind() request
+	abort: function(){ },
+	
+	// backButton: function(){ },
+	// forwardButton: function(){ },
+
+	fromKwArgs: function(kwArgs){
+		// normalize args
+		if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
+		if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
+		if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
+			kwArgs.method = kwArgs["formNode"].method;
+		}
+		
+		// backwards compatibility
+		if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
+		if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
+		if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }
+
+		// encoding fun!
+		kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");
+
+		kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);
+
+		var isFunction = dojo.lang.isFunction;
+		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
+			var fn = dojo.io.hdlrFuncNames[x];
+			if(kwArgs[fn] && isFunction(kwArgs[fn])){ continue; }
+			if(kwArgs["handle"] && isFunction(kwArgs["handle"])){
+				kwArgs[fn] = kwArgs.handle;
+			}
+			// handler is aliased above, shouldn't need this check
+			/* else if(dojo.lang.isObject(kwArgs.handler)){
+				if(isFunction(kwArgs.handler[fn])){
+					kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
+				}
+			}*/
+		}
+		dojo.lang.mixin(this, kwArgs);
+	}
+
+});
+
+dojo.io.Error = function(msg, type, num){
+	this.message = msg;
+	this.type =  type || "unknown"; // must be one of "io", "parse", "unknown"
+	this.number = num || 0; // per-substrate error number, not normalized
+}
+
+dojo.io.transports.addTransport = function(name){
+	this.push(name);
+	// FIXME: do we need to handle things that aren't direct children of the
+	// dojo.io namespace? (say, dojo.io.foo.fooTransport?)
+	this[name] = dojo.io[name];
+}
+
+// binding interface, the various implementations register their capabilities
+// and the bind() method dispatches
+dojo.io.bind = function(request){
+	// if the request asks for a particular implementation, use it
+	if(!(request instanceof dojo.io.Request)){
+		try{
+			request = new dojo.io.Request(request);
+		}catch(e){ dojo.debug(e); }
+	}
+	var tsName = "";
+	if(request["transport"]){
+		tsName = request["transport"];
+		// FIXME: it would be good to call the error handler, although we'd
+		// need to use setTimeout or similar to accomplish this and we can't
+		// garuntee that this facility is available.
+		if(!this[tsName]){ return request; }
+	}else{
+		// otherwise we do our best to auto-detect what available transports
+		// will handle 
+		for(var x=0; x<dojo.io.transports.length; x++){
+			var tmp = dojo.io.transports[x];
+			if((this[tmp])&&(this[tmp].canHandle(request))){
+				tsName = tmp;
+			}
+		}
+		if(tsName == ""){ return request; }
+	}
+	this[tsName].bind(request);
+	request.bindSuccess = true;
+	return request;
+}
+
+dojo.io.queueBind = function(request){
+	if(!(request instanceof dojo.io.Request)){
+		try{
+			request = new dojo.io.Request(request);
+		}catch(e){ dojo.debug(e); }
+	}
+
+	// make sure we get called if/when we get a response
+	var oldLoad = request.load;
+	request.load = function(){
+		dojo.io._queueBindInFlight = false;
+		var ret = oldLoad.apply(this, arguments);
+		dojo.io._dispatchNextQueueBind();
+		return ret;
+	}
+
+	var oldErr = request.error;
+	request.error = function(){
+		dojo.io._queueBindInFlight = false;
+		var ret = oldErr.apply(this, arguments);
+		dojo.io._dispatchNextQueueBind();
+		return ret;
+	}
+
+	dojo.io._bindQueue.push(request);
+	dojo.io._dispatchNextQueueBind();
+	return request;
+}
+
+dojo.io._dispatchNextQueueBind = function(){
+	if(!dojo.io._queueBindInFlight){
+		dojo.io._queueBindInFlight = true;
+		if(dojo.io._bindQueue.length > 0){
+			dojo.io.bind(dojo.io._bindQueue.shift());
+		}else{
+			dojo.io._queueBindInFlight = false;
+		}
+	}
+}
+dojo.io._bindQueue = [];
+dojo.io._queueBindInFlight = false;
+
+dojo.io.argsFromMap = function(map, encoding, last){
+	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
+	var mapped = [];
+	var control = new Object();
+	for(var name in map){
+		var domap = function(elt){
+			var val = enc(name)+"="+enc(elt);
+			mapped[(last == name) ? "push" : "unshift"](val);
+		}
+		if(!control[name]){
+			var value = map[name];
+			// FIXME: should be isArrayLike?
+			if (dojo.lang.isArray(value)){
+				dojo.lang.forEach(value, domap);
+			}else{
+				domap(value);
+			}
+		}
+	}
+	return mapped.join("&");
+}
+
+dojo.io.setIFrameSrc = function(iframe, src, replace){
+	try{
+		var r = dojo.render.html;
+		// dojo.debug(iframe);
+		if(!replace){
+			if(r.safari){
+				iframe.location = src;
+			}else{
+				frames[iframe.name].location = src;
+			}
+		}else{
+			// Fun with DOM 0 incompatibilities!
+			var idoc;
+			if(r.ie){
+				idoc = iframe.contentWindow.document;
+			}else if(r.safari){
+				idoc = iframe.document;
+			}else{ //  if(r.moz){
+				idoc = iframe.contentWindow;
+			}
+
+			//For Safari (at least 2.0.3) and Opera, if the iframe
+			//has just been created but it doesn't have content
+			//yet, then iframe.document may be null. In that case,
+			//use iframe.location and return.
+			if(!idoc){
+				iframe.location = src;
+				return;
+			}else{
+				idoc.location.replace(src);
+			}
+		}
+	}catch(e){ 
+		dojo.debug(e); 
+		dojo.debug("setIFrameSrc: "+e); 
+	}
+}
+
+/*
+dojo.io.sampleTranport = new function(){
+	this.canHandle = function(kwArgs){
+		// canHandle just tells dojo.io.bind() if this is a good transport to
+		// use for the particular type of request.
+		if(	
+			(
+				(kwArgs["mimetype"] == "text/plain") ||
+				(kwArgs["mimetype"] == "text/html") ||
+				(kwArgs["mimetype"] == "text/javascript")
+			)&&(
+				(kwArgs["method"] == "get") ||
+				( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
+			)
+		){
+			return true;
+		}
+
+		return false;
+	}
+
+	this.bind = function(kwArgs){
+		var hdlrObj = {};
+
+		// set up a handler object
+		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
+			var fn = dojo.io.hdlrFuncNames[x];
+			if(typeof kwArgs.handler == "object"){
+				if(typeof kwArgs.handler[fn] == "function"){
+					hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
+				}
+			}else if(typeof kwArgs[fn] == "function"){
+				hdlrObj[fn] = kwArgs[fn];
+			}else{
+				hdlrObj[fn] = kwArgs["handle"]||function(){};
+			}
+		}
+
+		// build a handler function that calls back to the handler obj
+		var hdlrFunc = function(evt){
+			if(evt.type == "onload"){
+				hdlrObj.load("load", evt.data, evt);
+			}else if(evt.type == "onerr"){
+				var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
+				hdlrObj.error("error", errObj);
+			}
+		}
+
+		// the sample transport would attach the hdlrFunc() when sending the
+		// request down the pipe at this point
+		var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
+		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
+	}
+
+	dojo.io.transports.addTransport("sampleTranport");
+}
+*/

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/common.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cookie.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cookie.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cookie.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cookie.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,108 @@
+/*
+	Copyright (c) 2004-2006, 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/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io.cookie");
+
+dojo.io.cookie.setCookie = function(name, value, days, path, domain, secure) {
+	var expires = -1;
+	if(typeof days == "number" && days >= 0) {
+		var d = new Date();
+		d.setTime(d.getTime()+(days*24*60*60*1000));
+		expires = d.toGMTString();
+	}
+	value = escape(value);
+	document.cookie = name + "=" + value + ";"
+		+ (expires != -1 ? " expires=" + expires + ";" : "")
+		+ (path ? "path=" + path : "")
+		+ (domain ? "; domain=" + domain : "")
+		+ (secure ? "; secure" : "");
+}
+
+dojo.io.cookie.set = dojo.io.cookie.setCookie;
+
+dojo.io.cookie.getCookie = function(name) {
+	// FIXME: Which cookie should we return?
+	//        If there are cookies set for different sub domains in the current
+	//        scope there could be more than one cookie with the same name.
+	//        I think taking the last one in the list takes the one from the
+	//        deepest subdomain, which is what we're doing here.
+	var idx = document.cookie.lastIndexOf(name+'=');
+	if(idx == -1) { return null; }
+	var value = document.cookie.substring(idx+name.length+1);
+	var end = value.indexOf(';');
+	if(end == -1) { end = value.length; }
+	value = value.substring(0, end);
+	value = unescape(value);
+	return value;
+}
+
+dojo.io.cookie.get = dojo.io.cookie.getCookie;
+
+dojo.io.cookie.deleteCookie = function(name) {
+	dojo.io.cookie.setCookie(name, "-", 0);
+}
+
+dojo.io.cookie.setObjectCookie = function(name, obj, days, path, domain, secure, clearCurrent) {
+	if(arguments.length == 5) { // for backwards compat
+		clearCurrent = domain;
+		domain = null;
+		secure = null;
+	}
+	var pairs = [], cookie, value = "";
+	if(!clearCurrent) { cookie = dojo.io.cookie.getObjectCookie(name); }
+	if(days >= 0) {
+		if(!cookie) { cookie = {}; }
+		for(var prop in obj) {
+			if(prop == null) {
+				delete cookie[prop];
+			} else if(typeof obj[prop] == "string" || typeof obj[prop] == "number") {
+				cookie[prop] = obj[prop];
+			}
+		}
+		prop = null;
+		for(var prop in cookie) {
+			pairs.push(escape(prop) + "=" + escape(cookie[prop]));
+		}
+		value = pairs.join("&");
+	}
+	dojo.io.cookie.setCookie(name, value, days, path, domain, secure);
+}
+
+dojo.io.cookie.getObjectCookie = function(name) {
+	var values = null, cookie = dojo.io.cookie.getCookie(name);
+	if(cookie) {
+		values = {};
+		var pairs = cookie.split("&");
+		for(var i = 0; i < pairs.length; i++) {
+			var pair = pairs[i].split("=");
+			var value = pair[1];
+			if( isNaN(value) ) { value = unescape(pair[1]); }
+			values[ unescape(pair[0]) ] = value;
+		}
+	}
+	return values;
+}
+
+dojo.io.cookie.isSupported = function() {
+	if(typeof navigator.cookieEnabled != "boolean") {
+		dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__",
+			"CookiesAllowed", 90, null);
+		var cookieVal = dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__");
+		navigator.cookieEnabled = (cookieVal == "CookiesAllowed");
+		if(navigator.cookieEnabled) {
+			// FIXME: should we leave this around?
+			this.deleteCookie("__TestingYourBrowserForCookieSupport__");
+		}
+	}
+	return navigator.cookieEnabled;
+}
+
+// need to leave this in for backwards-compat from 0.1 for when it gets pulled in by dojo.io.*
+if(!dojo.io.cookies) { dojo.io.cookies = dojo.io.cookie; }

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/cookie.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_client.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_client.html?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_client.html (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_client.html Fri Sep 22 16:22:30 2006
@@ -0,0 +1,200 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
+	<script type="text/javascript">
+	// <!--
+	/*
+	This file is really focused on just sending one message to the server, and
+	receiving one response. The code does not expect to be re-used for multiple messages.
+	This might be reworked later if performance indicates a need for it.
+	
+	xip fragment identifier/hash values have the form:
+	#id:cmd:realUrlEncodedMessage
+
+	id: some ID that should be unique among messages. No inherent meaning,
+	        just something to make sure the hash value is unique so the message
+	        receiver knows a new message is available.
+	        
+	cmd: command to the receiver. Valid values are:
+	         - loaded: the remote frame is loaded. Only sent from server to client.
+	         - ok: the message that this page sent was received OK. The next message may
+	               now be sent.
+	         - start: the start message of a block of messages (a complete message may
+	                  need to be segmented into many messages to get around the limitiations
+	                  of the size of an URL that a browser accepts.
+	         - part: indicates this is a part of a message.
+	         - end: the end message of a block of messages. The message can now be acted upon.
+	                If the message is small enough that it doesn't need to be segmented, then
+	                just one hash value message can be sent with "end" as the command.
+	
+	To reassemble a segmented message, the realUrlEncodedMessage parts just have to be concatenated
+	together.
+	*/
+
+	//Choosing 1024 as an arbitrary limit for the URL sizes.
+	//Anecdotal info seems to indicate this is safe to use in all
+	//modern browsers.
+	xipUrlLimit = 1024;
+	xipIdCounter = 1;
+
+	function xipInit(){
+		xipIsSending = false;
+		xipServerUrl = null;
+		xipStateId = null;
+		xipRequestData = null;
+		xipCurrentHash = "";
+		xipResponseMessage = "";
+		xipRequestParts = [];
+		xipPartIndex = 0;
+		xipServerWindow = null;
+	}
+	xipInit();
+	
+	function send(stateId, ifpServerUrl, urlEncodedData){
+		if(!xipIsSending){
+			xipIsSending = true;
+
+			xipStateId = stateId;
+			xipRequestData = urlEncodedData || "";
+
+			//Modify the server URL if it is a local path and 
+			//This is done for local/same domain testing.
+			xipServerUrl = ifpServerUrl;
+			if(ifpServerUrl.indexOf("..") == 0){
+				var parts = ifpServerUrl.split("/");
+				xipServerUrl = parts[parts.length - 1];
+			}
+
+			//Fix server URL to tell it about this page's URL. So that it can call us
+			//back correctly. Use the fragment identifier to allow for caching of the server
+			//page, and hey, we're using the fragment identifier for everything else.
+			ifpServerUrl += "#" + encodeURIComponent(window.location);	
+
+			//Start counter to inspect hash value.
+			setInterval(pollHash, 10);
+
+			//Loader server iframe, then wait for the server page to call us back.
+			xipServerWindow = frames["xipServerFrame"];
+			if (!xipServerWindow){
+				xipServerWindow = document.getElementById("xipServerFrame").contentWindow;
+			}
+
+			xipServerWindow.location.replace(ifpServerUrl);
+		}
+	}
+
+	function pollHash(){
+		//Can't use location.hash because at least Firefox does a decodeURIComponent on it.
+		var urlParts = window.location.href.split("#");
+		if(urlParts.length == 2){
+			var newHash = urlParts[1];
+			if(newHash != xipCurrentHash){
+				try{
+					messageReceived(newHash);
+				}catch(e){
+					//Make sure to not keep processing the error hash value.
+					xipCurrentHash = newHash;
+					throw e;
+				}
+				xipCurrentHash = newHash;
+			}
+		}
+	}
+
+	function messageReceived(urlEncodedMessage){
+		//Split off xip header.
+		var parts = urlEncodedMessage.split(":");
+		var command = parts[1];
+		urlEncodedMessage = parts[2] || "";
+		
+		switch(command){
+			case "loaded":
+				sendRequestStart();
+				break;
+			case "ok":
+				sendRequestPart();
+				break;
+			case "start":
+				xipResponseMessage = "";
+				xipResponseMessage += urlEncodedMessage;
+				setServerUrl("ok");
+				break;
+			case "part":
+				xipResponseMessage += urlEncodedMessage;			
+				setServerUrl("ok");
+				break;
+			case "end":
+				setServerUrl("ok");
+				xipResponseMessage += urlEncodedMessage;
+				parent.dojo.io.XhrIframeProxy.receive(xipStateId, xipResponseMessage);
+				break;
+		}
+	}
+	
+	function sendRequestStart(){
+		//Break the message into parts, if necessary.
+		xipRequestParts = [];
+		var reqData = xipRequestData;
+		var urlLength = xipServerUrl.length;
+		var partLength = xipUrlLimit - urlLength;
+		var reqIndex = 0;
+
+		while((reqData.length - reqIndex) + urlLength > xipUrlLimit){
+			xipRequestParts.push(reqData.substring(reqIndex, reqIndex + partLength));
+			reqIndex += partLength;
+		}
+		xipRequestParts.push(reqData.substring(reqIndex, reqData.length));
+		
+		xipPartIndex = 0;
+		sendRequestPart();
+		
+	}
+	
+	function sendRequestPart(){
+		if(xipPartIndex < xipRequestParts.length){
+			//Get the message part.
+			var partData = xipRequestParts[xipPartIndex];
+
+			//Get the command.
+			var cmd = "part";
+			if(xipPartIndex + 1 == xipRequestParts.length){
+				cmd = "end";
+			}else if (xipPartIndex == 0){
+				cmd = "start";
+			}
+			
+			setServerUrl(cmd, partData);
+			xipPartIndex++;
+		}
+	}
+	
+	function setServerUrl(cmd, message){
+		var serverUrl = xipServerUrl + "#" + (xipIdCounter++) + ":" + cmd;
+		if(message){
+			serverUrl += ":" + message;
+		}
+
+		//Safari won't let us replace across domains.
+		if(navigator.userAgent.indexOf("Safari") == -1){
+			xipServerWindow.location.replace(serverUrl);
+		}else{
+			xipServerWindow.location = serverUrl;
+		}
+
+	}
+	// -->
+	</script>
+</head>
+<body>
+	<h4>The Dojo Toolkit -- xip_client.html</h4>
+
+	<p>This file is used for Dojo's XMLHttpRequest Iframe Proxy. This is the "client" file used
+	internally by dojo.io.XhrIframeProxy.</p>
+	
+	<iframe id="xipServerFrame"></iframe>
+</body>
+</html>

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_client.html
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_server.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_server.html?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_server.html (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_server.html Fri Sep 22 16:22:30 2006
@@ -0,0 +1,337 @@
+<!--
+	/*
+		Copyright (c) 2004-2006, 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/community/licensing.shtml
+	*/
+	Pieces taken from Dojo source to make this file stand-alone
+-->
+<html>
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
+	<script type="text/javascript" src="isAllowed.js"></script>
+	<!--
+	BY DEFAULT THIS FILE DOES NOT WORK SO THAT YOU DON'T ACCIDENTALLY EXPOSE
+	ALL OF YOUR XHR-ENABLED SERVICES ON YOUR SITE. 
+	
+	In order for this file to work, you should define a function with the following signature:
+	
+	function isAllowedRequest(request){
+		return false;	
+	}
+	
+	Return true out of the function if you want to allow the cross-domain request.
+	
+	DON'T DEFINE THIS FUNCTION IN THIS FILE! Define it in a separate file called isAllowed.js
+	and include it in this page with a script tag that has a src attribute pointing to the file.
+	See the very first script tag in this file for an example. You do not have to place the
+	script file in the same directory as this file, just update the path above if you move it
+	somewhere else.
+	
+	Customize the isAllowedRequest function to restrict what types of requests are allowed
+	for this server. The request object has the following properties:
+	- requestHeaders: an object with the request headers that are to be added to
+	                  the XHR request.
+	- method: the HTTP method (GET, POST, etc...)
+	- uri: The URI for the request.
+	- data: The URL-encoded data for the request. For a GET request, this would
+	        be the querystring parameters. For a POST request, it wll be the
+	        body data.
+	-->
+	<script type="text/javascript">
+	// <!--
+	/*
+	See xip_client.html for more info on the xip fragment identifier protocol.
+	
+	This page uses Dojo to do the actual XMLHttpRequest (XHR) to the server, but you could
+	replace the Dojo references with your own XHR code if you like. But keep the other xip
+	code to communicate back to the xip client frame.
+	*/
+	djConfig = {
+		parseWidgets: false,
+		baseScriptUri: "./"
+	}
+	// -->
+	</script>
+	<script type="text/javascript">
+		dojo = {};
+		dojo.hostenv = {};
+		// These are in order of decreasing likelihood; this will change in time.
+		dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
+		
+		dojo.hostenv.getXmlhttpObject = function(){
+				var http = null;
+			var last_e = null;
+			try{ http = new XMLHttpRequest(); }catch(e){}
+				if(!http){
+				for(var i=0; i<3; ++i){
+					var progid = dojo.hostenv._XMLHTTP_PROGIDS[i];
+					try{
+						http = new ActiveXObject(progid);
+					}catch(e){
+						last_e = e;
+					}
+		
+					if(http){
+						dojo.hostenv._XMLHTTP_PROGIDS = [progid];  // so faster next time
+						break;
+					}
+				}
+		
+				/*if(http && !http.toString) {
+					http.toString = function() { "[object XMLHttpRequest]"; }
+				}*/
+			}
+		
+			if(!http){
+				throw "xip_server.html: XMLHTTP not available: " + last_e;
+			}
+		
+			return http;
+		}
+
+		dojo.setHeaders = function(http, headers){
+			if(headers) {
+				for(var header in headers) {
+					var headerValue = headers[header];
+					http.setRequestHeader(header, headerValue);
+				}
+			}
+		}
+
+	//Choosing 1024 as an arbitrary limit for the URL sizes.
+	//Anecdotal info seems to indicate this is safe to use in all
+	//modern browsers.
+	xipUrlLimit = 1024;
+	xipIdCounter = 1;
+
+	function xipServerInit(){
+		xipCurrentHash = "";
+		xipRequestMessage = "";
+		xipResponseParts = [];
+		xipPartIndex = 0;
+	}
+
+	function xipServerLoaded(){
+		xipServerInit();
+		xipClientUrl = decodeURIComponent(window.location.hash.substring(1, window.location.hash.length));
+		
+		setInterval(pollHash, 10);
+		setClientUrl("loaded");
+	}
+
+	function pollHash(){
+		//Can't use location.hash because at least Firefox does a decodeURIComponent on it.
+		var urlParts = window.location.href.split("#");
+		if(urlParts.length == 2){
+			var newHash = urlParts[1];
+			if(newHash != xipCurrentHash){
+				try{
+					messageReceived(newHash);
+				}catch(e){
+					//Make sure to not keep processing the error hash value.
+					xipCurrentHash = newHash;
+					throw e;
+				}
+				xipCurrentHash = newHash;
+			}
+		}
+	}
+
+	function messageReceived(urlEncodedMessage){
+		//Split off xip header.
+		var parts = urlEncodedMessage.split(":");
+		var command = parts[1];
+		urlEncodedMessage = parts[2] || "";
+		
+		switch(command){
+			case "ok":
+				sendResponsePart();
+				break;
+			case "start":
+				xipRequestMessage = "";
+				xipRequestMessage += urlEncodedMessage;
+				setClientUrl("ok");
+				break;
+			case "part":
+				xipRequestMessage += urlEncodedMessage;			
+				setClientUrl("ok");
+				break;
+			case "end":
+				setClientUrl("ok");
+				xipRequestMessage += urlEncodedMessage;
+				sendXhr();
+				break;
+		}
+	}
+
+	function sendResponse(urlEncodedData){
+		//Break the message into parts, if necessary.
+		xipResponseParts = [];
+		var resData = urlEncodedData;
+		var urlLength = xipClientUrl.length;
+		var partLength = xipUrlLimit - urlLength;
+		var resIndex = 0;
+
+		while((resData.length - resIndex) + urlLength > xipUrlLimit){
+			xipResponseParts.push(resData.substring(resIndex, resIndex + partLength));
+			resIndex += partLength;
+		}
+		xipResponseParts.push(resData.substring(resIndex, resData.length));
+		
+		xipPartIndex = 0;
+		sendResponsePart();
+	}
+	
+	function sendResponsePart(){
+		if(xipPartIndex < xipResponseParts.length){
+			//Get the message part.
+			var partData = xipResponseParts[xipPartIndex];
+			
+			//Get the command.
+			var cmd = "part";
+			if(xipPartIndex + 1 == xipResponseParts.length){
+				cmd = "end";
+			}else if (xipPartIndex == 0){
+				cmd = "start";
+			}
+
+			setClientUrl(cmd, partData);
+			xipPartIndex++;
+		}else{
+			xipServerInit();
+		}
+	}
+
+	function setClientUrl(cmd, message){
+		var clientUrl = xipClientUrl + "#" + (xipIdCounter++) + ":" + cmd;
+		if(message){
+			clientUrl += ":" + message;
+		}
+
+		//Safari won't let us replace across domains.
+		if(navigator.userAgent.indexOf("Safari") == -1){
+			parent.location.replace(clientUrl);
+		}else{
+			parent.location = clientUrl;
+		}
+	}
+
+	function xhrDone(xhr){
+		/* Need to pull off and return the following data:
+			- responseHeaders
+			- status
+			- statusText
+			- responseText
+		*/
+		var response = {};
+	
+		if(typeof(xhr.getAllResponseHeaders) != "undefined"){
+			var allHeaders = xhr.getAllResponseHeaders();
+			if(allHeaders){
+				response.responseHeaders = allHeaders;
+			}
+		}
+		
+		if(xhr.status == 0 || xhr.status){
+			response.status = xhr.status;
+		}
+		
+		if(xhr.statusText){
+			response.statusText = xhr.statusText;
+		}
+		
+		if(xhr.responseText){
+			response.responseText = xhr.responseText;
+		}
+	
+		//Build a string of the response object.
+		var result = "";
+		var isFirst = true;
+		for (var param in response){
+			if(isFirst){
+				isFirst = false;
+			}else{
+				result += "&";
+			}
+			result += param + "=" + encodeURIComponent(response[param]);
+		}
+		sendResponse(result);
+	}
+
+	function sendXhr(){
+		var request = {};
+		var nvPairs = xipRequestMessage.split("&");
+		var i = 0;
+		var nameValue = null;
+		for(i = 0; i < nvPairs.length; i++){
+			if(nvPairs[i]){
+				var nameValue = nvPairs[i].split("=");
+				request[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]);
+			}
+		}
+
+		//Split up the request headers, if any.
+		var headers = {};
+		if(request.requestHeaders){
+			nvPairs = request.requestHeaders.split("\r\n");
+			for(i = 0; i < nvPairs.length; i++){
+				if(nvPairs[i]){
+					nameValue = nvPairs[i].split(": ");
+					headers[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]);
+				}
+			}
+
+			request.requestHeaders = headers;
+		}
+		
+		if(isAllowedRequest(request)){
+		
+			//The request is allowed, so set up the XHR object.
+			var xhr = dojo.hostenv.getXmlhttpObject();
+			
+			//Start timer to look for readyState.
+			var xhrIntervalId = setInterval(function(){
+			
+				if(xhr.readyState == 4){
+					clearInterval(xhrIntervalId);
+					xhrDone(xhr);
+				}
+			}, 10);
+
+			//Actually start up the XHR request.
+			xhr.open(request.method, request.uri, true);
+			dojo.setHeaders(xhr, request.requestHeaders);
+			
+			var content = "";
+			if(request.data){
+				content = request.data;
+			}
+
+			try{
+				xhr.send(content);
+			}catch(e){
+				if(typeof xhr.abort == "function"){
+					xhr.abort();
+					xhrDone({status: 404, statusText: "xip_server.html error: " + e});
+				}
+			}
+		}
+	}
+
+	window.onload = xipServerLoaded;
+	// -->
+	</script>
+</head>
+<body>
+	<h4>The Dojo Toolkit -- xip_server.html</h4>
+
+	<p>This file is used for Dojo's XMLHttpRequest Iframe Proxy. This is the the file
+	that should go on the server that will actually be doing the XHR request.</p>
+</body>
+</html>

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/xip_server.html
------------------------------------------------------------------------------
    svn:eol-style = native