You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xap-commits@incubator.apache.org by mt...@apache.org on 2007/03/14 20:37:27 UTC
svn commit: r518313 [19/43] - in /incubator/xap/trunk/codebase/src/dojo: ./
src/ src/animation/ src/cal/ src/charting/ src/charting/svg/
src/charting/vml/ src/collections/ src/crypto/ src/data/ src/data/core/
src/data/old/ src/data/old/format/ src/data...
Added: incubator/xap/trunk/codebase/src/dojo/src/io/ScriptSrcIO.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/codebase/src/dojo/src/io/ScriptSrcIO.js?view=auto&rev=518313
==============================================================================
--- incubator/xap/trunk/codebase/src/dojo/src/io/ScriptSrcIO.js (added)
+++ incubator/xap/trunk/codebase/src/dojo/src/io/ScriptSrcIO.js Wed Mar 14 13:36:44 2007
@@ -0,0 +1,469 @@
+/*
+ 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(){
+ //summary: Internal method to start the process of watching for in-flight requests.
+ if(!this.inFlightTimer){
+ this.inFlightTimer = setInterval("dojo.io.ScriptSrcTransport.watchInFlight();", 100);
+ }
+ }
+
+ this.watchInFlight = function(){
+ //summary: Internal method to watch for in-flight requests.
+ 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(/*dojo.io.Request*/kwArgs){
+ //summary: Tells dojo.io.bind() if this is a good transport to
+ //use for the particular type of request. This type of transport can only
+ //handle responses that are JavaScript or JSON that is passed to a JavaScript
+ //callback. It can only do asynchronous binds, is limited to GET HTTP method
+ //requests, and cannot handle formNodes. However, it has the advantage of being
+ //able to do cross-domain requests.
+
+ return dojo.lang.inArray(["text/javascript", "text/json", "application/json"], (kwArgs["mimetype"].toLowerCase()))
+ && (kwArgs["method"].toLowerCase() == "get")
+ && !(kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]))
+ && (!kwArgs["sync"] || kwArgs["sync"] == false)
+ && !kwArgs["file"]
+ && !kwArgs["multipart"];
+ }
+
+ this.removeScripts = function(){
+ //summary: Removes any script tags from the DOM that may have been added by ScriptSrcTransport.
+ //description: 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 remove the script element while
+ //part of the response script is still executing, the browser might crash.
+ 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(/*dojo.io.Request*/kwArgs){
+ //summary: function that sends the request to the server.
+ //description: See the Dojo Book page on this transport for a full
+ //description of supported kwArgs properties and usage:
+ //http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book25
+
+ //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);
+};
Added: incubator/xap/trunk/codebase/src/dojo/src/io/XhrIframeProxy.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/codebase/src/dojo/src/io/XhrIframeProxy.js?view=auto&rev=518313
==============================================================================
--- incubator/xap/trunk/codebase/src/dojo/src/io/XhrIframeProxy.js (added)
+++ incubator/xap/trunk/codebase/src/dojo/src/io/XhrIframeProxy.js Wed Mar 14 13:36:44 2007
@@ -0,0 +1,239 @@
+/*
+ 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.require("dojo.experimental");
+dojo.experimental("dojo.io.XhrIframeProxy");
+
+dojo.require("dojo.io.IframeIO");
+dojo.require("dojo.dom");
+dojo.require("dojo.uri.Uri");
+
+dojo.io.XhrIframeProxy = {
+ //summary: Object that implements the iframe handling for XMLHttpRequest
+ //IFrame Proxying.
+ //description: Do not use this object directly. See the Dojo Book page
+ //on XMLHttpRequest IFrame Proxying:
+ //http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book75
+ //Usage of XHR IFrame Proxying does not work from local disk in Safari.
+
+ xipClientUrl: djConfig["xipClientUrl"] || dojo.uri.dojoUri("src/io/xip_client.html"),
+
+ _state: {},
+ _stateIdCounter: 0,
+
+ needFrameRecursion: function(){
+ return (true == dojo.render.html.ie70);
+ },
+
+ send: function(facade){
+ var stateId = "XhrIframeProxy" + (this._stateIdCounter++);
+ facade._stateId = stateId;
+
+
+ var frameUrl = this.xipClientUrl + "#0:init:id=" + stateId + "&server="
+ + encodeURIComponent(facade._ifpServerUrl) + "&fr=false";
+ if(this.needFrameRecursion()){
+ //IE7 hack. Need to load server URL, and have that load the xip_client.html.
+ //Also, this server URL needs to different from the one eventually loaded by xip_client.html
+ //Otherwise, IE7 will not load it. Funky.
+ var fullClientUrl = window.location.href.substring(0, window.location.href.lastIndexOf("/") + 1);
+ fullClientUrl += this.xipClientUrl;
+
+ var serverUrl = facade._ifpServerUrl
+ + (facade._ifpServerUrl.indexOf("?") == -1 ? "?" : "&") + "dojo.fr=1";
+
+ frameUrl = serverUrl + "#0:init:id=" + stateId + "&client="
+ + encodeURIComponent(fullClientUrl) + "&fr=" + this.needFrameRecursion(); //fr is for Frame Recursion
+ }
+
+ this._state[stateId] = {
+ facade: facade,
+ stateId: stateId,
+ clientFrame: dojo.io.createIFrame(stateId, "", frameUrl)
+ };
+
+ return stateId;
+ },
+
+ receive: function(/*String*/stateId, /*String*/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);
+ },
+
+ clientFrameLoaded: function(/*String*/stateId){
+ var state = this._state[stateId];
+ var facade = state.facade;
+
+ if(this.needFrameRecursion()){
+ var clientWindow = window.open("", state.stateId + "_clientEndPoint");
+ }else{
+ var clientWindow = state.clientFrame.contentWindow;
+ }
+
+ 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(dojo.io.argsFromMap(requestData, "utf8"));
+ },
+
+ destroyState: function(/*String*/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;
+ }
+ },
+
+ 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){
+ //summary: XMLHttpRequest facade object used by dojo.io.XhrIframeProxy.
+
+ //description: Do not use this object directly. See the Dojo Book page
+ //on XMLHttpRequest IFrame Proxying:
+ //http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book75
+ 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(/*String*/method, /*String*/uri){
+ this._method = method;
+ this._uri = uri;
+
+ this.readyState = 1;
+ },
+
+ setRequestHeader: function(/*String*/header, /*String*/value){
+ this._requestHeaders[header] = value;
+ },
+
+ send: function(/*String*/stringData){
+ this._bodyData = stringData;
+
+ this._stateId = dojo.io.XhrIframeProxy.send(this);
+
+ this.readyState = 2;
+ },
+ abort: function(){
+ dojo.io.XhrIframeProxy.destroyState(this._stateId);
+ },
+
+ getAllResponseHeaders: function(){
+ return this._allResponseHeaders; //String
+ },
+
+ getResponseHeader: function(/*String*/header){
+ return this._responseHeaders[header]; //String
+ },
+
+ _setResponseHeaders: function(/*String*/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];
+ }
+ }
+ }
+ }
+});
Added: incubator/xap/trunk/codebase/src/dojo/src/io/__package__.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/codebase/src/dojo/src/io/__package__.js?view=auto&rev=518313
==============================================================================
--- incubator/xap/trunk/codebase/src/dojo/src/io/__package__.js (added)
+++ incubator/xap/trunk/codebase/src/dojo/src/io/__package__.js Wed Mar 14 13:36:44 2007
@@ -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.*");
Added: incubator/xap/trunk/codebase/src/dojo/src/io/cometd.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/codebase/src/dojo/src/io/cometd.js?view=auto&rev=518313
==============================================================================
--- incubator/xap/trunk/codebase/src/dojo/src/io/cometd.js (added)
+++ incubator/xap/trunk/codebase/src/dojo/src/io/cometd.js Wed Mar 14 13:36:44 2007
@@ -0,0 +1,929 @@
+/*
+ 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 module
+dojo.provide("dojo.io.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";
+ bindArgs.method = "GET";
+ }
+ }
+ }
+ if(bargs){
+ dojo.lang.mixin(bindArgs, bargs);
+ }
+ return dojo.io.bind(bindArgs);
+ }
+
+ this.finishInit = function(type, data, evt, request){
+ data = data[0];
+ 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
+ var req = dojo.io.bind({
+ url: (url||cometd.url),
+ content: content,
+ mimetype: "text/json",
+ 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"],
+ mimetype: "text/json",
+ 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;
Added: incubator/xap/trunk/codebase/src/dojo/src/io/common.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/codebase/src/dojo/src/io/common.js?view=auto&rev=518313
==============================================================================
--- incubator/xap/trunk/codebase/src/dojo/src/io/common.js (added)
+++ incubator/xap/trunk/codebase/src/dojo/src/io/common.js Wed Mar 14 13:36:44 2007
@@ -0,0 +1,517 @@
+/*
+ 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.
+ *
+ * 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(/*String*/ url, /*String*/ mimetype, /*String*/ transport, /*String or Boolean*/ changeUrl){
+// summary:
+// Constructs a Request object that is used by dojo.io.bind().
+// description:
+// dojo.io.bind() will create one of these for you if
+// you call dojo.io.bind() with an plain object containing the bind parameters.
+// This method can either take the arguments specified, or an Object containing all of the parameters that you
+// want to use to create the dojo.io.Request (similar to how dojo.io.bind() is called.
+// The named parameters to this constructor represent the minimum set of parameters need
+ 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(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){
+ // summary:
+ // Called on successful completion of a bind.
+ // type: String
+ // A string with value "load"
+ // data: Object
+ // The object representing the result of the bind. The actual structure
+ // of the data object will depend on the mimetype that was given to bind
+ // in the bind arguments.
+ // transportImplementation: Object
+ // The object that implements a particular transport. Structure is depedent
+ // on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
+ // XMLHttpRequest object from the browser.
+ // kwArgs: Object
+ // Object that contains the request parameters that were given to the
+ // bind call. Useful for storing and retrieving state from when bind
+ // was called.
+ },
+ error: function(/*String*/type, /*Object*/error, /*Object*/transportImplementation, /*Object*/kwArgs){
+ // summary:
+ // Called when there is an error with a bind.
+ // type: String
+ // A string with value "error"
+ // error: Object
+ // The error object. Should be a dojo.io.Error object, but not guaranteed.
+ // transportImplementation: Object
+ // The object that implements a particular transport. Structure is depedent
+ // on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
+ // XMLHttpRequest object from the browser.
+ // kwArgs: Object
+ // Object that contains the request parameters that were given to the
+ // bind call. Useful for storing and retrieving state from when bind
+ // was called.
+ },
+ timeout: function(/*String*/type, /*Object*/empty, /*Object*/transportImplementation, /*Object*/kwArgs){
+ // summary:
+ // Called when there is an error with a bind. Only implemented in certain transports at this time.
+ // type: String
+ // A string with value "timeout"
+ // empty: Object
+ // Should be null. Just a spacer argument so that load, error, timeout and handle have the
+ // same signatures.
+ // transportImplementation: Object
+ // The object that implements a particular transport. Structure is depedent
+ // on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
+ // XMLHttpRequest object from the browser. May be null for the timeout case for
+ // some transports.
+ // kwArgs: Object
+ // Object that contains the request parameters that were given to the
+ // bind call. Useful for storing and retrieving state from when bind
+ // was called.
+ },
+ handle: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){
+ // summary:
+ // The handle method can be defined instead of defining separate load, error and timeout
+ // callbacks.
+ // type: String
+ // A string with the type of callback: "load", "error", or "timeout".
+ // data: Object
+ // See the above callbacks for what this parameter could be.
+ // transportImplementation: Object
+ // The object that implements a particular transport. Structure is depedent
+ // on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
+ // XMLHttpRequest object from the browser.
+ // kwArgs: Object
+ // Object that contains the request parameters that were given to the
+ // bind call. Useful for storing and retrieving state from when bind
+ // was called.
+ },
+
+ //FIXME: change IframeIO.js to use timeouts?
+ // 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(/*Object*/ kwArgs){
+ // summary:
+ // Creates a dojo.io.Request from a simple object (kwArgs object).
+
+ // 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(/*String*/ msg, /*String*/ type, /*Number*/num){
+ // summary:
+ // Constructs an object representing a bind error.
+ 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(/*String*/name){
+ // summary:
+ // Used to register transports that can support bind calls.
+ this.push(name);
+ // FIXME: do we need to handle things that aren't direct children of the
+ // dojo.io module? (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(/*dojo.io.Request or Object*/request){
+ // summary:
+ // Binding interface for IO. Loading different IO transports, like
+ // dojo.io.BrowserIO or dojo.io.IframeIO, will register with bind
+ // to handle particular types of bind calls.
+ // request: Object
+ // Object containing bind arguments. This object is converted to
+ // a dojo.io.Request object, and that request object is the return
+ // value for this method.
+ if(!(request instanceof dojo.io.Request)){
+ try{
+ request = new dojo.io.Request(request);
+ }catch(e){ dojo.debug(e); }
+ }
+
+ // if the request asks for a particular implementation, use it
+ var tsName = "";
+ if(request["transport"]){
+ tsName = request["transport"];
+ if(!this[tsName]){
+ dojo.io.sendBindError(request, "No dojo.io.bind() transport with name '"
+ + request["transport"] + "'.");
+ return request; //dojo.io.Request
+ }
+ if(!this[tsName].canHandle(request)){
+ dojo.io.sendBindError(request, "dojo.io.bind() transport with name '"
+ + request["transport"] + "' cannot handle this type of request.");
+ return request; //dojo.io.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;
+ break;
+ }
+ }
+ if(tsName == ""){
+ dojo.io.sendBindError(request, "None of the loaded transports for dojo.io.bind()"
+ + " can handle the request.");
+ return request; //dojo.io.Request
+ }
+ }
+ this[tsName].bind(request);
+ request.bindSuccess = true;
+ return request; //dojo.io.Request
+}
+
+dojo.io.sendBindError = function(/* Object */request, /* String */message){
+ // summary:
+ // Used internally by dojo.io.bind() to return/raise a bind error.
+
+ //Need to be careful since not all hostenvs support setTimeout.
+ if((typeof request.error == "function" || typeof request.handle == "function")
+ && (typeof setTimeout == "function" || typeof setTimeout == "object")){
+ var errorObject = new dojo.io.Error(message);
+ setTimeout(function(){
+ request[(typeof request.error == "function") ? "error" : "handle"]("error", errorObject, null, request);
+ }, 50);
+ }else{
+ dojo.raise(message);
+ }
+}
+
+dojo.io.queueBind = function(/*dojo.io.Request or Object*/request){
+ // summary:
+ // queueBind will use dojo.io.bind() but guarantee that only one bind
+ // call is handled at a time.
+ // description:
+ // If queueBind is called while a bind call
+ // is in process, it will queue up the other calls to bind and call them
+ // in order as bind calls complete.
+ // request: Object
+ // Same sort of request object as used for dojo.io.bind().
+ 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.Request
+}
+
+dojo.io._dispatchNextQueueBind = function(){
+ // summary:
+ // Private method used by dojo.io.queueBind().
+ 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(/*Object*/map, /*String?*/encoding, /*String?*/last){
+ // summary:
+ // Converts name/values pairs in the map object to an URL-encoded string
+ // with format of name1=value1&name2=value2...
+ // map: Object
+ // Object that has the contains the names and values.
+ // encoding: String?
+ // String to specify how to encode the name and value. If the encoding string
+ // contains "utf" (case-insensitive), then encodeURIComponent is used. Otherwise
+ // dojo.string.encodeAscii is used.
+ // last: String?
+ // The last parameter in the list. Helps with final string formatting?
+ 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("&"); //String
+}
+
+dojo.io.setIFrameSrc = function(/*DOMNode*/ iframe, /*String*/ src, /*Boolean*/ replace){
+ //summary:
+ // Sets the URL that is loaded in an IFrame. The replace parameter indicates whether
+ // location.replace() should be used when changing the location of the iframe.
+ 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");
+}
+*/
Added: incubator/xap/trunk/codebase/src/dojo/src/io/cookie.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/codebase/src/dojo/src/io/cookie.js?view=auto&rev=518313
==============================================================================
--- incubator/xap/trunk/codebase/src/dojo/src/io/cookie.js (added)
+++ incubator/xap/trunk/codebase/src/dojo/src/io/cookie.js Wed Mar 14 13:36:44 2007
@@ -0,0 +1,130 @@
+/*
+ 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(/*String*/name, /*String*/value,
+ /*Number?*/days, /*String?*/path,
+ /*String?*/domain, /*boolean?*/secure){
+ //summary: sets a cookie.
+ 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(/*String*/name){
+ //summary: Gets a cookie with the given 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; //String
+}
+
+dojo.io.cookie.get = dojo.io.cookie.getCookie;
+
+dojo.io.cookie.deleteCookie = function(/*String*/name){
+ //summary: Deletes a cookie with the given name.
+ dojo.io.cookie.setCookie(name, "-", 0);
+}
+
+dojo.io.cookie.setObjectCookie = function( /*String*/name, /*Object*/obj,
+ /*Number?*/days, /*String?*/path,
+ /*String?*/domain, /*boolean?*/secure,
+ /*boolean?*/clearCurrent){
+ //summary: Takes an object, serializes it to a cookie value, and either
+ //sets a cookie with the serialized value.
+ //description: If clearCurrent is true, then any current cookie value
+ //for this object will be replaced with the the new serialized object value.
+ //If clearCurrent is false, then the existing cookie value will be modified
+ //with any changes from the new object value.
+ //Objects must be simple name/value pairs where the value is either a string
+ //or a number. Any other value will be ignored.
+ 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(obj[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(/*String*/name){
+ //summary: Gets an object value for the given cookie name. The complement of
+ //dojo.io.cookie.setObjectCookie().
+ 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(){
+ //summary: Tests the browser to see if cookies are enabled.
+ 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; //boolean
+}
+
+// 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; }
Added: incubator/xap/trunk/codebase/src/dojo/src/io/xip_client.html
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/codebase/src/dojo/src/io/xip_client.html?view=auto&rev=518313
==============================================================================
--- incubator/xap/trunk/codebase/src/dojo/src/io/xip_client.html (added)
+++ incubator/xap/trunk/codebase/src/dojo/src/io/xip_client.html Wed Mar 14 13:36:44 2007
@@ -0,0 +1,254 @@
+<!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:realEncodedMessage
+
+ 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:
+ - init: message used to init the frame. Sent as the first URL when loading
+ the page. Contains some config parameters.
+ - 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 realEncodedMessage parts just have to be concatenated
+ together.
+ */
+
+ //MSIE has the lowest limit for URLs with fragment identifiers,
+ //at around 4K. Choosing a slightly smaller number for good measure.
+ xipUrlLimit = 4000;
+ xipIdCounter = 1;
+
+ function xipInit(){
+ xipStateId = "";
+ xipIsSending = false;
+ xipServerUrl = null;
+ xipStateId = null;
+ xipRequestData = null;
+ xipCurrentHash = "";
+ xipResponseMessage = "";
+ xipRequestParts = [];
+ xipPartIndex = 0;
+ xipServerWindow = null;
+ xipUseFrameRecursion = false;
+ }
+ xipInit();
+
+ function send(encodedData){
+ if(xipUseFrameRecursion == "true"){
+ var clientEndPoint = window.open(xipStateId + "_clientEndPoint");
+ clientEndPoint.send(encodedData);
+ }else{
+ if(!xipIsSending){
+ xipIsSending = true;
+
+ xipRequestData = encodedData || "";
+
+ //Get a handle to the server iframe.
+ xipServerWindow = frames[xipStateId + "_frame"];
+ if (!xipServerWindow){
+ xipServerWindow = document.getElementById(xipStateId + "_frame").contentWindow;
+ }
+
+ sendRequestStart();
+ }
+ }
+ }
+
+ //Modify the server URL if it is a local path and
+ //This is done for local/same domain testing.
+ function fixServerUrl(ifpServerUrl){
+ if(ifpServerUrl.indexOf("..") == 0){
+ var parts = ifpServerUrl.split("/");
+ ifpServerUrl = parts[parts.length - 1];
+ }
+ return 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(encodedData){
+ var msg = unpackMessage(encodedData);
+
+ switch(msg.command){
+ case "loaded":
+ xipMasterFrame.dojo.io.XhrIframeProxy.clientFrameLoaded(xipStateId);
+ break;
+ case "ok":
+ sendRequestPart();
+ break;
+ case "start":
+ xipResponseMessage = "";
+ xipResponseMessage += msg.message;
+ setServerUrl("ok");
+ break;
+ case "part":
+ xipResponseMessage += msg.message;
+ setServerUrl("ok");
+ break;
+ case "end":
+ setServerUrl("ok");
+ xipResponseMessage += msg.message;
+ xipMasterFrame.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 = makeServerUrl(cmd, message);
+
+ //Safari won't let us replace across domains.
+ if(navigator.userAgent.indexOf("Safari") == -1){
+ xipServerWindow.location.replace(serverUrl);
+ }else{
+ xipServerWindow.location = serverUrl;
+ }
+ }
+
+ function makeServerUrl(cmd, message){
+ var serverUrl = xipServerUrl + "#" + (xipIdCounter++) + ":" + cmd;
+ if(message){
+ serverUrl += ":" + message;
+ }
+ return serverUrl;
+ }
+
+ function unpackMessage(encodedMessage){
+ var parts = encodedMessage.split(":");
+ var command = parts[1];
+ encodedMessage = parts[2] || "";
+
+ var config = null;
+ if(command == "init"){
+ var configParts = encodedMessage.split("&");
+ config = {};
+ for(var i = 0; i < configParts.length; i++){
+ var nameValue = configParts[i].split("=");
+ config[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]);
+ }
+ }
+ return {command: command, message: encodedMessage, config: config};
+ }
+
+ function onClientLoad(){
+ //Decode the init params
+ var config = unpackMessage(window.location.href.split("#")[1]).config;
+
+ xipStateId = config.id;
+
+ //Remove the query param for the IE7 recursive case.
+ xipServerUrl = fixServerUrl(config.server).replace(/(\?|\&)dojo\.fr\=1/, "");
+ xipUseFrameRecursion = config["fr"];
+
+ if(xipUseFrameRecursion == "endpoint"){
+ xipMasterFrame = parent.parent;
+ }else{
+ xipMasterFrame = parent;
+ }
+
+ //Start counter to inspect hash value.
+ setInterval(pollHash, 10);
+
+ var clientUrl = window.location.href.split("#")[0];
+ document.getElementById("iframeHolder").innerHTML = '<iframe src="'
+ + makeServerUrl("init", 'id=' + xipStateId + '&client=' + encodeURIComponent(clientUrl)
+ + '&fr=' + xipUseFrameRecursion) + '" id="' + xipStateId + '_frame"></iframe>';
+
+
+ }
+
+ if(typeof(window.addEventListener) == "undefined"){
+ window.attachEvent("onload", onClientLoad);
+ }else{
+ window.addEventListener('load', onClientLoad, false);
+ }
+
+ // -->
+ </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>
+
+ <span id="iframeHolder"></span>
+</body>
+</html>