Improve output: distinguish between failed assertions (failures) and unexpected exceptions (errors), and print a filtered stack trace for any exception.
This commit is contained in:
commit
4f2e303079
1839 changed files with 235630 additions and 0 deletions
557
webapp/web/src/io/BrowserIO.js
Normal file
557
webapp/web/src/io/BrowserIO.js
Normal file
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
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.BrowserIO");
|
||||
|
||||
dojo.require("dojo.io");
|
||||
dojo.require("dojo.lang.array");
|
||||
dojo.require("dojo.lang.func");
|
||||
dojo.require("dojo.string.extras");
|
||||
dojo.require("dojo.dom");
|
||||
dojo.require("dojo.undo.browser");
|
||||
|
||||
dojo.io.checkChildrenForFile = function(node){
|
||||
var hasFile = false;
|
||||
var inputs = node.getElementsByTagName("input");
|
||||
dojo.lang.forEach(inputs, function(input){
|
||||
if(hasFile){ return; }
|
||||
if(input.getAttribute("type")=="file"){
|
||||
hasFile = true;
|
||||
}
|
||||
});
|
||||
return hasFile;
|
||||
}
|
||||
|
||||
dojo.io.formHasFile = function(formNode){
|
||||
return dojo.io.checkChildrenForFile(formNode);
|
||||
}
|
||||
|
||||
dojo.io.updateNode = function(node, urlOrArgs){
|
||||
node = dojo.byId(node);
|
||||
var args = urlOrArgs;
|
||||
if(dojo.lang.isString(urlOrArgs)){
|
||||
args = { url: urlOrArgs };
|
||||
}
|
||||
args.mimetype = "text/html";
|
||||
args.load = function(t, d, e){
|
||||
while(node.firstChild){
|
||||
if(dojo["event"]){
|
||||
try{
|
||||
dojo.event.browser.clean(node.firstChild);
|
||||
}catch(e){}
|
||||
}
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
node.innerHTML = d;
|
||||
};
|
||||
dojo.io.bind(args);
|
||||
}
|
||||
|
||||
dojo.io.formFilter = function(node) {
|
||||
var type = (node.type||"").toLowerCase();
|
||||
return !node.disabled && node.name
|
||||
&& !dojo.lang.inArray(type, ["file", "submit", "image", "reset", "button"]);
|
||||
}
|
||||
|
||||
// TODO: Move to htmlUtils
|
||||
dojo.io.encodeForm = function(formNode, encoding, formFilter){
|
||||
if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
|
||||
dojo.raise("Attempted to encode a non-form element.");
|
||||
}
|
||||
if(!formFilter) { formFilter = dojo.io.formFilter; }
|
||||
var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
|
||||
var values = [];
|
||||
|
||||
for(var i = 0; i < formNode.elements.length; i++){
|
||||
var elm = formNode.elements[i];
|
||||
if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
|
||||
var name = enc(elm.name);
|
||||
var type = elm.type.toLowerCase();
|
||||
|
||||
if(type == "select-multiple"){
|
||||
for(var j = 0; j < elm.options.length; j++){
|
||||
if(elm.options[j].selected) {
|
||||
values.push(name + "=" + enc(elm.options[j].value));
|
||||
}
|
||||
}
|
||||
}else if(dojo.lang.inArray(type, ["radio", "checkbox"])){
|
||||
if(elm.checked){
|
||||
values.push(name + "=" + enc(elm.value));
|
||||
}
|
||||
}else{
|
||||
values.push(name + "=" + enc(elm.value));
|
||||
}
|
||||
}
|
||||
|
||||
// now collect input type="image", which doesn't show up in the elements array
|
||||
var inputs = formNode.getElementsByTagName("input");
|
||||
for(var i = 0; i < inputs.length; i++) {
|
||||
var input = inputs[i];
|
||||
if(input.type.toLowerCase() == "image" && input.form == formNode
|
||||
&& formFilter(input)) {
|
||||
var name = enc(input.name);
|
||||
values.push(name + "=" + enc(input.value));
|
||||
values.push(name + ".x=0");
|
||||
values.push(name + ".y=0");
|
||||
}
|
||||
}
|
||||
return values.join("&") + "&";
|
||||
}
|
||||
|
||||
dojo.io.FormBind = function(args) {
|
||||
this.bindArgs = {};
|
||||
|
||||
if(args && args.formNode) {
|
||||
this.init(args);
|
||||
} else if(args) {
|
||||
this.init({formNode: args});
|
||||
}
|
||||
}
|
||||
dojo.lang.extend(dojo.io.FormBind, {
|
||||
form: null,
|
||||
|
||||
bindArgs: null,
|
||||
|
||||
clickedButton: null,
|
||||
|
||||
init: function(args) {
|
||||
var form = dojo.byId(args.formNode);
|
||||
|
||||
if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
|
||||
throw new Error("FormBind: Couldn't apply, invalid form");
|
||||
} else if(this.form == form) {
|
||||
return;
|
||||
} else if(this.form) {
|
||||
throw new Error("FormBind: Already applied to a form");
|
||||
}
|
||||
|
||||
dojo.lang.mixin(this.bindArgs, args);
|
||||
this.form = form;
|
||||
|
||||
this.connect(form, "onsubmit", "submit");
|
||||
|
||||
for(var i = 0; i < form.elements.length; i++) {
|
||||
var node = form.elements[i];
|
||||
if(node && node.type && dojo.lang.inArray(node.type.toLowerCase(), ["submit", "button"])) {
|
||||
this.connect(node, "onclick", "click");
|
||||
}
|
||||
}
|
||||
|
||||
var inputs = form.getElementsByTagName("input");
|
||||
for(var i = 0; i < inputs.length; i++) {
|
||||
var input = inputs[i];
|
||||
if(input.type.toLowerCase() == "image" && input.form == form) {
|
||||
this.connect(input, "onclick", "click");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onSubmit: function(form) {
|
||||
return true;
|
||||
},
|
||||
|
||||
submit: function(e) {
|
||||
e.preventDefault();
|
||||
if(this.onSubmit(this.form)) {
|
||||
dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
|
||||
formFilter: dojo.lang.hitch(this, "formFilter")
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
click: function(e) {
|
||||
var node = e.currentTarget;
|
||||
if(node.disabled) { return; }
|
||||
this.clickedButton = node;
|
||||
},
|
||||
|
||||
formFilter: function(node) {
|
||||
var type = (node.type||"").toLowerCase();
|
||||
var accept = false;
|
||||
if(node.disabled || !node.name) {
|
||||
accept = false;
|
||||
} else if(dojo.lang.inArray(type, ["submit", "button", "image"])) {
|
||||
if(!this.clickedButton) { this.clickedButton = node; }
|
||||
accept = node == this.clickedButton;
|
||||
} else {
|
||||
accept = !dojo.lang.inArray(type, ["file", "submit", "reset", "button"]);
|
||||
}
|
||||
return accept;
|
||||
},
|
||||
|
||||
// in case you don't have dojo.event.* pulled in
|
||||
connect: function(srcObj, srcFcn, targetFcn) {
|
||||
if(dojo.evalObjPath("dojo.event.connect")) {
|
||||
dojo.event.connect(srcObj, srcFcn, this, targetFcn);
|
||||
} else {
|
||||
var fcn = dojo.lang.hitch(this, targetFcn);
|
||||
srcObj[srcFcn] = function(e) {
|
||||
if(!e) { e = window.event; }
|
||||
if(!e.currentTarget) { e.currentTarget = e.srcElement; }
|
||||
if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
|
||||
fcn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dojo.io.XMLHTTPTransport = new function(){
|
||||
var _this = this;
|
||||
|
||||
var _cache = {}; // FIXME: make this public? do we even need to?
|
||||
this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
|
||||
this.preventCache = false; // if this is true, we'll always force GET requests to cache
|
||||
|
||||
// FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
|
||||
function getCacheKey(url, query, method) {
|
||||
return url + "|" + query + "|" + method.toLowerCase();
|
||||
}
|
||||
|
||||
function addToCache(url, query, method, http) {
|
||||
_cache[getCacheKey(url, query, method)] = http;
|
||||
}
|
||||
|
||||
function getFromCache(url, query, method) {
|
||||
return _cache[getCacheKey(url, query, method)];
|
||||
}
|
||||
|
||||
this.clearCache = function() {
|
||||
_cache = {};
|
||||
}
|
||||
|
||||
// moved successful load stuff here
|
||||
function doLoad(kwArgs, http, url, query, useCache) {
|
||||
if( ((http.status>=200)&&(http.status<300))|| // allow any 2XX response code
|
||||
(http.status==304)|| // get it out of the cache
|
||||
(location.protocol=="file:" && (http.status==0 || http.status==undefined))||
|
||||
(location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
|
||||
){
|
||||
var ret;
|
||||
if(kwArgs.method.toLowerCase() == "head"){
|
||||
var headers = http.getAllResponseHeaders();
|
||||
ret = {};
|
||||
ret.toString = function(){ return headers; }
|
||||
var values = headers.split(/[\r\n]+/g);
|
||||
for(var i = 0; i < values.length; i++) {
|
||||
var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
|
||||
if(pair) {
|
||||
ret[pair[1]] = pair[2];
|
||||
}
|
||||
}
|
||||
}else if(kwArgs.mimetype == "text/javascript"){
|
||||
try{
|
||||
ret = dj_eval(http.responseText);
|
||||
}catch(e){
|
||||
dojo.debug(e);
|
||||
dojo.debug(http.responseText);
|
||||
ret = null;
|
||||
}
|
||||
}else if(kwArgs.mimetype == "text/json"){
|
||||
try{
|
||||
ret = dj_eval("("+http.responseText+")");
|
||||
}catch(e){
|
||||
dojo.debug(e);
|
||||
dojo.debug(http.responseText);
|
||||
ret = false;
|
||||
}
|
||||
}else if((kwArgs.mimetype == "application/xml")||
|
||||
(kwArgs.mimetype == "text/xml")){
|
||||
ret = http.responseXML;
|
||||
if(!ret || typeof ret == "string" || !http.getResponseHeader("Content-Type")) {
|
||||
ret = dojo.dom.createDocumentFromText(http.responseText);
|
||||
}
|
||||
}else{
|
||||
ret = http.responseText;
|
||||
}
|
||||
|
||||
if(useCache){ // only cache successful responses
|
||||
addToCache(url, query, kwArgs.method, http);
|
||||
}
|
||||
kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
|
||||
}else{
|
||||
var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
|
||||
kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
|
||||
}
|
||||
}
|
||||
|
||||
// set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
|
||||
function setHeaders(http, kwArgs){
|
||||
if(kwArgs["headers"]) {
|
||||
for(var header in kwArgs["headers"]) {
|
||||
if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
|
||||
kwArgs["contentType"] = kwArgs["headers"][header];
|
||||
} else {
|
||||
http.setRequestHeader(header, kwArgs["headers"][header]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.inFlight = [];
|
||||
this.inFlightTimer = null;
|
||||
|
||||
this.startWatchingInFlight = function(){
|
||||
if(!this.inFlightTimer){
|
||||
this.inFlightTimer = setInterval("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
|
||||
}
|
||||
}
|
||||
|
||||
this.watchInFlight = function(){
|
||||
var now = null;
|
||||
for(var x=this.inFlight.length-1; x>=0; x--){
|
||||
var tif = this.inFlight[x];
|
||||
if(!tif){ this.inFlight.splice(x, 1); continue; }
|
||||
if(4==tif.http.readyState){
|
||||
// remove it so we can clean refs
|
||||
this.inFlight.splice(x, 1);
|
||||
doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
|
||||
}else if (tif.startTime){
|
||||
//See if this is a timeout case.
|
||||
if(!now){
|
||||
now = (new Date()).getTime();
|
||||
}
|
||||
if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
|
||||
//Stop the request.
|
||||
if(typeof tif.http.abort == "function"){
|
||||
tif.http.abort();
|
||||
}
|
||||
|
||||
// remove it so we can clean refs
|
||||
this.inFlight.splice(x, 1);
|
||||
tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(this.inFlight.length == 0){
|
||||
clearInterval(this.inFlightTimer);
|
||||
this.inFlightTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
|
||||
this.canHandle = function(kwArgs){
|
||||
// canHandle just tells dojo.io.bind() if this is a good transport to
|
||||
// use for the particular type of request.
|
||||
|
||||
// FIXME: we need to determine when form values need to be
|
||||
// multi-part mime encoded and avoid using this transport for those
|
||||
// requests.
|
||||
return hasXmlHttp
|
||||
&& dojo.lang.inArray((kwArgs["mimetype"].toLowerCase()||""), ["text/plain", "text/html", "application/xml", "text/xml", "text/javascript", "text/json"])
|
||||
&& !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) );
|
||||
}
|
||||
|
||||
this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F"; // unique guid as a boundary value for multipart posts
|
||||
|
||||
this.bind = function(kwArgs){
|
||||
if(!kwArgs["url"]){
|
||||
// are we performing a history action?
|
||||
if( !kwArgs["formNode"]
|
||||
&& (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
|
||||
&& (!djConfig.preventBackButtonFix)) {
|
||||
dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request",
|
||||
"Use dojo.undo.browser.addToHistory() instead.", "0.4");
|
||||
dojo.undo.browser.addToHistory(kwArgs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// build this first for cache purposes
|
||||
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];
|
||||
}
|
||||
|
||||
if(kwArgs["file"]){
|
||||
// force post for file transfer
|
||||
kwArgs.method = "post";
|
||||
}
|
||||
|
||||
if(!kwArgs["method"]){
|
||||
kwArgs.method = "get";
|
||||
}
|
||||
|
||||
// guess the multipart value
|
||||
if(kwArgs.method.toLowerCase() == "get"){
|
||||
// GET cannot use multipart
|
||||
kwArgs.multipart = false;
|
||||
}else{
|
||||
if(kwArgs["file"]){
|
||||
// enforce multipart when sending files
|
||||
kwArgs.multipart = true;
|
||||
}else if(!kwArgs["multipart"]){
|
||||
// default
|
||||
kwArgs.multipart = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
|
||||
dojo.undo.browser.addToHistory(kwArgs);
|
||||
}
|
||||
|
||||
var content = kwArgs["content"] || {};
|
||||
|
||||
if(kwArgs.sendTransport) {
|
||||
content["dojo.transport"] = "xmlhttp";
|
||||
}
|
||||
|
||||
do { // break-block
|
||||
if(kwArgs.postContent){
|
||||
query = kwArgs.postContent;
|
||||
break;
|
||||
}
|
||||
|
||||
if(content) {
|
||||
query += dojo.io.argsFromMap(content, kwArgs.encoding);
|
||||
}
|
||||
|
||||
if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
|
||||
break;
|
||||
}
|
||||
|
||||
var t = [];
|
||||
if(query.length){
|
||||
var q = query.split("&");
|
||||
for(var i = 0; i < q.length; ++i){
|
||||
if(q[i].length){
|
||||
var p = q[i].split("=");
|
||||
t.push( "--" + this.multipartBoundary,
|
||||
"Content-Disposition: form-data; name=\"" + p[0] + "\"",
|
||||
"",
|
||||
p[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(kwArgs.file){
|
||||
if(dojo.lang.isArray(kwArgs.file)){
|
||||
for(var i = 0; i < kwArgs.file.length; ++i){
|
||||
var o = kwArgs.file[i];
|
||||
t.push( "--" + this.multipartBoundary,
|
||||
"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
|
||||
"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
|
||||
"",
|
||||
o.content);
|
||||
}
|
||||
}else{
|
||||
var o = kwArgs.file;
|
||||
t.push( "--" + this.multipartBoundary,
|
||||
"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
|
||||
"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
|
||||
"",
|
||||
o.content);
|
||||
}
|
||||
}
|
||||
|
||||
if(t.length){
|
||||
t.push("--"+this.multipartBoundary+"--", "");
|
||||
query = t.join("\r\n");
|
||||
}
|
||||
}while(false);
|
||||
|
||||
// kwArgs.Connection = "close";
|
||||
|
||||
var async = kwArgs["sync"] ? false : true;
|
||||
|
||||
var preventCache = kwArgs["preventCache"] ||
|
||||
(this.preventCache == true && kwArgs["preventCache"] != false);
|
||||
var useCache = kwArgs["useCache"] == true ||
|
||||
(this.useCache == true && kwArgs["useCache"] != false );
|
||||
|
||||
// preventCache is browser-level (add query string junk), useCache
|
||||
// is for the local cache. If we say preventCache, then don't attempt
|
||||
// to look in the cache, but if useCache is true, we still want to cache
|
||||
// the response
|
||||
if(!preventCache && useCache){
|
||||
var cachedHttp = getFromCache(url, query, kwArgs.method);
|
||||
if(cachedHttp){
|
||||
doLoad(kwArgs, cachedHttp, url, query, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// much of this is from getText, but reproduced here because we need
|
||||
// more flexibility
|
||||
var http = dojo.hostenv.getXmlhttpObject(kwArgs);
|
||||
var received = false;
|
||||
|
||||
// build a handler function that calls back to the handler obj
|
||||
if(async){
|
||||
var startTime =
|
||||
// FIXME: setting up this callback handler leaks on IE!!!
|
||||
this.inFlight.push({
|
||||
"req": kwArgs,
|
||||
"http": http,
|
||||
"url": url,
|
||||
"query": query,
|
||||
"useCache": useCache,
|
||||
"startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
|
||||
});
|
||||
this.startWatchingInFlight();
|
||||
}
|
||||
|
||||
if(kwArgs.method.toLowerCase() == "post"){
|
||||
// FIXME: need to hack in more flexible Content-Type setting here!
|
||||
http.open("POST", url, async);
|
||||
setHeaders(http, kwArgs);
|
||||
http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) :
|
||||
(kwArgs.contentType || "application/x-www-form-urlencoded"));
|
||||
try{
|
||||
http.send(query);
|
||||
}catch(e){
|
||||
if(typeof http.abort == "function"){
|
||||
http.abort();
|
||||
}
|
||||
doLoad(kwArgs, {status: 404}, url, query, useCache);
|
||||
}
|
||||
}else{
|
||||
var tmpUrl = url;
|
||||
if(query != "") {
|
||||
tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
|
||||
}
|
||||
if(preventCache) {
|
||||
tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
|
||||
? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
|
||||
}
|
||||
http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
|
||||
setHeaders(http, kwArgs);
|
||||
try {
|
||||
http.send(null);
|
||||
}catch(e) {
|
||||
if(typeof http.abort == "function"){
|
||||
http.abort();
|
||||
}
|
||||
doLoad(kwArgs, {status: 404}, url, query, useCache);
|
||||
}
|
||||
}
|
||||
|
||||
if( !async ) {
|
||||
doLoad(kwArgs, http, url, query, useCache);
|
||||
}
|
||||
|
||||
kwArgs.abort = function(){
|
||||
return http.abort();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
dojo.io.transports.addTransport("XMLHTTPTransport");
|
||||
}
|
253
webapp/web/src/io/IframeIO.js
Normal file
253
webapp/web/src/io/IframeIO.js
Normal file
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
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.IframeIO");
|
||||
dojo.require("dojo.io.BrowserIO");
|
||||
dojo.require("dojo.uri.*");
|
||||
|
||||
// FIXME: is it possible to use the Google htmlfile hack to prevent the
|
||||
// background click with this transport?
|
||||
|
||||
dojo.io.createIFrame = function(fname, onloadstr){
|
||||
if(window[fname]){ return window[fname]; }
|
||||
if(window.frames[fname]){ return window.frames[fname]; }
|
||||
var r = dojo.render.html;
|
||||
var cframe = null;
|
||||
var turi = dojo.uri.dojoUri("iframe_history.html?noInit=true");
|
||||
var ifrstr = ((r.ie)&&(dojo.render.os.win)) ? "<iframe name='"+fname+"' src='"+turi+"' onload='"+onloadstr+"'>" : "iframe";
|
||||
cframe = document.createElement(ifrstr);
|
||||
with(cframe){
|
||||
name = fname;
|
||||
setAttribute("name", fname);
|
||||
id = fname;
|
||||
}
|
||||
(document.body||document.getElementsByTagName("body")[0]).appendChild(cframe);
|
||||
window[fname] = cframe;
|
||||
with(cframe.style){
|
||||
position = "absolute";
|
||||
left = top = "0px";
|
||||
height = width = "1px";
|
||||
visibility = "hidden";
|
||||
/*
|
||||
if(djConfig.isDebug){
|
||||
position = "relative";
|
||||
height = "300px";
|
||||
width = "600px";
|
||||
visibility = "visible";
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if(!r.ie){
|
||||
dojo.io.setIFrameSrc(cframe, turi, true);
|
||||
cframe.onload = new Function(onloadstr);
|
||||
}
|
||||
return cframe;
|
||||
}
|
||||
|
||||
// thanks burstlib!
|
||||
dojo.io.iframeContentWindow = function(iframe_el) {
|
||||
var win = iframe_el.contentWindow || // IE
|
||||
dojo.io.iframeContentDocument(iframe_el).defaultView || // Moz, opera
|
||||
// Moz. TODO: is this available when defaultView isn't?
|
||||
dojo.io.iframeContentDocument(iframe_el).__parent__ ||
|
||||
(iframe_el.name && document.frames[iframe_el.name]) || null;
|
||||
return win;
|
||||
}
|
||||
|
||||
dojo.io.iframeContentDocument = function(iframe_el){
|
||||
var doc = iframe_el.contentDocument || // W3
|
||||
(
|
||||
(iframe_el.contentWindow)&&(iframe_el.contentWindow.document)
|
||||
) || // IE
|
||||
(
|
||||
(iframe_el.name)&&(document.frames[iframe_el.name])&&
|
||||
(document.frames[iframe_el.name].document)
|
||||
) || null;
|
||||
return doc;
|
||||
}
|
||||
|
||||
dojo.io.IframeTransport = new function(){
|
||||
var _this = this;
|
||||
this.currentRequest = null;
|
||||
this.requestQueue = [];
|
||||
this.iframeName = "dojoIoIframe";
|
||||
|
||||
this.fireNextRequest = function(){
|
||||
if((this.currentRequest)||(this.requestQueue.length == 0)){ return; }
|
||||
// dojo.debug("fireNextRequest");
|
||||
var cr = this.currentRequest = this.requestQueue.shift();
|
||||
cr._contentToClean = [];
|
||||
var fn = cr["formNode"];
|
||||
var content = cr["content"] || {};
|
||||
if(cr.sendTransport) {
|
||||
content["dojo.transport"] = "iframe";
|
||||
}
|
||||
if(fn){
|
||||
if(content){
|
||||
// if we have things in content, we need to add them to the form
|
||||
// before submission
|
||||
for(var x in content){
|
||||
if(!fn[x]){
|
||||
var tn;
|
||||
if(dojo.render.html.ie){
|
||||
tn = document.createElement("<input type='hidden' name='"+x+"' value='"+content[x]+"'>");
|
||||
fn.appendChild(tn);
|
||||
}else{
|
||||
tn = document.createElement("input");
|
||||
fn.appendChild(tn);
|
||||
tn.type = "hidden";
|
||||
tn.name = x;
|
||||
tn.value = content[x];
|
||||
}
|
||||
cr._contentToClean.push(x);
|
||||
}else{
|
||||
fn[x].value = content[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(cr["url"]){
|
||||
cr._originalAction = fn.getAttribute("action");
|
||||
fn.setAttribute("action", cr.url);
|
||||
}
|
||||
if(!fn.getAttribute("method")){
|
||||
fn.setAttribute("method", (cr["method"]) ? cr["method"] : "post");
|
||||
}
|
||||
cr._originalTarget = fn.getAttribute("target");
|
||||
fn.setAttribute("target", this.iframeName);
|
||||
fn.target = this.iframeName;
|
||||
fn.submit();
|
||||
}else{
|
||||
// otherwise we post a GET string by changing URL location for the
|
||||
// iframe
|
||||
var query = dojo.io.argsFromMap(this.currentRequest.content);
|
||||
var tmpUrl = (cr.url.indexOf("?") > -1 ? "&" : "?") + query;
|
||||
dojo.io.setIFrameSrc(this.iframe, tmpUrl, true);
|
||||
}
|
||||
}
|
||||
|
||||
this.canHandle = function(kwArgs){
|
||||
return (
|
||||
(
|
||||
// FIXME: can we really handle text/plain and
|
||||
// text/javascript requests?
|
||||
dojo.lang.inArray(kwArgs["mimetype"],
|
||||
[ "text/plain", "text/html",
|
||||
"text/javascript", "text/json"])
|
||||
)&&(
|
||||
// make sur we really only get used in file upload cases
|
||||
(kwArgs["formNode"])&&(dojo.io.checkChildrenForFile(kwArgs["formNode"]))
|
||||
)&&(
|
||||
dojo.lang.inArray(kwArgs["method"].toLowerCase(), ["post", "get"])
|
||||
)&&(
|
||||
// never handle a sync request
|
||||
! ((kwArgs["sync"])&&(kwArgs["sync"] == true))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this.bind = function(kwArgs){
|
||||
if(!this["iframe"]){ this.setUpIframe(); }
|
||||
this.requestQueue.push(kwArgs);
|
||||
this.fireNextRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
this.setUpIframe = function(){
|
||||
|
||||
// NOTE: IE 5.0 and earlier Mozilla's don't support an onload event for
|
||||
// iframes. OTOH, we don't care.
|
||||
this.iframe = dojo.io.createIFrame(this.iframeName, "dojo.io.IframeTransport.iframeOnload();");
|
||||
}
|
||||
|
||||
this.iframeOnload = function(){
|
||||
if(!_this.currentRequest){
|
||||
_this.fireNextRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
var req = _this.currentRequest;
|
||||
|
||||
// remove all the hidden content inputs
|
||||
var toClean = req._contentToClean;
|
||||
for(var i = 0; i < toClean.length; i++) {
|
||||
var key = toClean[i];
|
||||
if(dojo.render.html.safari){
|
||||
//In Safari (at least 2.0.3), can't use formNode[key] syntax to find the node,
|
||||
//for nodes that were dynamically added.
|
||||
var fNode = req.formNode;
|
||||
for(var j = 0; j < fNode.childNodes.length; j++){
|
||||
var chNode = fNode.childNodes[j];
|
||||
if(chNode.name == key){
|
||||
var pNode = chNode.parentNode;
|
||||
pNode.removeChild(chNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
var input = req.formNode[key];
|
||||
req.formNode.removeChild(input);
|
||||
req.formNode[key] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// restore original action + target
|
||||
if(req["_originalAction"]){
|
||||
req.formNode.setAttribute("action", req._originalAction);
|
||||
}
|
||||
req.formNode.setAttribute("target", req._originalTarget);
|
||||
req.formNode.target = req._originalTarget;
|
||||
|
||||
var ifd = dojo.io.iframeContentDocument(_this.iframe);
|
||||
// handle successful returns
|
||||
// FIXME: how do we determine success for iframes? Is there an equiv of
|
||||
// the "status" property?
|
||||
var value;
|
||||
var success = false;
|
||||
|
||||
try{
|
||||
var cmt = req.mimetype;
|
||||
if((cmt == "text/javascript")||(cmt == "text/json")){
|
||||
// FIXME: not sure what to do here? try to pull some evalulable
|
||||
// text from a textarea or cdata section?
|
||||
// how should we set up the contract for that?
|
||||
var js = ifd.getElementsByTagName("textarea")[0].value;
|
||||
if(cmt == "text/json") { js = "(" + js + ")"; }
|
||||
value = dj_eval(js);
|
||||
}else if(cmt == "text/html"){
|
||||
value = ifd;
|
||||
}else{ // text/plain
|
||||
value = ifd.getElementsByTagName("textarea")[0].value;
|
||||
}
|
||||
success = true;
|
||||
}catch(e){
|
||||
// looks like we didn't get what we wanted!
|
||||
var errObj = new dojo.io.Error("IframeTransport Error");
|
||||
if(dojo.lang.isFunction(req["error"])){
|
||||
req.error("error", errObj, req);
|
||||
}
|
||||
}
|
||||
|
||||
// don't want to mix load function errors with processing errors, thus
|
||||
// a separate try..catch
|
||||
try {
|
||||
if(success && dojo.lang.isFunction(req["load"])){
|
||||
req.load("load", value, req);
|
||||
}
|
||||
} catch(e) {
|
||||
throw e;
|
||||
} finally {
|
||||
_this.currentRequest = null;
|
||||
_this.fireNextRequest();
|
||||
}
|
||||
}
|
||||
|
||||
dojo.io.transports.addTransport("IframeTransport");
|
||||
}
|
517
webapp/web/src/io/RepubsubIO.js
Normal file
517
webapp/web/src/io/RepubsubIO.js
Normal file
|
@ -0,0 +1,517 @@
|
|||
// Copyright (c) 2004 Friendster Inc., Licensed under the Academic Free
|
||||
// License version 2.0 or later
|
||||
|
||||
dojo.require("dojo.event.Event");
|
||||
dojo.require("dojo.event.BrowserEvent");
|
||||
dojo.require("dojo.io.BrowserIO");
|
||||
|
||||
dojo.provide("dojo.io.RepubsubIO");
|
||||
dojo.provide("dojo.io.repubsub");
|
||||
dojo.provide("dojo.io.repubsubTransport");
|
||||
|
||||
dojo.io.repubsubTranport = new function(){
|
||||
var rps = dojo.io.repubsub;
|
||||
this.canHandle = function(kwArgs){
|
||||
if((kwArgs["mimetype"] == "text/javascript")&&(kwArgs["method"] == "repubsub")){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
this.bind = function(kwArgs){
|
||||
if(!rps.isInitialized){
|
||||
// open up our tunnel, queue up requests anyway
|
||||
rps.init();
|
||||
}
|
||||
// FIXME: we need to turn this into a topic subscription
|
||||
// var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
|
||||
// sampleTransport.sendRequest(tgtURL, hdlrFunc);
|
||||
|
||||
// a normal "bind()" call in a request-response transport layer is
|
||||
// something that (usually) encodes most of it's payload with the
|
||||
// request. Multi-event systems like repubsub are a bit more complex,
|
||||
// and repubsub in particular distinguishes the publish and subscribe
|
||||
// portions of thep rocess with different method calls to handle each.
|
||||
// Therefore, a "bind" in the sense of repubsub must first determine if
|
||||
// we have an open subscription to a channel provided by the server,
|
||||
// and then "publish" the request payload if there is any. We therefore
|
||||
// must take care not to incorrectly or too agressively register or
|
||||
// file event handlers which are provided with the kwArgs method.
|
||||
|
||||
// NOTE: we ONLY pay attention to those event handlers that are
|
||||
// registered with the bind request that subscribes to the channel. If
|
||||
// event handlers are provided with subsequent requests, we might in
|
||||
// the future support some additive or replacement syntax, but for now
|
||||
// they get dropped on the floor.
|
||||
|
||||
// NOTE: in this case, url MUST be the "topic" to which we
|
||||
// subscribe/publish for this channel
|
||||
if(!rps.topics[kwArgs.url]){
|
||||
kwArgs.rpsLoad = function(evt){
|
||||
kwArgs.load("load", evt);
|
||||
}
|
||||
rps.subscribe(kwArgs.url, kwArgs, "rpsLoad");
|
||||
}
|
||||
|
||||
if(kwArgs["content"]){
|
||||
// what we wanted to send
|
||||
var cEvt = dojo.io.repubsubEvent.initFromProperties(kwArgs.content);
|
||||
rps.publish(kwArgs.url, cEvt);
|
||||
}
|
||||
}
|
||||
|
||||
dojo.io.transports.addTransport("repubsubTranport");
|
||||
}
|
||||
|
||||
dojo.io.repubsub = new function(){
|
||||
this.initDoc = "init.html";
|
||||
this.isInitialized = false;
|
||||
this.subscriptionBacklog = [];
|
||||
this.debug = true;
|
||||
this.rcvNodeName = null;
|
||||
this.sndNodeName = null;
|
||||
this.rcvNode = null;
|
||||
this.sndNode = null;
|
||||
this.canRcv = false;
|
||||
this.canSnd = false;
|
||||
this.canLog = false;
|
||||
this.sndTimer = null;
|
||||
this.windowRef = window;
|
||||
this.backlog = [];
|
||||
this.tunnelInitCount = 0;
|
||||
this.tunnelFrameKey = "tunnel_frame";
|
||||
this.serverBaseURL = location.protocol+"//"+location.host+location.pathname;
|
||||
this.logBacklog = [];
|
||||
this.getRandStr = function(){
|
||||
return Math.random().toString().substring(2, 10);
|
||||
}
|
||||
this.userid = "guest";
|
||||
this.tunnelID = this.getRandStr();
|
||||
this.attachPathList = [];
|
||||
this.topics = []; // list of topics we have listeners to
|
||||
|
||||
// actually, now that I think about it a little bit more, it would sure be
|
||||
// useful to parse out the <script> src attributes. We're looking for
|
||||
// something with a "do_method=lib", since that's what would have included
|
||||
// us in the first place (in the common case).
|
||||
this.parseGetStr = function(){
|
||||
var baseUrl = document.location.toString();
|
||||
var params = baseUrl.split("?", 2);
|
||||
if(params.length > 1){
|
||||
var paramStr = params[1];
|
||||
var pairs = paramStr.split("&");
|
||||
var opts = [];
|
||||
for(var x in pairs){
|
||||
var sp = pairs[x].split("=");
|
||||
// FIXME: is this eval dangerous?
|
||||
try{
|
||||
opts[sp[0]]=eval(sp[1]);
|
||||
}catch(e){
|
||||
opts[sp[0]]=sp[1];
|
||||
}
|
||||
}
|
||||
return opts;
|
||||
}else{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// parse URL params and use them as default vals
|
||||
var getOpts = this.parseGetStr();
|
||||
for(var x in getOpts){
|
||||
// FIXME: should I be checking for undefined here before setting? Does
|
||||
// that buy me anything?
|
||||
this[x] = getOpts[x];
|
||||
}
|
||||
|
||||
if(!this["tunnelURI"]){
|
||||
this.tunnelURI = [ "/who/", escape(this.userid), "/s/",
|
||||
this.getRandStr(), "/kn_journal"].join("");
|
||||
// this.tunnelURI = this.absoluteTopicURI(this.tunnelURI);
|
||||
}
|
||||
|
||||
/*
|
||||
if (self.kn_tunnelID) kn.tunnelID = self.kn_tunnelID; // the server says
|
||||
if (kn._argv.kn_tunnelID) kn.tunnelID = kn._argv.kn_tunnelID; // the url says
|
||||
*/
|
||||
|
||||
// check the options object if it exists and use its properties as an
|
||||
// over-ride
|
||||
if(window["repubsubOpts"]||window["rpsOpts"]){
|
||||
var optObj = window["repubsubOpts"]||window["rpsOpts"];
|
||||
for(var x in optObj){
|
||||
this[x] = optObj[x]; // copy the option object properties
|
||||
}
|
||||
}
|
||||
|
||||
// things that get called directly from our iframe to inform us of events
|
||||
this.tunnelCloseCallback = function(){
|
||||
// when we get this callback, we should immediately attempt to re-start
|
||||
// our tunnel connection
|
||||
dojo.io.setIFrameSrc(this.rcvNode, this.initDoc+"?callback=repubsub.rcvNodeReady&domain="+document.domain);
|
||||
}
|
||||
|
||||
this.receiveEventFromTunnel = function(evt, srcWindow){
|
||||
// we should never be getting events from windows we didn't create
|
||||
// NOTE: events sourced from the local window are also supported for
|
||||
// debugging purposes
|
||||
|
||||
// any event object MUST have a an "elements" property
|
||||
if(!evt["elements"]){
|
||||
this.log("bailing! event received without elements!", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// if the event passes some minimal sanity tests, we need to attempt to
|
||||
// dispatch it!
|
||||
|
||||
// first, it seems we have to munge the event object a bit
|
||||
var e = {};
|
||||
for(var i=0; i<evt.elements.length; i++){
|
||||
var ee = evt.elements[i];
|
||||
e[ee.name||ee.nameU] = (ee.value||ee.valueU);
|
||||
// FIXME: need to enable this only in some extreme debugging mode!
|
||||
this.log("[event]: "+(ee.name||ee.nameU)+": "+e[ee.name||ee.nameU]);
|
||||
}
|
||||
|
||||
// NOTE: the previous version of this library put a bunch of code here
|
||||
// to manage state that tried to make sure that we never, ever, lost
|
||||
// any info about an event. If we unload RIGHT HERE, I don't think it's
|
||||
// going to make a huge difference one way or another. Time will tell.
|
||||
|
||||
// and with THAT out of the way, dispatch it!
|
||||
this.dispatch(e);
|
||||
|
||||
// TODO: remove the script block that created the event obj to save
|
||||
// memory, etc.
|
||||
}
|
||||
|
||||
this.widenDomain = function(domainStr){
|
||||
// the purpose of this is to set the most liberal domain policy
|
||||
// available
|
||||
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(".");
|
||||
}
|
||||
|
||||
// FIXME: parseCookie and setCookie should be methods that are more broadly
|
||||
// available. Perhaps in htmlUtils?
|
||||
|
||||
this.parseCookie = function(){
|
||||
var cs = document.cookie;
|
||||
var keypairs = cs.split(";");
|
||||
for(var x=0; x<keypairs.length; x++){
|
||||
keypairs[x] = keypairs[x].split("=");
|
||||
if(x!=keypairs.length-1){ cs+=";"; }
|
||||
}
|
||||
return keypairs;
|
||||
}
|
||||
|
||||
this.setCookie = function(keypairs, clobber){
|
||||
// NOTE: we want to only ever set session cookies, so never provide an
|
||||
// expires date
|
||||
if((clobber)&&(clobber==true)){ document.cookie = ""; }
|
||||
var cs = "";
|
||||
for(var x=0; x<keypairs.length; x++){
|
||||
cs += keypairs[x][0]+"="+keypairs[x][1];
|
||||
if(x!=keypairs.length-1){ cs+=";"; }
|
||||
}
|
||||
document.cookie = cs;
|
||||
}
|
||||
|
||||
// FIXME: need to replace w/ dojo.log.*
|
||||
this.log = function(str, lvl){
|
||||
if(!this.debug){ return; } // we of course only care if we're in debug mode
|
||||
while(this.logBacklog.length>0){
|
||||
if(!this.canLog){ break; }
|
||||
var blo = this.logBacklog.shift();
|
||||
this.writeLog("["+blo[0]+"]: "+blo[1], blo[2]);
|
||||
}
|
||||
this.writeLog(str, lvl);
|
||||
}
|
||||
|
||||
this.writeLog = function(str, lvl){
|
||||
dojo.debug(((new Date()).toLocaleTimeString())+": "+str);
|
||||
}
|
||||
|
||||
this.init = function(){
|
||||
this.widenDomain();
|
||||
// this.findPeers();
|
||||
this.openTunnel();
|
||||
this.isInitialized = true;
|
||||
// FIXME: this seems like entirely the wrong place to replay the backlog
|
||||
while(this.subscriptionBacklog.length){
|
||||
this.subscribe.apply(this, this.subscriptionBacklog.shift());
|
||||
}
|
||||
}
|
||||
|
||||
this.clobber = function(){
|
||||
if(this.rcvNode){
|
||||
this.setCookie( [
|
||||
[this.tunnelFrameKey,"closed"],
|
||||
["path","/"]
|
||||
], false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.openTunnel = function(){
|
||||
// We create two iframes here:
|
||||
|
||||
// one for getting data
|
||||
this.rcvNodeName = "rcvIFrame_"+this.getRandStr();
|
||||
// set cookie that can be used to find the receiving iframe
|
||||
this.setCookie( [
|
||||
[this.tunnelFrameKey,this.rcvNodeName],
|
||||
["path","/"]
|
||||
], false
|
||||
);
|
||||
|
||||
this.rcvNode = dojo.io.createIFrame(this.rcvNodeName);
|
||||
// FIXME: set the src attribute here to the initialization URL
|
||||
dojo.io.setIFrameSrc(this.rcvNode, this.initDoc+"?callback=repubsub.rcvNodeReady&domain="+document.domain);
|
||||
|
||||
// the other for posting data in reply
|
||||
|
||||
this.sndNodeName = "sndIFrame_"+this.getRandStr();
|
||||
this.sndNode = dojo.io.createIFrame(this.sndNodeName);
|
||||
// FIXME: set the src attribute here to the initialization URL
|
||||
dojo.io.setIFrameSrc(this.sndNode, this.initDoc+"?callback=repubsub.sndNodeReady&domain="+document.domain);
|
||||
|
||||
}
|
||||
|
||||
this.rcvNodeReady = function(){
|
||||
// FIXME: why is this sequence number needed? Why isn't the UID gen
|
||||
// function enough?
|
||||
var statusURI = [this.tunnelURI, '/kn_status/', this.getRandStr(), '_',
|
||||
String(this.tunnelInitCount++)].join("");
|
||||
// (kn._seqNum++); // FIXME: !!!!
|
||||
// this.canRcv = true;
|
||||
this.log("rcvNodeReady");
|
||||
// FIXME: initialize receiver and request the base topic
|
||||
// dojo.io.setIFrameSrc(this.rcvNode, this.serverBaseURL+"/kn?do_method=blank");
|
||||
var initURIArr = [ this.serverBaseURL, "/kn?kn_from=", escape(this.tunnelURI),
|
||||
"&kn_id=", escape(this.tunnelID), "&kn_status_from=",
|
||||
escape(statusURI)];
|
||||
// FIXME: does the above really need a kn_response_flush? won't the
|
||||
// server already know? If not, what good is it anyway?
|
||||
dojo.io.setIFrameSrc(this.rcvNode, initURIArr.join(""));
|
||||
|
||||
// setup a status path listener, but don't tell the server about it,
|
||||
// since it already knows we're itnerested in our own tunnel status
|
||||
this.subscribe(statusURI, this, "statusListener", true);
|
||||
|
||||
this.log(initURIArr.join(""));
|
||||
}
|
||||
|
||||
this.sndNodeReady = function(){
|
||||
this.canSnd = true;
|
||||
this.log("sndNodeReady");
|
||||
this.log(this.backlog.length);
|
||||
// FIXME: handle any pent-up send commands
|
||||
if(this.backlog.length > 0){
|
||||
this.dequeueEvent();
|
||||
}
|
||||
}
|
||||
|
||||
this.statusListener = function(evt){
|
||||
this.log("status listener called");
|
||||
this.log(evt.status, "info");
|
||||
}
|
||||
|
||||
// this handles local event propigation
|
||||
this.dispatch = function(evt){
|
||||
// figure out what topic it came from
|
||||
if(evt["to"]||evt["kn_routed_from"]){
|
||||
var rf = evt["to"]||evt["kn_routed_from"];
|
||||
// split off the base server URL
|
||||
var topic = rf.split(this.serverBaseURL, 2)[1];
|
||||
if(!topic){
|
||||
// FIXME: how do we recover when we don't get a sane "from"? Do
|
||||
// we try to route to it anyway?
|
||||
topic = rf;
|
||||
}
|
||||
this.log("[topic] "+topic);
|
||||
if(topic.length>3){
|
||||
if(topic.slice(0, 3)=="/kn"){
|
||||
topic = topic.slice(3);
|
||||
}
|
||||
}
|
||||
if(this.attachPathList[topic]){
|
||||
this.attachPathList[topic](evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.subscribe = function( topic /* kn_from in the old terminilogy */,
|
||||
toObj, toFunc, dontTellServer){
|
||||
if(!this.isInitialized){
|
||||
this.subscriptionBacklog.push([topic, toObj, toFunc, dontTellServer]);
|
||||
return;
|
||||
}
|
||||
if(!this.attachPathList[topic]){
|
||||
this.attachPathList[topic] = function(){ return true; }
|
||||
this.log("subscribing to: "+topic);
|
||||
this.topics.push(topic);
|
||||
}
|
||||
var revt = new dojo.io.repubsubEvent(this.tunnelURI, topic, "route");
|
||||
var rstr = [this.serverBaseURL+"/kn", revt.toGetString()].join("");
|
||||
dojo.event.kwConnect({
|
||||
once: true,
|
||||
srcObj: this.attachPathList,
|
||||
srcFunc: topic,
|
||||
adviceObj: toObj,
|
||||
adviceFunc: toFunc
|
||||
});
|
||||
// NOTE: the above is a local mapping, if we're not the leader, we
|
||||
// should connect our mapping to the topic handler of the peer
|
||||
// leader, this ensures that not matter what happens to the
|
||||
// leader, we don't really loose our heads if/when the leader
|
||||
// goes away.
|
||||
if(!this.rcvNode){ /* this should be an error! */ }
|
||||
if(dontTellServer){
|
||||
return;
|
||||
}
|
||||
this.log("sending subscription to: "+topic);
|
||||
// create a subscription event object and give it all the props we need
|
||||
// to updates on the specified topic
|
||||
|
||||
// FIXME: we should only enqueue if this is our first subscription!
|
||||
this.sendTopicSubToServer(topic, rstr);
|
||||
}
|
||||
|
||||
this.sendTopicSubToServer = function(topic, str){
|
||||
if(!this.attachPathList[topic]["subscriptions"]){
|
||||
this.enqueueEventStr(str);
|
||||
this.attachPathList[topic].subscriptions = 0;
|
||||
}
|
||||
this.attachPathList[topic].subscriptions++;
|
||||
}
|
||||
|
||||
this.unSubscribe = function(topic, toObj, toFunc){
|
||||
// first, locally disconnect
|
||||
dojo.event.kwDisconnect({
|
||||
srcObj: this.attachPathList,
|
||||
srcFunc: topic,
|
||||
adviceObj: toObj,
|
||||
adviceFunc: toFunc
|
||||
});
|
||||
|
||||
// FIXME: figure out if there are any remaining listeners to the topic,
|
||||
// and if not, inform the server of our desire not to be
|
||||
// notified of updates to the topic
|
||||
}
|
||||
|
||||
// the "publish" method is really a misnomer, since it really means "take
|
||||
// this event and send it to the server". Note that the "dispatch" method
|
||||
// handles local event promigulation, and therefore we emulate both sides
|
||||
// of a real event router without having to swallow all of the complexity.
|
||||
this.publish = function(topic, event){
|
||||
var evt = dojo.io.repubsubEvent.initFromProperties(event);
|
||||
// FIXME: need to make sure we have from and to set correctly
|
||||
// before we serialize and send off to the great blue
|
||||
// younder.
|
||||
evt.to = topic;
|
||||
// evt.from = this.tunnelURI;
|
||||
|
||||
var evtURLParts = [];
|
||||
evtURLParts.push(this.serverBaseURL+"/kn");
|
||||
|
||||
// serialize the event to a string and then post it to the correct
|
||||
// topic
|
||||
evtURLParts.push(evt.toGetString());
|
||||
this.enqueueEventStr(evtURLParts.join(""));
|
||||
}
|
||||
|
||||
this.enqueueEventStr = function(evtStr){
|
||||
this.log("enqueueEventStr");
|
||||
this.backlog.push(evtStr);
|
||||
this.dequeueEvent();
|
||||
}
|
||||
|
||||
this.dequeueEvent = function(force){
|
||||
this.log("dequeueEvent");
|
||||
if(this.backlog.length <= 0){ return; }
|
||||
if((this.canSnd)||(force)){
|
||||
dojo.io.setIFrameSrc(this.sndNode, this.backlog.shift()+"&callback=repubsub.sndNodeReady");
|
||||
this.canSnd = false;
|
||||
}else{
|
||||
this.log("sndNode not available yet!", "debug");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dojo.io.repubsubEvent = function(to, from, method, id, routeURI, payload, dispname, uid){
|
||||
this.to = to;
|
||||
this.from = from;
|
||||
this.method = method||"route";
|
||||
this.id = id||repubsub.getRandStr();
|
||||
this.uri = routeURI;
|
||||
this.displayname = dispname||repubsub.displayname;
|
||||
this.userid = uid||repubsub.userid;
|
||||
this.payload = payload||"";
|
||||
this.flushChars = 4096;
|
||||
|
||||
this.initFromProperties = function(evt){
|
||||
if(evt.constructor = dojo.io.repubsubEvent){
|
||||
for(var x in evt){
|
||||
this[x] = evt[x];
|
||||
}
|
||||
}else{
|
||||
// we want to copy all the properties of the evt object, and transform
|
||||
// those that are "stock" properties of dojo.io.repubsubEvent. All others should
|
||||
// be copied as-is
|
||||
for(var x in evt){
|
||||
if(typeof this.forwardPropertiesMap[x] == "string"){
|
||||
this[this.forwardPropertiesMap[x]] = evt[x];
|
||||
}else{
|
||||
this[x] = evt[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.toGetString = function(noQmark){
|
||||
var qs = [ ((noQmark) ? "" : "?") ];
|
||||
for(var x=0; x<this.properties.length; x++){
|
||||
var tp = this.properties[x];
|
||||
if(this[tp[0]]){
|
||||
qs.push(tp[1]+"="+encodeURIComponent(String(this[tp[0]])));
|
||||
}
|
||||
// FIXME: we need to be able to serialize non-stock properties!!!
|
||||
}
|
||||
return qs.join("&");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dojo.io.repubsubEvent.prototype.properties = [["from", "kn_from"], ["to", "kn_to"],
|
||||
["method", "do_method"], ["id", "kn_id"],
|
||||
["uri", "kn_uri"],
|
||||
["displayname", "kn_displayname"],
|
||||
["userid", "kn_userid"],
|
||||
["payload", "kn_payload"],
|
||||
["flushChars", "kn_response_flush"],
|
||||
["responseFormat", "kn_response_format"] ];
|
||||
|
||||
// maps properties from their old names to their new names...
|
||||
dojo.io.repubsubEvent.prototype.forwardPropertiesMap = {};
|
||||
// ...and vice versa...
|
||||
dojo.io.repubsubEvent.prototype.reversePropertiesMap = {};
|
||||
|
||||
// and we then populate them both from the properties list
|
||||
for(var x=0; x<dojo.io.repubsubEvent.prototype.properties.length; x++){
|
||||
var tp = dojo.io.repubsubEvent.prototype.properties[x];
|
||||
dojo.io.repubsubEvent.prototype.reversePropertiesMap[tp[0]] = tp[1];
|
||||
dojo.io.repubsubEvent.prototype.forwardPropertiesMap[tp[1]] = tp[0];
|
||||
}
|
||||
// static version of initFromProperties, creates new event and object and
|
||||
// returns it after init
|
||||
dojo.io.repubsubEvent.initFromProperties = function(evt){
|
||||
var eventObj = new dojo.io.repubsubEvent();
|
||||
eventObj.initFromProperties(evt);
|
||||
return eventObj;
|
||||
}
|
22
webapp/web/src/io/RhinoIO.js
Normal file
22
webapp/web/src/io/RhinoIO.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
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.RhinoIO");
|
||||
|
||||
// TODO: this doesn't execute
|
||||
/*dojo.io.SyncHTTPRequest = function(){
|
||||
dojo.io.SyncRequest.call(this);
|
||||
|
||||
this.send = function(URI){
|
||||
}
|
||||
}
|
||||
|
||||
dojo.inherits(dojo.io.SyncHTTPRequest, dojo.io.SyncRequest);
|
||||
*/
|
452
webapp/web/src/io/ScriptSrcIO.js
Normal file
452
webapp/web/src/io/ScriptSrcIO.js
Normal file
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
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{
|
||||
var listener = currentState.kwArgs;
|
||||
try{
|
||||
if(currentState.checkString && eval("typeof(" + currentState.checkString + ") != 'undefined'")){
|
||||
this._finish(currentState, "load");
|
||||
doneCount++;
|
||||
delete this._state[param];
|
||||
}else if(listener.timeoutSeconds && listener.timeout){
|
||||
if(currentState.startTime + (listener.timeoutSeconds * 1000) < (new Date()).getTime()){
|
||||
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){
|
||||
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((kwArgs["mimetype"].toLowerCase()), ["text/javascript", "text/json"])
|
||||
&& (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()
|
||||
};
|
||||
|
||||
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"]){
|
||||
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);
|
||||
};
|
171
webapp/web/src/io/ShortBusIO.js
Normal file
171
webapp/web/src/io/ShortBusIO.js
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
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.ShortBusIO");
|
||||
dojo.require("dojo.io"); // io.js provides setIFrameSrc
|
||||
// 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"); // for posting across domains
|
||||
dojo.require("dojo.io.cookie"); // for peering
|
||||
dojo.require("dojo.event.*");
|
||||
|
||||
/*
|
||||
* this file defines a "forever-frame" style Comet client. It passes opaque
|
||||
* JSON data structures to/from the client. Both styles of request provide a
|
||||
* topic for the event to be sent to and a payload object to be acted upon.
|
||||
*
|
||||
* All outbound events are sent via dojo.io.bind() and all inbound requests are
|
||||
* processed by Dojo topic dispatch.
|
||||
*
|
||||
* ShortBusIO packets have the basic format:
|
||||
*
|
||||
* {
|
||||
* topic: "/destination/topic/name",
|
||||
* body: {
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Packets bound for the event router (not one of it's clients) or generated
|
||||
* from it are prefixed with the special "/meta" topic. Meta-topic events
|
||||
* either inform the client to take an action or inform the server of a system
|
||||
* event.
|
||||
*
|
||||
* Upon tunnel creation, the server might therefore send the following meta
|
||||
* topic packet to the client to inform the client of it's assigned identity:
|
||||
*
|
||||
* // client <-- server
|
||||
* {
|
||||
* topic: "/meta",
|
||||
* body: {
|
||||
* action: "setClientId",
|
||||
* clientId: "fooBar23",
|
||||
* tunnelId: "fooBarTunnel4",
|
||||
* tunnelExpiration: "...", // some date in the future
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The client may then respond with a confirmation:
|
||||
*
|
||||
* // client --> server
|
||||
* {
|
||||
* topic: "/meta",
|
||||
* body: {
|
||||
* action: "confirmClientId",
|
||||
* from: "fooBar23"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The client must implement a basic vocabulary of /meta topic verbs in order
|
||||
* to participate as a ShortBus endpoint. These are TBD.
|
||||
*
|
||||
* NOTE: this example elides any authentication or authorization steps the
|
||||
* client and server may have undertaken prior to tunnel setup.
|
||||
*/
|
||||
|
||||
// TODO: unlike repubsubio we don't handle any sort of connection
|
||||
// subscription/publishing backlog. Should we?
|
||||
|
||||
dojo.io.ShortBusTransport = new function(){
|
||||
|
||||
var initialized = false;
|
||||
var connected = false;
|
||||
|
||||
// this class is similar to RepubsubIO save that we don't have the
|
||||
// externalized protocol handler code. Our messages are simpler so our code
|
||||
// can be as well.
|
||||
|
||||
this.rcvNode = null;
|
||||
this.rcvNodeName = "";
|
||||
this.topicRoot = null;
|
||||
|
||||
this.getRandStr = function(){
|
||||
return Math.random().toString().substring(2, 10);
|
||||
}
|
||||
|
||||
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(".");
|
||||
}
|
||||
|
||||
this.canHandle = function(kwArgs){
|
||||
return (
|
||||
(connected) &&
|
||||
(kwArgs["topic"]) &&
|
||||
(! // async only!
|
||||
((kwArgs["sync"])&&(kwArgs["sync"] == true))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this.buildConnection = function(){
|
||||
// NOTE: we require the server to cooperate by hosting
|
||||
// ShortBusInit.html at the designated endpoint
|
||||
this.rcvNodeName = "ShortBusRcv_"+this.getRandStr();
|
||||
// the "forever frame" approach
|
||||
if(dojo.render.html.ie){
|
||||
// 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='"+this.topicRoot+"/?tunntelType=htmlfile'></iframe>"
|
||||
// and we're ready to go!
|
||||
connected = true;
|
||||
}else{
|
||||
this.rcvNode = dojo.io.createIFrame(this.rcvNodeName);
|
||||
dojo.io.setIFrameSrc(this.rcvNode, this.topicRoot+"/?tunnelType=iframe");
|
||||
// we're still waiting on this one to call back up and advertise
|
||||
// that it's been initialized
|
||||
}
|
||||
}
|
||||
|
||||
this.iframeConnectionInit = function(){
|
||||
connected = true;
|
||||
}
|
||||
|
||||
this.dispatchServerEvent = function(eObj){
|
||||
// FIXME: implement basic /meta topic semantics here!
|
||||
}
|
||||
|
||||
this.init = function(){
|
||||
if(initialized){
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
this.widenDomain();
|
||||
|
||||
// we want to set up a connection to the designated server. Grab the
|
||||
// server location out of djConfig.
|
||||
this.topicRoot = djConfig["ShortBusRoot"];
|
||||
if(!this.topicRoot){
|
||||
dojo.debug("no topic root specified in djConfig.ShortBusRoot");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.dispatch = function(evt){
|
||||
// dipatch events along the specified path
|
||||
}
|
||||
|
||||
dojo.io.transports.addTransport("ShortBusTransport");
|
||||
}
|
75
webapp/web/src/io/ShortBusInit.html
Normal file
75
webapp/web/src/io/ShortBusInit.html
Normal file
|
@ -0,0 +1,75 @@
|
|||
<html>
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
<script type="text/javascript">
|
||||
if(window!=window.parent){
|
||||
function callByDeRef(fname){
|
||||
if(!fname){ return null; }
|
||||
// if someone inadvertently passed in "foo(...)", we make it "foo"
|
||||
fname = String(fname).split("(")[0];
|
||||
// get a real array of arguments
|
||||
var aa = [];
|
||||
for(var x=1; x<arguments.length; x++){
|
||||
aa.push(arguments[x]);
|
||||
}
|
||||
|
||||
var parts = String(fname).split(".");
|
||||
var obj = window;
|
||||
for(var x=0; x<parts.length-1; x++){
|
||||
obj = obj[parts[x]];
|
||||
}
|
||||
var fn = parts.pop(); // the last element is the function name
|
||||
// exec the function in the specified namespace
|
||||
return obj[fn].apply(obj, aa);
|
||||
}
|
||||
|
||||
function widenDomain(domainStr){
|
||||
// the purpose of this is to set the most liberal domain policy
|
||||
var cd = domainStr||document.domain;
|
||||
if(cd.indexOf(".")==-1){
|
||||
document.domain = cd;
|
||||
return;
|
||||
}
|
||||
var dps = cd.split(".");
|
||||
if(dps.length>2){
|
||||
dps = dps.slice(dps.length-2);
|
||||
}
|
||||
document.domain = dps.join(".");
|
||||
}
|
||||
|
||||
function doInit(){
|
||||
|
||||
widenDomain();
|
||||
|
||||
var baseUrl = document.location.toString();
|
||||
var params = baseUrl.split("?", 2);
|
||||
if(params.length > 1){
|
||||
var paramStr = params[1];
|
||||
var pairs = paramStr.split("&");
|
||||
var opts = [];
|
||||
for(var x in pairs){
|
||||
// alert(pairs[x]);
|
||||
var sp = pairs[x].split("=");
|
||||
opts[sp[0]]=sp[1];
|
||||
if(sp[0]=="true"){
|
||||
sp[0] = true;
|
||||
}else if(sp[0]=="false"){
|
||||
sp[0] = false;
|
||||
}
|
||||
}
|
||||
if(opts["callback"]){
|
||||
callByDeRef("parent."+opts["callback"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
doInit();
|
||||
}
|
||||
</script>
|
||||
</html>
|
17
webapp/web/src/io/__package__.js
Normal file
17
webapp/web/src/io/__package__.js
Normal file
|
@ -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"],
|
||||
rhino: ["dojo.io.RhinoIO"],
|
||||
browser: ["dojo.io.BrowserIO", "dojo.io.cookie"],
|
||||
dashboard: ["dojo.io.BrowserIO", "dojo.io.cookie"]
|
||||
});
|
||||
dojo.provide("dojo.io.*");
|
108
webapp/web/src/io/cookie.js
Normal file
108
webapp/web/src/io/cookie.js
Normal file
|
@ -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; }
|
14
webapp/web/src/io/cookies.js
Normal file
14
webapp/web/src/io/cookies.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
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.deprecated("dojo.io.cookies", "replaced by dojo.io.cookie", "0.4");
|
||||
dojo.require("dojo.io.cookie");
|
||||
if(!dojo.io.cookies) { dojo.io.cookies = dojo.io.cookie; }
|
||||
dojo.provide("dojo.io.cookies");
|
Loading…
Add table
Add a link
Reference in a new issue