/* * copyright 2005 joe walker * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ /** * the dwr object is defined by dwr.util etc. */ if (typeof this['dwr'] == 'undefined') { dwr = { }; } (function() { dwr.engine = { }; /** * set an alternative error handler from the default alert box. * @param {function} handler the function to call when an error happens * @see http://getahead.org/dwr/browser/engine/errors */ dwr.engine.seterrorhandler = function(handler) { dwr.engine._errorhandler = handler; }; /** * set an alternative warning handler from the default alert box. * @param {function} handler the function to call when a warning happens * @see http://getahead.org/dwr/browser/engine/errors */ dwr.engine.setwarninghandler = function(handler) { dwr.engine._warninghandler = handler; }; /** * setter for the text/html handler - what happens if a dwr request gets an html * reply rather than the expected javascript. often due to login timeout * @param {function} handler the function to call on an unexpected text/html content type */ dwr.engine.settexthtmlhandler = function(handler) { dwr.engine._texthtmlhandler = handler; }; /** * set a default timeout value for all calls. 0 (the default) turns timeouts off. * @param {function} handler the function to call when we get bored of waiting for a call * @see getahead.org/dwr/browser/engine/errors */ dwr.engine.settimeout = function(timeout) { dwr.engine._timeout = timeout; }; /** * the pre-hook is called before any dwr remoting is done. * @param {function} handler the function to call before any remote calls * @see getahead.org/dwr/browser/engine/hooks */ dwr.engine.setprehook = function(handler) { dwr.engine._prehook = handler; }; /** * the post-hook is called after any dwr remoting is done. * @param {function} handler the function to call after any remote calls * @see getahead.org/dwr/browser/engine/hooks */ dwr.engine.setposthook = function(handler) { dwr.engine._posthook = handler; }; /** * custom headers for all dwr calls * @param {object} headers object containing name/value pairs for extra headers * @see getahead.org/dwr/???? */ dwr.engine.setheaders = function(headers) { dwr.engine._headers = headers; }; /** * custom parameters for all dwr calls * @param {object} parameters object containing name/value pairs for extra request parameters * @see getahead.org/dwr/???? */ dwr.engine.setparameters = function(parameters) { dwr.engine._parameters = parameters; }; /** * ensure that remote calls happen in the order in which they were sent? (default: false) * @param {boolean} ordered true to enable ordered processing * @see getahead.org/dwr/browser/engine/ordering */ dwr.engine.setordered = function(ordered) { dwr.engine._ordered = ordered; }; /** * do we ask the xhr object to be asynchronous? (default: true) * warning: it is highly advised to use the default ofasync * processing, especially when dealing with internet based requests. * @param {boolean} async false to enable sync processing for xhr queries * @see getahead.org/dwr/browser/engine/options */ dwr.engine.setasync = function(async) { dwr.engine._async = async; }; /** * does the client actively check the server for updates? (default: false) * @param {boolean} async true to enable low latency reverse ajax * @see getahead.org/dwr/browser/engine/options */ dwr.engine.setactivereverseajax = function(activereverseajax) { if (activereverseajax) { // bail if we are already started if (dwr.engine._activereverseajax) return; dwr.engine._activereverseajax = true; dwr.engine._poll(); } else { // can we cancel an existing request? if (dwr.engine._activereverseajax && dwr.engine._pollreq) dwr.engine._pollreq.abort(); dwr.engine._activereverseajax = false; } // todo: in iframe mode, if we start, stop, start then the second start may // well kick off a second iframe while the first is still about to return // we should cope with this but we don't }; /** * turn server notification of page unload on and off * @param {boolean} notify true or false depending on if we want to turn unload on or off * @see getahead.org/dwr/browser/engine/options */ dwr.engine.setnotifyserveronpageunload = function(notify) { if (notify == dwr.engine._isnotifyserveronpageunload) return; if (notify) { if (window.addeventlistener) window.addeventlistener('unload', dwr.engine._unloader, false); else if (window.attachevent) window.attachevent('onunload', dwr.engine._unloader); } else { if (window.removeeventlistener) window.removeeventlistener('unload', dwr.engine._unloader, false); else if (window.detachevent) window.detachevent('onunload', dwr.engine._unloader); } dwr.engine._isnotifyserveronpageunload = notify; }; /** * the default message handler. * @param {string} message the text of the error message * @param {object} ex an error object containing at least a name and message * @see getahead.org/dwr/browser/engine/errors */ dwr.engine.defaulterrorhandler = function(message, ex) { dwr.engine._debug("error: " + ex.name + ", " + ex.message, true); if (message == null || message == "") alert("a server error has occurred."); // ignore ns_error_not_available if mozilla is being narky else if (message.indexof("0x80040111") != -1) dwr.engine._debug(message); else alert(message); }; /** * the default warning handler. * @param {string} message the text of the error message * @param {object} ex an error object containing at least a name and message * @see getahead.org/dwr/browser/engine/errors */ dwr.engine.defaultwarninghandler = function(message, ex) { dwr.engine._debug(message); }; /** * for reduced latency you can group several remote calls together using a batch. * @see getahead.org/dwr/browser/engine/batch */ dwr.engine.beginbatch = function() { if (dwr.engine._batch) { dwr.engine._handleerror(null, { name:"dwr.engine.batchbegun", message:"batch already begun" }); return; } dwr.engine._batch = dwr.engine.batch.create(); }; /** * finished grouping a set of remote calls together. go and execute them all. * @param {object} options a options object to customize processing * @see getahead.org/dwr/browser/engine/batch */ dwr.engine.endbatch = function(options) { var batch = dwr.engine._batch; if (batch == null) { dwr.engine._handleerror(null, { name:"dwr.engine.batchnotbegun", message:"no batch in progress" }); return; } dwr.engine._batch = null; if (batch.map.callcount == 0) return; // the hooks need to be merged carefully to preserve ordering if (options) dwr.engine.batch.merge(batch, options); // in ordered mode, we don't send unless the list of sent items is empty if (dwr.engine._ordered && dwr.engine._batcheslength != 0) { dwr.engine._batchqueue[dwr.engine._batchqueue.length] = batch; } else { dwr.engine.transport.send(batch); } }; /** * for use with file downloads. when a dwr function returns a binary download * you can prompt the user to save it using this function * @param {object} data the binary data passed from dwr */ dwr.engine.openindownload = function(data) { var div = document.createelement("div"); document.body.appendchild(div); div.innerhtml = ""; }; //============================================================================== // only private stuff below here //============================================================================== /** the session cookie name */ dwr.engine._sessioncookiename = "jsessionid"; // jsessionid /** is get enabled for the benefit of safari? */ dwr.engine._allowgetforsafaributmakeforgeryeasier = "false"; /** the script prefix to strip in the case of scripttagprotection. */ dwr.engine._scripttagprotection = "throw 'allowscripttagremoting is false.';"; /** the default path to the dwr servlet */ dwr.engine._defaultpath = "/dwr"; /** do we use xhr for reverse ajax because we are not streaming? */ dwr.engine._pollwithxhr = "false"; /** these urls can be configured from the server */ dwr.engine._modeplaincall = "/call/plaincall/"; dwr.engine._modeplainpoll = "/call/plainpoll/"; dwr.engine._modehtmlcall = "/call/htmlcall/"; dwr.engine._modehtmlpoll = "/call/htmlpoll/"; /** the page id */ dwr.engine._scriptsessionid = null; /** a function to be called before requests are marshalled. can be null. */ dwr.engine._prehook = null; /** a function to be called after replies are received. can be null. */ dwr.engine._posthook = null; /** an map of the batches that we have sent and are awaiting a reply on. */ dwr.engine._batches = {}; /** a count of the number of outstanding batches. should be == to _batches.length unless prototype has messed things up */ dwr.engine._batcheslength = 0; /** in ordered mode, the array of batches waiting to be sent */ dwr.engine._batchqueue = []; /** do we attempt to ensure that calls happen in the order in which they were sent? this starts true until we have fetched the ids, when it is to false */ dwr.engine._ordered = true; /** do we make the calls async? */ dwr.engine._async = true; /** the current batch (if we are in batch mode) */ dwr.engine._batch = null; /** the global timeout */ dwr.engine._timeout = 0; /** are we doing comet or polling? */ dwr.engine._activereverseajax = false; /** the xhr object that we are using to poll */ dwr.engine._pollreq = null; /** how many milliseconds between internal comet polls */ dwr.engine._pollcometinterval = 200; /** how many times have we re-tried to poll? */ dwr.engine._pollretries = 0; dwr.engine._maxpollretries = 10; /** the intervals between successive retries in seconds */ dwr.engine._retryintervals = [ 2, 5, 10, 60, 300 ]; /** do we do a document.reload if we get a text/html reply? */ dwr.engine._texthtmlhandler = null; /** if you wish to send custom headers with every request */ dwr.engine._headers = null; /** if you wish to send extra custom request parameters with each request */ dwr.engine._parameters = null; /** batch ids allow us to know which batch the server is answering */ dwr.engine._nextbatchid = 0; /** a list of the properties that need merging from calls to a batch */ dwr.engine._propnames = [ "async", "timeout", "errorhandler", "warninghandler", "texthtmlhandler" ]; /** do we stream, or can be hacked to do so? */ dwr.engine._partialresponseno = 0; dwr.engine._partialresponseyes = 1; dwr.engine._partialresponseflush = 2; /** are we doing page unloading? */ dwr.engine._isnotifyserveronpageunload = false; /** * find the http session id sent by the web server * @private */ dwr.engine._gethttpsessionid = function() { // try to find the httpsessionid var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = cookies[i]; while (cookie.charat(0) == ' ') cookie = cookie.substring(1, cookie.length); if (cookie.indexof(dwr.engine._sessioncookiename + "=") == 0) { return cookie.substring(dwr.engine._sessioncookiename.length + 1, cookie.length); } } return ""; }; /** a function to call if something fails. */ dwr.engine._errorhandler = dwr.engine.defaulterrorhandler; /** for debugging when something unexplained happens. */ dwr.engine._warninghandler = dwr.engine.defaultwarninghandler; /** undocumented interceptors - do not use */ dwr.engine._postseperator = "\n"; dwr.engine._defaultinterceptor = function(data) { return data; }; dwr.engine._urlrewritehandler = dwr.engine._defaultinterceptor; dwr.engine._contentrewritehandler = dwr.engine._defaultinterceptor; dwr.engine._replyrewritehandler = dwr.engine._defaultinterceptor; /** @private if we have used reverse ajax then we try to tell the server we are gone */ dwr.engine._unloader = function() { dwr.engine._debug("calling unloader for: " + dwr.engine._scriptsessionid); var batch = { map:{ callcount:1, 'c0-scriptname':'__system', 'c0-methodname':'pageunloaded', 'c0-id':0 }, paramcount:0, ispoll:false, async:true, headers:{}, prehooks:[], posthooks:[], timeout:dwr.engine._timeout, errorhandler:null, warninghandler:null, texthtmlhandler:null, path:dwr.engine._defaultpath, handlers:[{ exceptionhandler:null, callback:null }] }; dwr.engine.transport.send(batch); dwr.engine.setnotifyserveronpageunload(false); }; /** * send a request. called by the javascript interface stub * @private * @param path part of url after the host and before the exec bit without leading or trailing /s * @param scriptname the class to execute * @param methodname the method on said class to execute * @param func the callback function to which any returned data should be passed * if this is null, any returned data will be ignored * @param vararg_params the parameters to pass to the above class */ dwr.engine._execute = function(path, scriptname, methodname, vararg_params) { var singleshot = false; if (dwr.engine._batch == null) { dwr.engine.beginbatch(); singleshot = true; } // to make them easy to manipulate we copy the arguments into an args array var args = []; for (var i = 0; i < arguments.length - 3; i++) { args[i] = arguments[i + 3]; } var batch = dwr.engine._batch; // all the paths must be to the same servlet if (batch.path == null) { batch.path = path; } else { if (batch.path != path) { dwr.engine._handleerror(batch, { name:"dwr.engine.multipleservlets", message:"can't batch requests to multiple dwr servlets." }); return; } } dwr.engine.batch.addcall(batch, scriptname, methodname, args); // now we have finished remembering the call, we increment the call count batch.map.callcount++; if (singleshot) dwr.engine.endbatch(); }; /** * poll the server to see if there is any data waiting * @private * @param {object} overridepath */ dwr.engine._poll = function(overridepath) { if (!dwr.engine._activereverseajax) { return; } var batch = dwr.engine.batch.createpoll(); dwr.engine.transport.send(batch); }; /** * try to recover from polling errors * @param {object} msg * @param {object} ex */ dwr.engine._pollerrorhandler = function(msg, ex) { if (dwr.engine._pollretries > dwr.engine._maxpollretries) { dwr.engine._activereverseajax = false; dwr.engine._debug("reverse ajax poll failed (retries=" + dwr.engine._pollretries + "). giving up: " + ex.name + " : " + ex.message); dwr.engine._debug("giving up."); return; } var index = dwr.engine._pollretries; if (index >= dwr.engine._retryintervals.length) { index = dwr.engine._retryintervals.length - 1; } dwr.engine._debug("reverse ajax poll failed (retries=" + dwr.engine._pollretries + "). trying again in " + dwr.engine._retryintervals[index] + "s: " + ex.name + " : " + ex.message); settimeout("dwr.engine._poll()", 1000 * dwr.engine._retryintervals[index]); dwr.engine._pollretries++; }; /** @private this is a hack to make the context be this window */ dwr.engine._eval = function(script) { if (script == null) { return null; } if (script == "") { dwr.engine._debug("warning: blank script", true); return null; } // dwr.engine._debug("exec: [" + script + "]", true); return eval(script); }; /** @private call all the post hooks for a batch */ dwr.engine._callposthooks = function(batch) { if (batch.posthooks) { for (var i = 0; i < batch.posthooks.length; i++) { batch.posthooks[i](); } batch.posthooks = null; } }; /** * generic error handling routing to save having null checks everywhere * @private * @param {object} batch * @param {object} ex */ dwr.engine._handleerror = function(batch, ex) { if (typeof ex == "string") ex = { name:"unknown", message:ex }; if (ex.message == null) ex.message = ""; if (ex.name == null) ex.name = "unknown"; if (batch && typeof batch.errorhandler == "function") batch.errorhandler(ex.message, ex); else if (dwr.engine._errorhandler) dwr.engine._errorhandler(ex.message, ex); if (batch) dwr.engine.batch.remove(batch); }; /** * generic error handling routing to save having null checks everywhere * @private * @param {object} batch * @param {object} ex */ dwr.engine._handlewarning = function(batch, ex) { if (typeof ex == "string") ex = { name:"unknown", message:ex }; if (ex.message == null) ex.message = ""; if (ex.name == null) ex.name = "unknown"; if (batch && typeof batch.warninghandler == "function") batch.warninghandler(ex.message, ex); else if (dwr.engine._warninghandler) dwr.engine._warninghandler(ex.message, ex); if (batch) dwr.engine.batch.remove(batch); }; /** * used internally when some message needs to get to the programmer * @private * @param {string} message * @param {object} stacktrace */ dwr.engine._debug = function(message, stacktrace) { var written = false; try { if (window.console) { if (stacktrace && window.console.trace) window.console.trace(); window.console.log(message); written = true; } else if (window.jaxer) { jaxer.log.info(message); } else if (window.opera && window.opera.posterror) { window.opera.posterror(message); written = true; } } catch (ex) { /* ignore */ } if (!written) { var debug = document.getelementbyid("dwr-debug"); if (debug) { var contents = message + "
" + debug.innerhtml; if (contents.length > 2048) contents = contents.substring(0, 2048); debug.innerhtml = contents; } } }; /** * functions called by the server */ dwr.engine.remote = { /** * execute a callback * @private * @param {int} batchid the id of the batch that we are replying to * @param {int} callid the call id that the script relates to * @param {string} reply the script to execute */ handlecallback:function(batchid, callid, reply) { var batch = dwr.engine._batches[batchid]; if (batch == null) { dwr.engine._debug("warning: batch == null in remotehandlecallback for batchid=" + batchid, true); return; } // error handlers inside here indicate an error that is nothing to do // with dwr so we handle them differently. try { var handlers = batch.handlers[callid]; if (!handlers) { dwr.engine._debug("warning: missing handlers. callid=" + callid, true); } else { batch.handlers[callid] = null; if (typeof handlers.callback == "function") { handlers.callback.call(handlers.callbackscope, reply, handlers.callbackargs); } } } catch (ex) { dwr.engine._handleerror(batch, ex); } }, /** * called by the server: handle an exception for a call * @private * @param {int} batchid the id of the batch that we are replying to * @param {int} callid the call id that the script relates to * @param {string} reply the script to execute */ handleexception:function(batchid, callid, ex) { var batch = dwr.engine._batches[batchid]; if (batch == null) { dwr.engine._debug("warning: null batch in remotehandleexception", true); return; } var handlers = batch.handlers[callid]; batch.handlers[callid] = null; if (handlers == null) { dwr.engine._debug("warning: null handlers in remotehandleexception", true); return; } if (ex.message == undefined) { ex.message = ""; } if (typeof handlers.exceptionhandler == "function") { handlers.exceptionhandler.call(handlers.exceptionscope, ex.message, ex, handlers.exceptionargs); } else if (typeof batch.errorhandler == "function") { batch.errorhandler(ex.message, ex); } }, /** * called by the server: the whole batch is broken * @private * @param {object} ex the data about what broke * @param {int} batchid the id of the batch that we are replying to */ handlebatchexception:function(ex, batchid) { var searchbatch = (dwr.engine._receivedbatch == null && batchid != null); if (searchbatch) { dwr.engine._receivedbatch = dwr.engine._batches[batchid]; } if (ex.message == undefined) ex.message = ""; dwr.engine._handleerror(dwr.engine._receivedbatch, ex); if (searchbatch) { dwr.engine._receivedbatch = null; dwr.engine.batch.remove(dwr.engine._batches[batchid]); } }, /** * called by the server when we need to set a new script session id * @param {object} newsessionid the new script session id to be used from now */ handlenewscriptsession:function(newsessionid) { if (dwr.engine._scriptsessionid != null) { dwr.engine._debug("server side script session id timed out. new session automatically created"); } dwr.engine._scriptsessionid = newsessionid; }, /** * called by the server when we need to set a new script session id * @param {object} newsessionid the new script session id to be used from now */ handlenewwindowname:function(windowname) { dwr.engine._debug("setting new window name: " + windowname); if (window.name != null && window.name != "") { dwr.engine._debug("- warning: this will override existing name of: " + window.name); } window.name = windowname; }, /** * execute some script in a different window * @param {object} windowname the name of the window in which to eval the script * @param {object} script the script to eval elsewhere */ handleforeign:function(windowname, script) { var foreign = window.open(null, windowname); if (foreign != null) { if (foreign.dwr != null) { foreign.dwr.engine._eval(script); } else { dwr.engine._debug("found window, but dwr did not exist in it"); } } else { dwr.engine._debug("could not find window"); } }, /** * called by the server: reverse ajax should not be used * @private * @param {object} ex * @param {int} batchid */ pollcometdisabled:function(ex, batchid){ dwr.engine.setactivereverseajax(false); var searchbatch = (dwr.engine._receivedbatch == null && batchid != null); if (searchbatch) { dwr.engine._receivedbatch = dwr.engine._batches[batchid]; } if (ex.message == undefined) { ex.message = ""; } dwr.engine._handleerror(dwr.engine._receivedbatch, ex); if (searchbatch) { dwr.engine._receivedbatch = null; dwr.engine.batch.remove(dwr.engine._batches[batchid]); } } }; /** * functions to serialize a data set into a list of parameters */ dwr.engine.serialize = { /** * activex objects to use when we want to convert an xml string into a dom object */ domdocument:[ "msxml2.domdocument.6.0", "msxml2.domdocument.5.0", "msxml2.domdocument.4.0", "msxml2.domdocument.3.0", "msxml2.domdocument", "msxml.domdocument", "microsoft.xmldom" ], /** * convert a text representation of xml into a dom tree * @param {string} xml an xml string */ todom:function(xml) { var dom; if (window.domparser) { var parser = new domparser(); dom = parser.parsefromstring(xml, "text/xml"); if (!dom.documentelement || dom.documentelement.tagname == "parsererror") { var message = dom.documentelement.firstchild.data; message += "\n" + dom.documentelement.firstchild.nextsibling.firstchild.data; throw message; } return dom; } else if (window.activexobject) { dom = dwr.engine.util.newactivexobject(dwr.engine.serialize.domdocument); dom.loadxml(xml); // what happens on parse fail with ie? return dom; } else { var div = document.createelement("div"); div.innerhtml = xml; return div; } }, /** * marshall a data item * @private * @param batch a map of variables to how they have been marshalled * @param referto an array of already marshalled variables to prevent recurrsion * @param data the data to be marshalled * @param name the name of the data being marshalled */ convert:function(batch, referto, data, name) { if (data == null) { batch.map[name] = "null:null"; return; } switch (typeof data) { case "boolean": batch.map[name] = "boolean:" + data; break; case "number": batch.map[name] = "number:" + data; break; case "string": batch.map[name] = "string:" + encodeuricomponent(data); break; case "object": var ref = dwr.engine.serialize.lookup(referto, data, name); if (ref) { batch.map[name] = ref; } else if (data instanceof string) batch.map[name] = "string:" + encodeuricomponent(data); else if (data instanceof boolean) batch.map[name] = "boolean:" + data; else if (data instanceof number) batch.map[name] = "number:" + data; else if (data instanceof date) batch.map[name] = "date:" + data.gettime(); else if (data && data.join) batch.map[name] = dwr.engine.serialize.convertarray(batch, referto, data, name); else if (data && data.tagname && data.tagname.tolowercase() == "input" && data.type && data.type.tolowercase() == "file") { batch.fileupload = true; batch.map[name] = data; } else { // this check for an html is not complete, but is there a better way? // maybe we should add: data.haschildnodes typeof "function" == true if (data.nodename && data.nodetype) { batch.map[name] = dwr.engine.serialize.convertxml(batch, referto, data, name); } else { batch.map[name] = dwr.engine.serialize.convertobject(batch, referto, data, name); } } break; case "function": // we just ignore functions. break; default: dwr.engine._handlewarning(null, { name:"dwr.engine.unexpectedtype", message:"unexpected type: " + typeof data + ", attempting default converter." }); batch.map[name] = "default:" + data; break; } }, /** * marshall an array * @private * @see dwr.engine.serialize.convert() for parameter details */ convertarray:function(batch, referto, data, name) { var reply = "array:["; for (var i = 0; i < data.length; i++) { if (i != 0) reply += ","; batch.paramcount++; var childname = "c" + dwr.engine._batch.map.callcount + "-e" + batch.paramcount; dwr.engine.serialize.convert(batch, referto, data[i], childname); reply += "reference:"; reply += childname; } reply += "]"; return reply; }, /** * marshall an object * @private * @see dwr.engine.serialize.convert() for parameter details */ convertobject:function(batch, referto, data, name) { // treat objects as an associative arrays var reply = "object_" + dwr.engine.serialize.getobjectclassname(data) + ":{"; var element; for (element in data) { if (typeof data[element] != "function") { batch.paramcount++; var childname = "c" + dwr.engine._batch.map.callcount + "-e" + batch.paramcount; dwr.engine.serialize.convert(batch, referto, data[element], childname); reply += encodeuricomponent(element) + ":reference:" + childname + ", "; } } if (reply.substring(reply.length - 2) == ", ") { reply = reply.substring(0, reply.length - 2); } reply += "}"; return reply; }, /** * marshall an object * @private * @see dwr.engine.serialize.convert() for parameter details */ convertxml:function(batch, referto, data, name) { var output; if (window.xmlserializer) output = new xmlserializer().serializetostring(data); else if (data.toxml) output = data.toxml; else output = data.innerhtml; return "xml:" + encodeuricomponent(output); }, /** * have we already converted this object? * @private * @see dwr.engine.serialize.convert() for parameter details */ lookup:function(referto, data, name) { var lookup; // can't use a map: getahead.org/ajax/javascript-gotchas for (var i = 0; i < referto.length; i++) { if (referto[i].data == data) { lookup = referto[i]; break; } } if (lookup) { return "reference:" + lookup.name; } referto.push({ data:data, name:name }); return null; }, /** * the names of classes that need special treatment */ errorclasses:{ "error":error, "evalerror":evalerror, "rangeerror":rangeerror, "referenceerror":referenceerror, "syntaxerror":syntaxerror, "typeerror":typeerror, "urierror":urierror }, /** * returns the classname of supplied argument obj. similar to typeof, but * which returns the name of the constructor that created the object rather * than 'object' * @private * @param {object} obj the object to detect the type of * @return the name of the object */ getobjectclassname:function(obj) { // try to find the classname by stringifying the object's constructor // and extract from "function ". if (obj && obj.constructor && obj.constructor.tostring) { var str = obj.constructor.tostring(); var regexpmatch = str.match(/function\s+(\w+)/); if (regexpmatch && regexpmatch.length == 2) { return regexpmatch[1]; } } // now manually test against the core error classes, as these in some // browsers successfully match to the wrong class in the // object.tostring() test we will do later if (obj && obj.constructor) { for (var errorname in dwr.engine.serialize.errorclasses) { if (obj.constructor == dwr.engine.serialize.errorclasses[errorname]) return errorname; } } // try to find the classname by calling object.tostring() on the object // and extracting from "[object ]" if (obj) { var str = object.prototype.tostring.call(obj); var regexpmatch = str.match(/\[object\s+(\w+)/); if (regexpmatch && regexpmatch.length==2) { return regexpmatch[1]; } } // supplied argument was probably not an object, but what is better? return "object"; } }; /** * functions to handle the various remoting transport */ dwr.engine.transport = { /** * actually send the block of data in the batch object. * @private * @param {object} batch */ send:function(batch) { dwr.engine.batch.preparetosend(batch); if (batch.fileupload) { batch.transport = dwr.engine.transport.iframe; } else if (dwr.engine.iscrossdomain) { batch.transport = dwr.engine.transport.scripttag; } // else if (batch.ispoll && dwr.engine.isie) { // batch.transport = dwr.engine.transport.htmlfile; // } else { batch.transport = dwr.engine.transport.xhr; } batch.transport.send(batch); }, /** * a generic function to remove all remoting artifacts * @param {object} batch the batch that has completed */ remove:function(batch) { dwr.engine.transport.iframe.remove(batch); dwr.engine.transport.xhr.remove(batch); }, /** * called as a result of a request timeout * @private * @param {object} batch the batch that is aborting */ abort:function(batch) { if (batch && !batch.completed) { clearinterval(batch.interval); dwr.engine.batch.remove(batch); if (batch.req) { batch.req.abort(); } dwr.engine.transport.remove(batch); dwr.engine._handleerror(batch, { name:"dwr.engine.timeout", message:"timeout" }); } }, /** * remoting through xhr */ xhr:{ /** * the default http method to use */ httpmethod:"post", /** * the activex objects to use when we want to do an xmlhttprequest call. * todo: we arrived at this by trial and error. other toolkits use * different strings, maybe there is an officially correct version? */ xmlhttp:["msxml2.xmlhttp.6.0", "msxml2.xmlhttp.5.0", "msxml2.xmlhttp.4.0", "msxml2.xmlhttp.3.0", "msxml2.xmlhttp", "microsoft.xmlhttp"], /** * setup a batch for transfer through xhr * @param {object} batch the batch to alter for xhr transmit */ send:function(batch) { if (batch.ispoll) { batch.map.partialresponse = dwr.engine._partialresponseyes; } // do proxies or ie force us to use early closing mode? if (batch.ispoll && dwr.engine._pollwithxhr == "true") { batch.map.partialresponse = dwr.engine._partialresponseno; } if (batch.ispoll && dwr.engine.isie) { batch.map.partialresponse = dwr.engine._partialresponseno; } if (window.xmlhttprequest) { batch.req = new xmlhttprequest(); } else if (window.activexobject) { batch.req = dwr.engine.util.newactivexobject(dwr.engine.transport.xhr.xmlhttp); } // proceed using xmlhttprequest if (batch.async) { batch.req.onreadystatechange = function() { if (typeof dwr != 'undefined') dwr.engine.transport.xhr.statechange(batch); }; } // if we're polling, record this for monitoring if (batch.ispoll) { dwr.engine._pollreq = batch.req; // in ie xhr is an activex control so you can't augment it like this if (!document.all) batch.req.batch = batch; } httpmethod = dwr.engine.transport.xhr.httpmethod; // workaround for safari 1.x post bug var indexsafari = navigator.useragent.indexof("safari/"); if (indexsafari >= 0) { var version = navigator.useragent.substring(indexsafari + 7); if (parseint(version, 10) < 400) { if (dwr.engine._allowgetforsafaributmakeforgeryeasier == "true") { httpmethod = "get"; } else { dwr.engine._handlewarning(batch, { name: "dwr.engine.oldsafari", message: "safari get support disabled. see getahead.org/dwr/server/servlet and allowgetforsafaributmakeforgeryeasier." }); } } } batch.mode = batch.ispoll ? dwr.engine._modeplainpoll : dwr.engine._modeplaincall; var request = dwr.engine.batch.constructrequest(batch, httpmethod); try { batch.req.open(httpmethod, request.url, batch.async); try { for (var prop in batch.headers) { var value = batch.headers[prop]; if (typeof value == "string") batch.req.setrequestheader(prop, value); } if (!batch.headers["content-type"]) batch.req.setrequestheader("content-type", "text/plain"); } catch (ex) { dwr.engine._handlewarning(batch, ex); } batch.req.send(request.body); if (!batch.async) dwr.engine.transport.xhr.statechange(batch); } catch (ex) { dwr.engine._handleerror(batch, ex); } if (batch.ispoll && batch.map.partialresponse == dwr.engine._partialresponseyes) { dwr.engine.transport.xhr.checkcometpoll(); } }, /** * called by xmlhttprequest to indicate that something has happened * @private * @param {object} batch the current remote operation */ statechange:function(batch) { var toeval; if (batch.completed) { dwr.engine._debug("error: _statechange() with batch.completed"); return; } var req = batch.req; try { if (req.readystate != 4) return; } catch (ex) { dwr.engine._handlewarning(batch, ex); // it's broken - clear up and forget this call dwr.engine.batch.remove(batch); return; } try { var reply = req.responsetext; reply = dwr.engine._replyrewritehandler(reply); var status = req.status; // causes mozilla to except on page moves if (reply == null || reply == "") { dwr.engine._handlewarning(batch, { name:"dwr.engine.missingdata", message:"no data received from server" }); } else if (status != 200) { dwr.engine._handleerror(batch, { name:"dwr.engine.http." + status, message:req.statustext }); } else { var contenttype = req.getresponseheader("content-type"); if (!contenttype.match(/^text\/plain/) && !contenttype.match(/^text\/javascript/)) { if (contenttype.match(/^text\/html/) && typeof batch.texthtmlhandler == "function") { batch.texthtmlhandler({ status:status, responsetext:reply, contenttype:contenttype }); } else { dwr.engine._handlewarning(batch, { name:"dwr.engine.invalidmimetype", message:"invalid content type: '" + contenttype + "'" }); } } else { // comet replies might have already partially executed if (batch.ispoll && batch.map.partialresponse == dwr.engine._partialresponseyes) { dwr.engine.transport.xhr.processcometresponse(reply, batch); } else { if (reply.search("//#dwr") == -1) { dwr.engine._handlewarning(batch, { name:"dwr.engine.invalidreply", message:"invalid reply from server" }); } else { toeval = reply; } } } } } catch (ex) { dwr.engine._handlewarning(batch, ex); } dwr.engine._callposthooks(batch); // outside of the try/catch so errors propagate normally: dwr.engine._receivedbatch = batch; if (toeval != null) toeval = toeval.replace(dwr.engine._scripttagprotection, ""); dwr.engine._eval(toeval); dwr.engine._receivedbatch = null; dwr.engine.batch.validate(batch); dwr.engine.batch.remove(batch); }, /** * check for reverse ajax activity * @private */ checkcometpoll:function() { if (dwr.engine._pollreq) { var req = dwr.engine._pollreq; var text = req.responsetext; if (text != null) { dwr.engine.transport.xhr.processcometresponse(text, req.batch); } } // if the poll resources are still there, come back again if (dwr.engine._pollreq) { settimeout("dwr.engine.transport.xhr.checkcometpoll()", dwr.engine._pollcometinterval); } }, /** * some more text might have come in, test and execute the new stuff. * this method could also be called by the iframe transport * @private * @param {object} response from xhr.responsetext * @param {object} batch the batch that the xhr object pertains to */ processcometresponse:function(response, batch) { if (batch.charsprocessed == response.length) return; if (response.length == 0) { batch.charsprocessed = 0; return; } var firststarttag = response.indexof("//#dwr-start#", batch.charsprocessed); if (firststarttag == -1) { // dwr.engine._debug("no start tag (search from " + batch.charsprocessed + "). skipping '" + response.substring(batch.charsprocessed) + "'"); batch.charsprocessed = response.length; return; } // if (firststarttag > 0) { // dwr.engine._debug("start tag not at start (search from " + batch.charsprocessed + "). skipping '" + response.substring(batch.charsprocessed, firststarttag) + "'"); // } var lastendtag = response.lastindexof("//#dwr-end#"); if (lastendtag == -1) { // dwr.engine._debug("no end tag. unchanged charsprocessed=" + batch.charsprocessed); return; } // skip the end tag too for next time, remembering cr and lf if (response.charcodeat(lastendtag + 11) == 13 && response.charcodeat(lastendtag + 12) == 10) { batch.charsprocessed = lastendtag + 13; } else { batch.charsprocessed = lastendtag + 11; } var exec = response.substring(firststarttag + 13, lastendtag); try { dwr.engine._receivedbatch = batch; dwr.engine._eval(exec); dwr.engine._receivedbatch = null; } catch (ex) { dwr.engine._handleerror(batch, ex); } }, /** * tidy-up when an xhr call is done * @param {object} batch */ remove:function(batch) { // xhr tidyup: avoid ie handles increase if (batch.req) { // if this is a poll frame then stop comet polling if (batch.req == dwr.engine._pollreq) dwr.engine._pollreq = null; delete batch.req; } } }, /** * functions for remoting through iframe */ iframe:{ /** * setup a batch for transfer through iframe * @param {object} batch the batch to alter for iframe transmit */ send:function(batch) { if (batch.fileupload) { batch.httpmethod = "post"; batch.enctype = "multipart/form-data"; } var idname = dwr.engine.transport.iframe.getid(batch); batch.div = document.createelement("div"); // add the div to the document first, otherwise ie 6 will ignore onload handler. document.body.appendchild(batch.div); batch.div.innerhtml = ""; batch.document = document; dwr.engine.transport.iframe.beginloader(batch, idname); }, /** * create a unique id so multiple iframes can fire at the same time * @param {object} batch a source of a unique number for the batch * @return {string} a name prefix for created elements */ getid:function(batch) { return batch.ispoll ? "dwr-if-poll-" + batch.map.batchid : "dwr-if-" + batch.map["c0-id"]; }, /** * setup a form or construct a src attribute to use the iframe. * this is abstracted from send() because the same logic will do for htmlfile * @param {object} batch */ beginloader:function(batch, idname) { batch.iframe = batch.document.getelementbyid(idname); batch.iframe.batch = batch; batch.mode = batch.ispoll ? dwr.engine._modehtmlpoll : dwr.engine._modehtmlcall; if (batch.ispoll) dwr.engine._outstandingiframes.push(batch.iframe); var request = dwr.engine.batch.constructrequest(batch, batch.httpmethod); if (batch.httpmethod == "get") { batch.iframe.setattribute("src", request.url); } else { // setting enctype via the dom does not work in ie, create the form using innerhtml instead var formhtml = "