Cant batch requests to multiple dwr servlets

1/* 2 * Copyright 2005 Joe Walker 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/** 18 * Declare an object to which we can add real functions. 19 */ 20if (dwr == null) var dwr = {}; 21if (dwr.engine == null) dwr.engine = {}; 22if (DWREngine == null) var DWREngine = dwr.engine; 23 24/** 25 * Set an alternative error handler from the default alert box. 26 * @see getahead.org/dwr/browser/engine/errors 27 */ 28dwr.engine.setErrorHandler = function(handler) { 29 dwr.engine._errorHandler = handler; 30}; 31 32/** 33 * Set an alternative warning handler from the default alert box. 34 * @see getahead.org/dwr/browser/engine/errors 35 */ 36dwr.engine.setWarningHandler = function(handler) { 37 dwr.engine._warningHandler = handler; 38}; 39 40/** 41 * Setter for the text/html handler - what happens if a DWR request gets an HTML 42 * reply rather than the expected Javascript. Often due to login timeout 43 */ 44dwr.engine.setTextHtmlHandler = function(handler) { 45 dwr.engine._textHtmlHandler = handler; 46}; 47 48/** 49 * Set a default timeout value for all calls. 0 (the default) turns timeouts off. 50 * @see getahead.org/dwr/browser/engine/errors 51 */ 52dwr.engine.setTimeout = function(timeout) { 53 dwr.engine._timeout = timeout; 54}; 55 56/** 57 * The Pre-Hook is called before any DWR remoting is done. 58 * @see getahead.org/dwr/browser/engine/hooks 59 */ 60dwr.engine.setPreHook = function(handler) { 61 dwr.engine._preHook = handler; 62}; 63 64/** 65 * The Post-Hook is called after any DWR remoting is done. 66 * @see getahead.org/dwr/browser/engine/hooks 67 */ 68dwr.engine.setPostHook = function(handler) { 69 dwr.engine._postHook = handler; 70}; 71 72/** 73 * Custom headers for all DWR calls 74 * @see getahead.org/dwr/???? 75 */ 76dwr.engine.setHeaders = function(headers) { 77 dwr.engine._headers = headers; 78}; 79 80/** 81 * Custom parameters for all DWR calls 82 * @see getahead.org/dwr/???? 83 */ 84dwr.engine.setParameters = function(parameters) { 85 dwr.engine._parameters = parameters; 86}; 87 88/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */ 89dwr.engine.XMLHttpRequest = 1; 90 91/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */ 92dwr.engine.IFrame = 2; 93 94/** XHR remoting type constant. See dwr.engine.setRpcType() */ 95dwr.engine.ScriptTag = 3; 96 97/** 98 * Set the preferred remoting type. 99 * @param newType One of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag 100 * @see getahead.org/dwr/browser/engine/options 101 */ 102dwr.engine.setRpcType = function(newType) { 103 if (newType != dwr.engine.XMLHttpRequest && newType != dwr.engine.IFrame && newType != dwr.engine.ScriptTag) { 104 dwr.engine._handleError(null, { name:"dwr.engine.invalidRpcType", message:"RpcType must be one of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag" }); 105 return; 106 } 107 dwr.engine._rpcType = newType; 108}; 109 110/** 111 * Which HTTP method do we use to send results? Must be one of "GET" or "POST". 112 * @see getahead.org/dwr/browser/engine/options 113 */ 114dwr.engine.setHttpMethod = function(httpMethod) { 115 if (httpMethod != "GET" && httpMethod != "POST") { 116 dwr.engine._handleError(null, { name:"dwr.engine.invalidHttpMethod", message:"Remoting method must be one of GET or POST" }); 117 return; 118 } 119 dwr.engine._httpMethod = httpMethod; 120}; 121 122/** 123 * Ensure that remote calls happen in the order in which they were sent? (Default: false) 124 * @see getahead.org/dwr/browser/engine/ordering 125 */ 126dwr.engine.setOrdered = function(ordered) { 127 dwr.engine._ordered = ordered; 128}; 129 130/** 131 * Do we ask the XHR object to be asynchronous? (Default: true) 132 * @see getahead.org/dwr/browser/engine/options 133 */ 134dwr.engine.setAsync = function(async) { 135 dwr.engine._async = async; 136}; 137 138/** 139 * Does DWR poll the server for updates? (Default: false) 140 * @see getahead.org/dwr/browser/engine/options 141 */ 142dwr.engine.setActiveReverseAjax = function(activeReverseAjax) { 143 if (activeReverseAjax) { 144 // Bail if we are already started 145 if (dwr.engine._activeReverseAjax) return; 146 dwr.engine._activeReverseAjax = true; 147 dwr.engine._poll(); 148 } 149 else { 150 // Can we cancel an existing request? 151 if (dwr.engine._activeReverseAjax && dwr.engine._pollReq) dwr.engine._pollReq.abort(); 152 dwr.engine._activeReverseAjax = false; 153 } 154 // TODO: in iframe mode, if we start, stop, start then the second start may 155 // well kick off a second iframe while the first is still about to return 156 // we should cope with this but we don't 157}; 158 159/** 160 * The default message handler. 161 * @see getahead.org/dwr/browser/engine/errors 162 */ 163dwr.engine.defaultErrorHandler = function(message, ex) { 164 dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true); 165 if (message == null || message == "") alert("A server error has occured."); 166 // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky 167 else if (message.indexOf("0x80040111") != -1) dwr.engine._debug(message); 168 else alert(message); 169}; 170 171/** 172 * The default warning handler. 173 * @see getahead.org/dwr/browser/engine/errors 174 */ 175dwr.engine.defaultWarningHandler = function(message, ex) { 176 dwr.engine._debug(message); 177}; 178 179/** 180 * For reduced latency you can group several remote calls together using a batch. 181 * @see getahead.org/dwr/browser/engine/batch 182 */ 183dwr.engine.beginBatch = function() { 184 if (dwr.engine._batch) { 185 dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" }); 186 return; 187 } 188 dwr.engine._batch = dwr.engine._createBatch(); 189}; 190 191/** 192 * Finished grouping a set of remote calls together. Go and execute them all. 193 * @see getahead.org/dwr/browser/engine/batch 194 */ 195dwr.engine.endBatch = function(options) { 196 var batch = dwr.engine._batch; 197 if (batch == null) { 198 dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" }); 199 return; 200 } 201 dwr.engine._batch = null; 202 if (batch.map.callCount == 0) return; 203 204 // The hooks need to be merged carefully to preserve ordering 205 if (options) dwr.engine._mergeBatch(batch, options); 206 207 // In ordered mode, we don't send unless the list of sent items is empty 208 if (dwr.engine._ordered && dwr.engine._batchesLength != 0) { 209 dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch; 210 } 211 else { 212 dwr.engine._sendData(batch); 213 } 214}; 215 216/** @deprecated */ 217dwr.engine.setPollMethod = function(type) { dwr.engine.setPollType(type); }; 218dwr.engine.setMethod = function(type) { dwr.engine.setRpcType(type); }; 219dwr.engine.setVerb = function(verb) { dwr.engine.setHttpMethod(verb); }; 220dwr.engine.setPollType = function() { dwr.engine._debug("Manually setting the Poll Type is not supported"); }; 221 222//============================================================================== 223// Only private stuff below here 224//============================================================================== 225 226/** The original page id sent from the server */ 227dwr.engine._origScriptSessionId = "AE5F896A3748A8F3D6ACDE6288C34069"; 228 229/** The session cookie name */ 230dwr.engine._sessionCookieName = "JSESSIONID"; // JSESSIONID 231 232/** Is GET enabled for the benefit of Safari? */ 233dwr.engine._allowGetForSafariButMakeForgeryEasier = "false"; 234 235/** The script prefix to strip in the case of scriptTagProtection. */ 236dwr.engine._scriptTagProtection = "throw 'allowScriptTagRemoting is false.';"; 237 238/** The default path to the DWR servlet */ 239dwr.engine._defaultPath = "/OpenXavaTest/dwr"; 240 241/** Do we use XHR for reverse ajax because we are not streaming? */ 242dwr.engine._pollWithXhr = "false"; 243 244/** The read page id that we calculate */ 245dwr.engine._scriptSessionId = null; 246 247/** The function that we use to fetch/calculate a session id */ 248dwr.engine._getScriptSessionId = function() { 249 if (dwr.engine._scriptSessionId == null) { 250 dwr.engine._scriptSessionId = dwr.engine._origScriptSessionId + Math.floor(Math.random() * 1000); 251 } 252 return dwr.engine._scriptSessionId; 253}; 254 255/** A function to call if something fails. */ 256dwr.engine._errorHandler = dwr.engine.defaultErrorHandler; 257 258/** For debugging when something unexplained happens. */ 259dwr.engine._warningHandler = dwr.engine.defaultWarningHandler; 260 261/** A function to be called before requests are marshalled. Can be null. */ 262dwr.engine._preHook = null; 263 264/** A function to be called after replies are received. Can be null. */ 265dwr.engine._postHook = null; 266 267/** An map of the batches that we have sent and are awaiting a reply on. */ 268dwr.engine._batches = {}; 269 270/** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */ 271dwr.engine._batchesLength = 0; 272 273/** In ordered mode, the array of batches waiting to be sent */ 274dwr.engine._batchQueue = []; 275 276/** What is the default rpc type */ 277dwr.engine._rpcType = dwr.engine.XMLHttpRequest; 278 279/** What is the default remoting method (ie GET or POST) */ 280dwr.engine._httpMethod = "POST"; 281 282/** Do we attempt to ensure that calls happen in the order in which they were sent? */ 283dwr.engine._ordered = false; 284 285/** Do we make the calls async? */ 286dwr.engine._async = true; 287 288/** The current batch (if we are in batch mode) */ 289dwr.engine._batch = null; 290 291/** The global timeout */ 292dwr.engine._timeout = 0; 293 294/** ActiveX objects to use when we want to convert an xml string into a DOM object. */ 295dwr.engine._DOMDocument = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]; 296 297/** The ActiveX objects to use when we want to do an XMLHttpRequest call. */ 298dwr.engine._XMLHTTP = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]; 299 300/** Are we doing comet or polling? */ 301dwr.engine._activeReverseAjax = false; 302 303/** The iframe that we are using to poll */ 304dwr.engine._outstandingIFrames = []; 305 306/** The xhr object that we are using to poll */ 307dwr.engine._pollReq = null; 308 309/** How many milliseconds between internal comet polls */ 310dwr.engine._pollCometInterval = 200; 311 312/** How many times have we re-tried to poll? */ 313dwr.engine._pollRetries = 0; 314dwr.engine._maxPollRetries = 0; 315 316/** Do we do a document.reload if we get a text/html reply? */ 317dwr.engine._textHtmlHandler = null; 318 319/** If you wish to send custom headers with every request */ 320dwr.engine._headers = null; 321 322/** If you wish to send extra custom request parameters with each request */ 323dwr.engine._parameters = null; 324 325/** Undocumented interceptors - do not use */ 326dwr.engine._postSeperator = "\n"; 327dwr.engine._defaultInterceptor = function(data) { return data; }; 328dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor; 329dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor; 330dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor; 331 332/** Batch ids allow us to know which batch the server is answering */ 333dwr.engine._nextBatchId = 0; 334 335/** A list of the properties that need merging from calls to a batch */ 336dwr.engine._propnames = [ "rpcType", "httpMethod", "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ]; 337 338/** Do we stream, or can be hacked to do so? */ 339dwr.engine._partialResponseNo = 0; 340dwr.engine._partialResponseYes = 1; 341dwr.engine._partialResponseFlush = 2; 342 343/** Is this page in the process of unloading? */ 344dwr.engine._unloading = false; 345 346/** 347 * @private Send a request. Called by the Javascript interface stub 348 * @param path part of URL after the host and before the exec bit without leading or trailing /s 349 * @param scriptName The class to execute 350 * @param methodName The method on said class to execute 351 * @param func The callback function to which any returned data should be passed 352 * if this is null, any returned data will be ignored 353 * @param vararg_params The parameters to pass to the above class 354 */ 355dwr.engine._execute = function(path, scriptName, methodName, vararg_params) { 356 var singleShot = false; 357 if (dwr.engine._batch == null) { 358 dwr.engine.beginBatch(); 359 singleShot = true; 360 } 361 var batch = dwr.engine._batch; 362 // To make them easy to manipulate we copy the arguments into an args array 363 var args = []; 364 for (var i = 0; i < arguments.length - 3; i++) { 365 args[i] = arguments[i + 3]; 366 } 367 // All the paths MUST be to the same servlet 368 if (batch.path == null) { 369 batch.path = path; 370 } 371 else { 372 if (batch.path != path) { 373 dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." }); 374 return; 375 } 376 } 377 // From the other params, work out which is the function (or object with 378 // call meta-data) and which is the call parameters 379 var callData; 380 var lastArg = args[args.length - 1]; 381 if (typeof lastArg == "function" || lastArg == null) callData = { callback:args.pop() }; 382 else callData = args.pop(); 383 384 // Merge from the callData into the batch 385 dwr.engine._mergeBatch(batch, callData); 386 batch.handlers[batch.map.callCount] = { 387 exceptionHandler:callData.exceptionHandler, 388 callback:callData.callback 389 }; 390 391 // Copy to the map the things that need serializing 392 var prefix = "c" + batch.map.callCount + "-"; 393 batch.map[prefix + "scriptName"] = scriptName; 394 batch.map[prefix + "methodName"] = methodName; 395 batch.map[prefix + "id"] = batch.map.callCount; 396 for (i = 0; i < args.length; i++) { 397 dwr.engine._serializeAll(batch, [], args[i], prefix + "param" + i); 398 } 399 400 // Now we have finished remembering the call, we incr the call count 401 batch.map.callCount++; 402 if (singleShot) dwr.engine.endBatch(); 403}; 404 405/** @private Poll the server to see if there is any data waiting */ 406dwr.engine._poll = function() { 407 if (!dwr.engine._activeReverseAjax) return; 408 409 var batch = dwr.engine._createBatch(); 410 batch.map.id = 0; // TODO: Do we need this?? 411 batch.map.callCount = 1; 412 batch.isPoll = true; 413 if (dwr.engine._pollWithXhr == "true") { 414 batch.rpcType = dwr.engine.XMLHttpRequest; 415 batch.map.partialResponse = dwr.engine._partialResponseNo; 416 } 417 else { 418 if (navigator.userAgent.indexOf("Gecko/") != -1) { 419 batch.rpcType = dwr.engine.XMLHttpRequest; 420 batch.map.partialResponse = dwr.engine._partialResponseYes; 421 } 422 else { 423 batch.rpcType = dwr.engine.XMLHttpRequest; 424 batch.map.partialResponse = dwr.engine._partialResponseNo; 425 } 426 } 427 batch.httpMethod = "POST"; 428 batch.async = true; 429 batch.timeout = 0; 430 batch.path = dwr.engine._defaultPath; 431 batch.preHooks = []; 432 batch.postHooks = []; 433 batch.errorHandler = dwr.engine._pollErrorHandler; 434 batch.warningHandler = dwr.engine._pollErrorHandler; 435 batch.handlers[0] = { 436 callback:function(pause) { 437 dwr.engine._pollRetries = 0; 438 setTimeout(dwr.engine._poll, pause); 439 } 440 }; 441 442 // Send the data 443 dwr.engine._sendData(batch); 444 if (batch.rpcType == dwr.engine.XMLHttpRequest && batch.map.partialResponse == dwr.engine._partialResponseYes) { 445 dwr.engine._checkCometPoll(); 446 } 447}; 448 449/** Try to recover from polling errors */ 450dwr.engine._pollErrorHandler = function(msg, ex) { 451 // if anything goes wrong then just silently try again (up to 3x) after 10s 452 dwr.engine._pollRetries++; 453 dwr.engine._debug("Reverse Ajax poll failed (pollRetries=" + dwr.engine._pollRetries + "): " + ex.name + " : " + ex.message); 454 if (dwr.engine._pollRetries < dwr.engine._maxPollRetries) { 455 setTimeout(dwr.engine._poll, 10000); 456 } 457 else { 458 dwr.engine._activeReverseAjax = false; 459 dwr.engine._debug("Giving up."); 460 } 461}; 462 463/** @private Generate a new standard batch */ 464dwr.engine._createBatch = function() { 465 var batch = { 466 map:{ 467 callCount:0, 468 page:window.location.pathname + window.location.search, 469 httpSessionId:dwr.engine._getJSessionId(), 470 scriptSessionId:dwr.engine._getScriptSessionId() 471 }, 472 charsProcessed:0, paramCount:0, 473 parameters:{}, headers:{}, 474 isPoll:false, handlers:{}, preHooks:[], postHooks:[], 475 rpcType:dwr.engine._rpcType, 476 httpMethod:dwr.engine._httpMethod, 477 async:dwr.engine._async, 478 timeout:dwr.engine._timeout, 479 errorHandler:dwr.engine._errorHandler, 480 warningHandler:dwr.engine._warningHandler, 481 textHtmlHandler:dwr.engine._textHtmlHandler 482 }; 483 if (dwr.engine._preHook) batch.preHooks.push(dwr.engine._preHook); 484 if (dwr.engine._postHook) batch.postHooks.push(dwr.engine._postHook); 485 var propname, data; 486 if (dwr.engine._headers) { 487 for (propname in dwr.engine._headers) { 488 data = dwr.engine._headers[propname]; 489 if (typeof data != "function") batch.headers[propname] = data; 490 } 491 } 492 if (dwr.engine._parameters) { 493 for (propname in dwr.engine._parameters) { 494 data = dwr.engine._parameters[propname]; 495 if (typeof data != "function") batch.parameters[propname] = data; 496 } 497 } 498 return batch; 499}; 500 501/** @private Take further options and merge them into */ 502dwr.engine._mergeBatch = function(batch, overrides) { 503 var propname, data; 504 for (var i = 0; i < dwr.engine._propnames.length; i++) { 505 propname = dwr.engine._propnames[i]; 506 if (overrides[propname] != null) batch[propname] = overrides[propname]; 507 } 508 if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook); 509 if (overrides.postHook != null) batch.postHooks.push(overrides.postHook); 510 if (overrides.headers) { 511 for (propname in overrides.headers) { 512 data = overrides.headers[propname]; 513 if (typeof data != "function") batch.headers[propname] = data; 514 } 515 } 516 if (overrides.parameters) { 517 for (propname in overrides.parameters) { 518 data = overrides.parameters[propname]; 519 if (typeof data != "function") batch.map["p-" + propname] = "" + data; 520 } 521 } 522}; 523 524/** @private What is our session id? */ 525dwr.engine._getJSessionId = function() { 526 var cookies = document.cookie.split(';'); 527 for (var i = 0; i < cookies.length; i++) { 528 var cookie = cookies[i]; 529 while (cookie.charAt(0) == ' ') cookie = cookie.substring(1, cookie.length); 530 if (cookie.indexOf(dwr.engine._sessionCookieName + "=") == 0) { 531 return cookie.substring(dwr.engine._sessionCookieName.length + 1, cookie.length); 532 } 533 } 534 return ""; 535}; 536 537/** @private Check for reverse Ajax activity */ 538dwr.engine._checkCometPoll = function() { 539 for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) { 540 var text = ""; 541 var iframe = dwr.engine._outstandingIFrames[i]; 542 try { 543 text = dwr.engine._getTextFromCometIFrame(iframe); 544 } 545 catch (ex) { 546 dwr.engine._handleWarning(iframe.batch, ex); 547 } 548 if (text != "") dwr.engine._processCometResponse(text, iframe.batch); 549 } 550 if (dwr.engine._pollReq) { 551 var req = dwr.engine._pollReq; 552 var text = req.responseText; 553 if (text != null) dwr.engine._processCometResponse(text, req.batch); 554 } 555 556 // If the poll resources are still there, come back again 557 if (dwr.engine._outstandingIFrames.length > 0 || dwr.engine._pollReq) { 558 setTimeout(dwr.engine._checkCometPoll, dwr.engine._pollCometInterval); 559 } 560}; 561 562/** @private Extract the whole (executed an all) text from the current iframe */ 563dwr.engine._getTextFromCometIFrame = function(frameEle) { 564 var body = frameEle.contentWindow.document.body; 565 if (body == null) return ""; 566 var text = body.innerHTML; 567 // We need to prevent IE from stripping line feeds 568 if (text.indexOf("<PRE>") == 0 || text.indexOf("<pre>") == 0) { 569 text = text.substring(5, text.length - 7); 570 } 571 return text; 572}; 573 574/** @private Some more text might have come in, test and execute the new stuff */ 575dwr.engine._processCometResponse = function(response, batch) { 576 if (batch.charsProcessed == response.length) return; 577 if (response.length == 0) { 578 batch.charsProcessed = 0; 579 return; 580 } 581 582 var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed); 583 if (firstStartTag == -1) { 584 // dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'"); 585 batch.charsProcessed = response.length; 586 return; 587 } 588 // if (firstStartTag > 0) { 589 // dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'"); 590 // } 591 592 var lastEndTag = response.lastIndexOf("//#DWR-END#"); 593 if (lastEndTag == -1) { 594 // dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed); 595 return; 596 } 597 598 // Skip the end tag too for next time, remembering CR and LF 599 if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) { 600 batch.charsProcessed = lastEndTag + 13; 601 } 602 else { 603 batch.charsProcessed = lastEndTag + 11; 604 } 605 606 var exec = response.substring(firstStartTag + 13, lastEndTag); 607 608 dwr.engine._receivedBatch = batch; 609 dwr.engine._eval(exec); 610 dwr.engine._receivedBatch = null; 611}; 612 613/** @private Actually send the block of data in the batch object. */ 614dwr.engine._sendData = function(batch) { 615 batch.map.batchId = dwr.engine._nextBatchId; 616 dwr.engine._nextBatchId++; 617 dwr.engine._batches[batch.map.batchId] = batch; 618 dwr.engine._batchesLength++; 619 batch.completed = false; 620 621 for (var i = 0; i < batch.preHooks.length; i++) { 622 batch.preHooks[i](); 623 } 624 batch.preHooks = null; 625 // Set a timeout 626 if (batch.timeout && batch.timeout != 0) { 627 batch.timeoutId = setTimeout(function() { dwr.engine._abortRequest(batch); }, batch.timeout); 628 } 629 // Get setup for XMLHttpRequest if possible 630 if (batch.rpcType == dwr.engine.XMLHttpRequest) { 631 if (window.XMLHttpRequest) { 632 batch.req = new XMLHttpRequest(); 633 } 634 // IE5 for the mac claims to support window.ActiveXObject, but throws an error when it's used 635 else if (window.ActiveXObject && !(navigator.userAgent.indexOf("Mac") >= 0 && navigator.userAgent.indexOf("MSIE") >= 0)) { 636 batch.req = dwr.engine._newActiveXObject(dwr.engine._XMLHTTP); 637 } 638 } 639 640 var prop, request; 641 if (batch.req) { 642 // Proceed using XMLHttpRequest 643 if (batch.async) { 644 batch.req.onreadystatechange = function() { 645 if (typeof dwr != 'undefined') dwr.engine._stateChange(batch); 646 }; 647 } 648 // If we're polling, record this for monitoring 649 if (batch.isPoll) { 650 dwr.engine._pollReq = batch.req; 651 // In IE XHR is an ActiveX control so you can't augment it like this 652 if (!(document.all && !window.opera)) batch.req.batch = batch; 653 } 654 // Workaround for Safari 1.x POST bug 655 var indexSafari = navigator.userAgent.indexOf("Safari/"); 656 if (indexSafari >= 0) { 657 var version = navigator.userAgent.substring(indexSafari + 7); 658 if (parseInt(version, 10) < 400) { 659 if (dwr.engine._allowGetForSafariButMakeForgeryEasier == "true") batch.httpMethod = "GET"; 660 else dwr.engine._handleWarning(batch, { name:"dwr.engine.oldSafari", message:"Safari GET support disabled. See getahead.org/dwr/server/servlet and allowGetForSafariButMakeForgeryEasier." }); 661 } 662 } 663 batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall; 664 request = dwr.engine._constructRequest(batch); 665 try { 666 batch.req.open(batch.httpMethod, request.url, batch.async); 667 try { 668 for (prop in batch.headers) { 669 var value = batch.headers[prop]; 670 if (typeof value == "string") batch.req.setRequestHeader(prop, value); 671 } 672 if (!batch.headers["Content-Type"]) batch.req.setRequestHeader("Content-Type", "text/plain"); 673 } 674 catch (ex) { 675 dwr.engine._handleWarning(batch, ex); 676 } 677 batch.req.send(request.body); 678 if (!batch.async) dwr.engine._stateChange(batch); 679 } 680 catch (ex) { 681 dwr.engine._handleError(batch, ex); 682 } 683 } 684 else if (batch.rpcType != dwr.engine.ScriptTag) { 685 var idname = batch.isPoll ? "dwr-if-poll-" + batch.map.batchId : "dwr-if-" + batch.map.batchId; 686 // Removed htmlfile implementation. Don't expect it to return before v3 687 batch.div = document.createElement("div"); 688 // Add the div to the document first, otherwise IE 6 will ignore onload handler. 689 document.body.appendChild(batch.div); 690 batch.div.innerHTML = "<iframe src='javascript:void(0)' frameborder='0' style='width:0px;height:0px;border:0;' id='" + idname + "' name='" + idname + "' onload='dwr.engine._iframeLoadingComplete (" + batch.map.batchId + ");'></iframe>"; 691 batch.document = document; 692 batch.iframe = batch.document.getElementById(idname); 693 batch.iframe.batch = batch; 694 batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall; 695 if (batch.isPoll) dwr.engine._outstandingIFrames.push(batch.iframe); 696 request = dwr.engine._constructRequest(batch); 697 if (batch.httpMethod == "GET") { 698 batch.iframe.setAttribute("src", request.url); 699 } 700 else { 701 batch.form = batch.document.createElement("form"); 702 batch.form.setAttribute("id", "dwr-form"); 703 batch.form.setAttribute("action", request.url); 704 batch.form.setAttribute("style", "display:none;"); 705 batch.form.setAttribute("target", idname); 706 batch.form.target = idname; 707 batch.form.setAttribute("method", batch.httpMethod); 708 for (prop in batch.map) { 709 var value = batch.map[prop]; 710 if (typeof value != "function") { 711 var formInput = batch.document.createElement("input"); 712 formInput.setAttribute("type", "hidden"); 713 formInput.setAttribute("name", prop); 714 formInput.setAttribute("value", value); 715 batch.form.appendChild(formInput); 716 } 717 } 718 batch.document.body.appendChild(batch.form); 719 batch.form.submit(); 720 } 721 } 722 else { 723 batch.httpMethod = "GET"; // There's no such thing as ScriptTag using POST 724 batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall; 725 request = dwr.engine._constructRequest(batch); 726 batch.script = document.createElement("script"); 727 batch.script.id = "dwr-st-" + batch.map["c0-id"]; 728 batch.script.src = request.url; 729 document.body.appendChild(batch.script); 730 } 731}; 732 733dwr.engine._ModePlainCall = "/call/plaincall/"; 734dwr.engine._ModeHtmlCall = "/call/htmlcall/"; 735dwr.engine._ModePlainPoll = "/call/plainpoll/"; 736dwr.engine._ModeHtmlPoll = "/call/htmlpoll/"; 737 738/** @private Work out what the URL should look like */ 739dwr.engine._constructRequest = function(batch) { 740 // A quick string to help people that use web log analysers 741 var request = { url:batch.path + batch.mode, body:null }; 742 if (batch.isPoll == true) { 743 request.url += "ReverseAjax.dwr"; 744 } 745 else if (batch.map.callCount == 1) { 746 request.url += batch.map["c0-scriptName"] + "." + batch.map["c0-methodName"] + ".dwr"; 747 } 748 else { 749 request.url += "Multiple." + batch.map.callCount + ".dwr"; 750 } 751 // Play nice with url re-writing 752 var sessionMatch = location.href.match(/jsessionid=([^?]+)/); 753 if (sessionMatch != null) { 754 request.url += ";jsessionid=" + sessionMatch[1]; 755 } 756 757 var prop; 758 if (batch.httpMethod == "GET") { 759 // Some browsers (Opera/Safari2) seem to fail to convert the callCount value 760 // to a string in the loop below so we do it manually here. 761 batch.map.callCount = "" + batch.map.callCount; 762 request.url += "?"; 763 for (prop in batch.map) { 764 if (typeof batch.map[prop] != "function") { 765 request.url += encodeURIComponent(prop) + "=" + encodeURIComponent(batch.map[prop]) + "&"; 766 } 767 } 768 request.url = request.url.substring(0, request.url.length - 1); 769 } 770 else { 771 // PERFORMANCE: for iframe mode this is thrown away. 772 request.body = ""; 773 if (document.all && !window.opera) { 774 // Use array joining on IE (fastest) 775 var buf = []; 776 for (prop in batch.map) { 777 if (typeof batch.map[prop] != "function") { 778 buf.push(prop + "=" + batch.map[prop] + dwr.engine._postSeperator); 779 } 780 } 781 request.body = buf.join(""); 782 } 783 else { 784 // Use string concat on other browsers (fastest) 785 for (prop in batch.map) { 786 if (typeof batch.map[prop] != "function") { 787 request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator; 788 } 789 } 790 } 791 request.body = dwr.engine._contentRewriteHandler(request.body); 792 } 793 request.url = dwr.engine._urlRewriteHandler(request.url); 794 return request; 795}; 796 797/** @private Called by XMLHttpRequest to indicate that something has happened */ 798dwr.engine._stateChange = function(batch) { 799 var toEval; 800 801 if (batch.completed) { 802 dwr.engine._debug("Error: _stateChange() with batch.completed"); 803 return; 804 } 805 806 var req = batch.req; 807 try { 808 if (req.readyState != 4) return; 809 } 810 catch (ex) { 811 dwr.engine._handleWarning(batch, ex); 812 // It's broken - clear up and forget this call 813 dwr.engine._clearUp(batch); 814 return; 815 } 816 817 if (dwr.engine._unloading) { 818 dwr.engine._debug("Ignoring reply from server as page is unloading."); 819 return; 820 } 821 822 try { 823 var reply = req.responseText; 824 reply = dwr.engine._replyRewriteHandler(reply); 825 var status = req.status; // causes Mozilla to except on page moves 826 827 if (reply == null || reply == "") { 828 dwr.engine._handleWarning(batch, { name:"dwr.engine.missingData", message:"No data received from server" }); 829 } 830 else if (status != 200) { 831 dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:req.statusText }); 832 } 833 else { 834 var contentType = req.getResponseHeader("Content-Type"); 835 if (!contentType.match(/^text\/plain/) && !contentType.match(/^text\/javascript/)) { 836 if (contentType.match(/^text\/html/) && typeof batch.textHtmlHandler == "function") { 837 batch.textHtmlHandler({ status:status, responseText:reply, contentType:contentType }); 838 } 839 else { 840 dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" }); 841 } 842 } 843 else { 844 // Comet replies might have already partially executed 845 if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) { 846 dwr.engine._processCometResponse(reply, batch); 847 } 848 else { 849 if (reply.search("//#DWR") == -1) { 850 dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" }); 851 } 852 else { 853 toEval = reply; 854 } 855 } 856 } 857 } 858 } 859 catch (ex) { 860 dwr.engine._handleWarning(batch, ex); 861 } 862 863 dwr.engine._callPostHooks(batch); 864 865 // Outside of the try/catch so errors propogate normally: 866 dwr.engine._receivedBatch = batch; 867 if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, ""); 868 dwr.engine._eval(toEval); 869 dwr.engine._receivedBatch = null; 870 dwr.engine._validateBatch(batch); 871 if (!batch.completed) dwr.engine._clearUp(batch); 872}; 873 874/** 875 * @private This function is invoked when a batch reply is received. 876 * It checks that there is a response for every call in the batch. Otherwise, 877 * an error will be signaled (a call without a response indicates that the 878 * server failed to send complete batch response). 879 */ 880dwr.engine._validateBatch = function(batch) { 881 // If some call left unreplied, report an error. 882 if (!batch.completed) { 883 for (var i = 0; i < batch.map.callCount; i++) { 884 if (batch.handlers[i] != null) { 885 dwr.engine._handleWarning(batch, { name:"dwr.engine.incompleteReply", message:"Incomplete reply from server" }); 886 break; 887 } 888 } 889 } 890} 891 892/** @private Called from iframe onload, check batch using batch-id */ 893dwr.engine._iframeLoadingComplete = function(batchId) { 894 // dwr.engine._checkCometPoll(); 895 var batch = dwr.engine._batches[batchId]; 896 if (batch) dwr.engine._validateBatch(batch); 897} 898 899/** @private Called by the server: Execute a callback */ 900dwr.engine._remoteHandleCallback = function(batchId, callId, reply) { 901 var batch = dwr.engine._batches[batchId]; 902 if (batch == null) { 903 dwr.engine._debug("Warning: batch == null in remoteHandleCallback for batchId=" + batchId, true); 904 return; 905 } 906 // Error handlers inside here indicate an error that is nothing to do 907 // with DWR so we handle them differently. 908 try { 909 var handlers = batch.handlers[callId]; 910 batch.handlers[callId] = null; 911 if (!handlers) { 912 dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true); 913 } 914 else if (typeof handlers.callback == "function") handlers.callback(reply); 915 } 916 catch (ex) { 917 dwr.engine._handleError(batch, ex); 918 } 919}; 920 921/** @private Called by the server: Handle an exception for a call */ 922dwr.engine._remoteHandleException = function(batchId, callId, ex) { 923 var batch = dwr.engine._batches[batchId]; 924 if (batch == null) { dwr.engine._debug("Warning: null batch in remoteHandleException", true); return; } 925 var handlers = batch.handlers[callId]; 926 batch.handlers[callId] = null; 927 if (handlers == null) { dwr.engine._debug("Warning: null handlers in remoteHandleException", true); return; } 928 if (ex.message == undefined) ex.message = ""; 929 if (typeof handlers.exceptionHandler == "function") handlers.exceptionHandler(ex.message, ex); 930 else if (typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex); 931}; 932 933/** @private Called by the server: The whole batch is broken */ 934dwr.engine._remoteHandleBatchException = function(ex, batchId) { 935 var searchBatch = (dwr.engine._receivedBatch == null && batchId != null); 936 if (searchBatch) { 937 dwr.engine._receivedBatch = dwr.engine._batches[batchId]; 938 } 939 if (ex.message == undefined) ex.message = ""; 940 dwr.engine._handleError(dwr.engine._receivedBatch, ex); 941 if (searchBatch) { 942 dwr.engine._receivedBatch = null; 943 dwr.engine._clearUp(dwr.engine._batches[batchId]); 944 } 945}; 946 947/** @private Called by the server: Reverse ajax should not be used */ 948dwr.engine._remotePollCometDisabled = function(ex, batchId) { 949 dwr.engine.setActiveReverseAjax(false); 950 var searchBatch = (dwr.engine._receivedBatch == null && batchId != null); 951 if (searchBatch) { 952 dwr.engine._receivedBatch = dwr.engine._batches[batchId]; 953 } 954 if (ex.message == undefined) ex.message = ""; 955 dwr.engine._handleError(dwr.engine._receivedBatch, ex); 956 if (searchBatch) { 957 dwr.engine._receivedBatch = null; 958 dwr.engine._clearUp(dwr.engine._batches[batchId]); 959 } 960}; 961 962/** @private Called by the server: An IFrame reply is about to start */ 963dwr.engine._remoteBeginIFrameResponse = function(iframe, batchId) { 964 if (iframe != null) dwr.engine._receivedBatch = iframe.batch; 965 dwr.engine._callPostHooks(dwr.engine._receivedBatch); 966}; 967 968/** @private Called by the server: An IFrame reply is just completing */ 969dwr.engine._remoteEndIFrameResponse = function(batchId) { 970 dwr.engine._clearUp(dwr.engine._receivedBatch); 971 dwr.engine._receivedBatch = null; 972}; 973 974/** @private This is a hack to make the context be this window */ 975dwr.engine._eval = function(script) { 976 if (script == null) return null; 977 if (script == "") { dwr.engine._debug("Warning: blank script", true); return null; } 978 // dwr.engine._debug("Exec: [" + script + "]", true); 979 return eval(script); 980}; 981 982/** @private Called as a result of a request timeout */ 983dwr.engine._abortRequest = function(batch) { 984 if (batch && !batch.completed) { 985 dwr.engine._clearUp(batch); 986 if (batch.req) batch.req.abort(); 987 dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" }); 988 } 989}; 990 991/** @private call all the post hooks for a batch */ 992dwr.engine._callPostHooks = function(batch) { 993 if (batch.postHooks) { 994 for (var i = 0; i < batch.postHooks.length; i++) { 995 batch.postHooks[i](); 996 } 997 batch.postHooks = null; 998 } 999}; 1000 1001/** @private A call has finished by whatever means and we need to shut it all down. */ 1002dwr.engine._clearUp = function(batch) { 1003 if (!batch) { dwr.engine._debug("Warning: null batch in dwr.engine._clearUp()", true); return; } 1004 if (batch.completed) { dwr.engine._debug("Warning: Double complete", true); return; } 1005 1006 // IFrame tidyup 1007 if (batch.div) batch.div.parentNode.removeChild(batch.div); 1008 if (batch.iframe) { 1009 // If this is a poll frame then stop comet polling 1010 for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) { 1011 if (dwr.engine._outstandingIFrames[i] == batch.iframe) { 1012 dwr.engine._outstandingIFrames.splice(i, 1); 1013 } 1014 } 1015 batch.iframe.parentNode.removeChild(batch.iframe); 1016 } 1017 if (batch.form) batch.form.parentNode.removeChild(batch.form); 1018 1019 // XHR tidyup: avoid IE handles increase 1020 if (batch.req) { 1021 // If this is a poll frame then stop comet polling 1022 if (batch.req == dwr.engine._pollReq) dwr.engine._pollReq = null; 1023 delete batch.req; 1024 } 1025 1026 // Timeout tidyup 1027 if (batch.timeoutId) { 1028 clearTimeout(batch.timeoutId); 1029 delete batch.timeoutId; 1030 } 1031 1032 if (batch.map && (batch.map.batchId || batch.map.batchId == 0)) { 1033 delete dwr.engine._batches[batch.map.batchId]; 1034 dwr.engine._batchesLength--; 1035 } 1036 1037 batch.completed = true; 1038 1039 // If there is anything on the queue waiting to go out, then send it. 1040 // We don't need to check for ordered mode, here because when ordered mode 1041 // gets turned off, we still process *waiting* batches in an ordered way. 1042 if (dwr.engine._batchQueue.length != 0) { 1043 var sendbatch = dwr.engine._batchQueue.shift(); 1044 dwr.engine._sendData(sendbatch); 1045 } 1046}; 1047 1048/** @private Abort any XHRs in progress at page unload (solves zombie socket problems in IE). */ 1049dwr.engine._unloader = function() { 1050 dwr.engine._unloading = true; 1051 1052 // Empty queue of waiting ordered requests 1053 dwr.engine._batchQueue.length = 0; 1054 1055 // Abort any ongoing XHRs and clear their batches 1056 for (var batchId in dwr.engine._batches) { 1057 var batch = dwr.engine._batches[batchId]; 1058 // Only process objects that look like batches (avoid prototype additions!) 1059 if (batch && batch.map) { 1060 if (batch.req) { 1061 batch.req.abort(); 1062 } 1063 dwr.engine._clearUp(batch); 1064 } 1065 } 1066}; 1067// Now register the unload handler 1068if (window.addEventListener) window.addEventListener('unload', dwr.engine._unloader, false); 1069else if (window.attachEvent) window.attachEvent('onunload', dwr.engine._unloader); 1070 1071/** @private Generic error handling routing to save having null checks everywhere */ 1072dwr.engine._handleError = function(batch, ex) { 1073 if (typeof ex == "string") ex = { name:"unknown", message:ex }; 1074 if (ex.message == null) ex.message = ""; 1075 if (ex.name == null) ex.name = "unknown"; 1076 if (batch && typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex); 1077 else if (dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex); 1078 if (batch) dwr.engine._clearUp(batch); 1079}; 1080 1081/** @private Generic error handling routing to save having null checks everywhere */ 1082dwr.engine._handleWarning = function(batch, ex) { 1083 if (typeof ex == "string") ex = { name:"unknown", message:ex }; 1084 if (ex.message == null) ex.message = ""; 1085 if (ex.name == null) ex.name = "unknown"; 1086 if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex); 1087 else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex); 1088 if (batch) dwr.engine._clearUp(batch); 1089}; 1090 1091/** 1092 * @private Marshall a data item 1093 * @param batch A map of variables to how they have been marshalled 1094 * @param referto An array of already marshalled variables to prevent recurrsion 1095 * @param data The data to be marshalled 1096 * @param name The name of the data being marshalled 1097 */ 1098dwr.engine._serializeAll = function(batch, referto, data, name) { 1099 if (data == null) { 1100 batch.map[name] = "null:null"; 1101 return; 1102 } 1103 1104 switch (typeof data) { 1105 case "boolean": 1106 batch.map[name] = "boolean:" + data; 1107 break; 1108 case "number": 1109 batch.map[name] = "number:" + data; 1110 break; 1111 case "string": 1112 batch.map[name] = "string:" + encodeURIComponent(data); 1113 break; 1114 case "object": 1115 if (data instanceof String) batch.map[name] = "String:" + encodeURIComponent(data); 1116 else if (data instanceof Boolean) batch.map[name] = "Boolean:" + data; 1117 else if (data instanceof Number) batch.map[name] = "Number:" + data; 1118 else if (data instanceof Date) batch.map[name] = "Date:" + data.getTime(); 1119 else if (data && data.join) batch.map[name] = dwr.engine._serializeArray(batch, referto, data, name); 1120 else batch.map[name] = dwr.engine._serializeObject(batch, referto, data, name); 1121 break; 1122 case "function": 1123 // We just ignore functions. 1124 break; 1125 default: 1126 dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." }); 1127 batch.map[name] = "default:" + data; 1128 break; 1129 } 1130}; 1131 1132/** @private Have we already converted this object? */ 1133dwr.engine._lookup = function(referto, data, name) { 1134 var lookup; 1135 // Can't use a map: getahead.org/ajax/javascript-gotchas 1136 for (var i = 0; i < referto.length; i++) { 1137 if (referto[i].data == data) { 1138 lookup = referto[i]; 1139 break; 1140 } 1141 } 1142 if (lookup) return "reference:" + lookup.name; 1143 referto.push({ data:data, name:name }); 1144 return null; 1145}; 1146 1147/** @private Marshall an object */ 1148dwr.engine._serializeObject = function(batch, referto, data, name) { 1149 var ref = dwr.engine._lookup(referto, data, name); 1150 if (ref) return ref; 1151 1152 // This check for an HTML is not complete, but is there a better way? 1153 // Maybe we should add: data.hasChildNodes typeof "function" == true 1154 if (data.nodeName && data.nodeType) { 1155 return dwr.engine._serializeXml(batch, referto, data, name); 1156 } 1157 1158 // treat objects as an associative arrays 1159 var reply = "Object_" + dwr.engine._getObjectClassName(data) + ":{"; 1160 var element; 1161 for (element in data) { 1162 if (typeof data[element] != "function") { 1163 batch.paramCount++; 1164 var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount; 1165 dwr.engine._serializeAll(batch, referto, data[element], childName); 1166 1167 reply += encodeURIComponent(element) + ":reference:" + childName + ", "; 1168 } 1169 } 1170 1171 if (reply.substring(reply.length - 2) == ", ") { 1172 reply = reply.substring(0, reply.length - 2); 1173 } 1174 reply += "}"; 1175 1176 return reply; 1177}; 1178 1179/** @private Returns the classname of supplied argument obj */ 1180dwr.engine._errorClasses = { "Error":Error, "EvalError":EvalError, "RangeError":RangeError, "ReferenceError":ReferenceError, "SyntaxError":SyntaxError, "TypeError":TypeError, "URIError":URIError }; 1181dwr.engine._getObjectClassName = function(obj) { 1182 // Try to find the classname by stringifying the object's constructor 1183 // and extract <class> from "function <class>". 1184 if (obj && obj.constructor && obj.constructor.toString) 1185 { 1186 var str = obj.constructor.toString(); 1187 var regexpmatch = str.match(/function\s+(\w+)/); 1188 if (regexpmatch && regexpmatch.length == 2) { 1189 return regexpmatch[1]; 1190 } 1191 } 1192 1193 // Now manually test against the core Error classes, as these in some 1194 // browsers successfully match to the wrong class in the 1195 // Object.toString() test we will do later 1196 if (obj && obj.constructor) { 1197 for (var errorname in dwr.engine._errorClasses) { 1198 if (obj.constructor == dwr.engine._errorClasses[errorname]) return errorname; 1199 } 1200 } 1201 1202 // Try to find the classname by calling Object.toString() on the object 1203 // and extracting <class> from "[object <class>]" 1204 if (obj) { 1205 var str = Object.prototype.toString.call(obj); 1206 var regexpmatch = str.match(/\[object\s+(\w+)/); 1207 if (regexpmatch && regexpmatch.length==2) { 1208 return regexpmatch[1]; 1209 } 1210 } 1211 1212 // Supplied argument was probably not an object, but what is better? 1213 return "Object"; 1214}; 1215 1216/** @private Marshall an object */ 1217dwr.engine._serializeXml = function(batch, referto, data, name) { 1218 var ref = dwr.engine._lookup(referto, data, name); 1219 if (ref) return ref; 1220 1221 var output; 1222 if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data); 1223 else if (data.toXml) output = data.toXml; 1224 else output = data.innerHTML; 1225 1226 return "XML:" + encodeURIComponent(output); 1227}; 1228 1229/** @private Marshall an array */ 1230dwr.engine._serializeArray = function(batch, referto, data, name) { 1231 var ref = dwr.engine._lookup(referto, data, name); 1232 if (ref) return ref; 1233 1234 if (document.all && !window.opera) { 1235 // Use array joining on IE (fastest) 1236 var buf = ["Array:["]; 1237 for (var i = 0; i < data.length; i++) { 1238 if (i != 0) buf.push(","); 1239 batch.paramCount++; 1240 var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount; 1241 dwr.engine._serializeAll(batch, referto, data[i], childName); 1242 buf.push("reference:"); 1243 buf.push(childName); 1244 } 1245 buf.push("]"); 1246 reply = buf.join(""); 1247 } 1248 else { 1249 // Use string concat on other browsers (fastest) 1250 var reply = "Array:["; 1251 for (var i = 0; i < data.length; i++) { 1252 if (i != 0) reply += ","; 1253 batch.paramCount++; 1254 var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount; 1255 dwr.engine._serializeAll(batch, referto, data[i], childName); 1256 reply += "reference:"; 1257 reply += childName; 1258 } 1259 reply += "]"; 1260 } 1261 1262 return reply; 1263}; 1264 1265/** @private Convert an XML string into a DOM object. */ 1266dwr.engine._unserializeDocument = function(xml) { 1267 var dom; 1268 if (window.DOMParser) { 1269 var parser = new DOMParser(); 1270 dom = parser.parseFromString(xml, "text/xml"); 1271 if (!dom.documentElement || dom.documentElement.tagName == "parsererror") { 1272 var message = dom.documentElement.firstChild.data; 1273 message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data; 1274 throw message; 1275 } 1276 return dom; 1277 } 1278 else if (window.ActiveXObject) { 1279 dom = dwr.engine._newActiveXObject(dwr.engine._DOMDocument); 1280 dom.loadXML(xml); // What happens on parse fail with IE? 1281 return dom; 1282 } 1283 else { 1284 var div = document.createElement("div"); 1285 div.innerHTML = xml; 1286 return div; 1287 } 1288}; 1289 1290/** @param axarray An array of strings to attempt to create ActiveX objects from */ 1291dwr.engine._newActiveXObject = function(axarray) { 1292 var returnValue; 1293 for (var i = 0; i < axarray.length; i++) { 1294 try { 1295 returnValue = new ActiveXObject(axarray[i]); 1296 break; 1297 } 1298 catch (ex) { /* ignore */ } 1299 } 1300 return returnValue; 1301}; 1302 1303/** @private Used internally when some message needs to get to the programmer */ 1304dwr.engine._debug = function(message, stacktrace) { 1305 var written = false; 1306 try { 1307 if (window.console) { 1308 if (stacktrace && window.console.trace) window.console.trace(); 1309 window.console.log(message); 1310 written = true; 1311 } 1312 else if (window.opera && window.opera.postError) { 1313 window.opera.postError(message); 1314 written = true; 1315 } 1316 } 1317 catch (ex) { /* ignore */ } 1318 1319 if (!written) { 1320 var debug = document.getElementById("dwr-debug"); 1321 if (debug) { 1322 var contents = message + "<br/>" + debug.innerHTML; 1323 if (contents.length > 2048) contents = contents.substring(0, 2048); 1324 debug.innerHTML = contents; 1325 } 1326 } 1327}; 1328