PageSourceSearch

https://pncvets.com/dwr/engine.js

js pncvets.com collected 2026-09-27 05:04:12 UTC 78,467 bytes, 2,227 lines download raw bytes

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 * The DWR object is also defined by dwr.util etc.
19 */
20if (window['dojo']) dojo.provide('dwr.engine');
21if (typeof window['dwr'] == 'undefined') {
22  dwr = { };
23}
24
25(function() {
26  dwr.engine = { };
27
28  /**
29    * Set an alternative error handler from the default alert box.
30    * @param {Function} handler The function to call when an error happens
31    * @see http://getahead.org/dwr/browser/engine/errors
32    */
33  dwr.engine.setErrorHandler = function(handler) {
34    dwr.engine._errorHandler = handler;
35  };
36
37  /**
38    * Set an alternative warning handler from the default alert box.
39    * @param {Function} handler The function to call when a warning happens
40    * @see http://getahead.org/dwr/browser/engine/errors
41    */
42  dwr.engine.setWarningHandler = function(handler) {
43    dwr.engine._warningHandler = handler;
44  };
45
46  /**
47    * Setter for the text/html handler - what happens if a DWR request gets an HTML
48    * reply rather than the expected Javascript. Often due to login timeout
49    * @param {Function} handler The function to call on an unexpected text/html content type
50    */
51  dwr.engine.setTextHtmlHandler = function(handler) {
52    dwr.engine._textHtmlHandler = handler;
53  };
54
55  /**
56    * Set a default timeout value for all calls. 0 (the default) turns timeouts off.
57    * @param {Function} handler The function to call when we get bored of waiting for a call
58    * @see getahead.org/dwr/browser/engine/errors
59    */
60  dwr.engine.setTimeout = function(timeout) {
61    dwr.engine._timeout = timeout;
62  };
63
64  /**
65    * The Pre-Hook is called before any DWR remoting is done.
66    * @param {Function} handler The function to call before any remote calls
67    * @see getahead.org/dwr/browser/engine/hooks
68    */
69  dwr.engine.setPreHook = function(handler) {
70    dwr.engine._preHook = handler;
71  };
72
73  /**
74   * The Post-Hook is called after any DWR remoting is done.
75   * @param {Function} handler The function to call after any remote calls
76   * @see getahead.org/dwr/browser/engine/hooks
77   */
78  dwr.engine.setPostHook = function(handler) {
79    dwr.engine._postHook = handler;
80  };
81
82  /**
83   * Custom headers for all DWR calls
84   * @param {Object} headers Object containing name/value pairs for extra headers
85   * @see getahead.org/dwr/????
86   */
87  dwr.engine.setHeaders = function(headers) {
88    dwr.engine._headers = headers;
89  };
90
91  /**
92   * Custom parameters for all DWR calls
93   * @param {Object} parameters Object containing name/value pairs for extra request parameters
94   * @see getahead.org/dwr/????
95   */
96  dwr.engine.setParameters = function(parameters) {
97    dwr.engine._parameters = parameters;
98  };
99
100  /**
101   * Ensure that remote calls happen in the order in which they were sent? (Default: false)
102   * @param {boolean} ordered true to enable ordered processing
103   * @see getahead.org/dwr/browser/engine/ordering
104   */
105  dwr.engine.setOrdered = function(ordered) {
106    dwr.engine._ordered = ordered;
107  };
108
109  /**
110   * Do we ask the XHR object to be asynchronous? (Default: true)
111   * Warning: it is <strong>highly</strong> advised to use the default ofasync
112   * processing, especially when dealing with Internet based requests.
113   * @param {boolean} async false to enable sync processing for XHR queries
114   * @see getahead.org/dwr/browser/engine/options
115   */
116  dwr.engine.setAsync = function(async) {
117    dwr.engine._async = async;
118  };
119
120  /**
121   * Does the client actively check the server for updates? (Default: false)
122   * @param {boolean} async true to enable low latency reverse ajax
123   * @see getahead.org/dwr/browser/engine/options
124   */
125  dwr.engine.setActiveReverseAjax = function(activeReverseAjax) {
126    if (activeReverseAjax) {
127      // Bail if we are already started
128      if (dwr.engine._activeReverseAjax) return;
129      dwr.engine._activeReverseAjax = true;
130      dwr.engine._poll();
131    }
132    else {
133      // Can we cancel an existing request?
134      if (dwr.engine._activeReverseAjax && dwr.engine._pollReq) {
135        dwr.engine._pollReq.abort();
136      }
137      dwr.engine._activeReverseAjax = false;
138    }
139    // TODO: in iframe mode, if we start, stop, start then the second start may
140    // well kick off a second iframe while the first is still about to return
141    // we should cope with this but we don't
142  };
143
144  /**
145   * Turn server notification of page unload on and off
146   * @param {boolean} notify true or false depending on if we want to turn unload on or off
147   * @see getahead.org/dwr/browser/engine/options
148   */
149  dwr.engine.setNotifyServerOnPageUnload = function(notify) {
150    dwr.engine._isNotifyServerOnPageUnload = notify;
151  };
152
153  /**
154   * The default message handler.
155   * @param {String} message The text of the error message
156   * @param {Object} ex An error object containing at least a name and message
157   * @see getahead.org/dwr/browser/engine/errors
158   */
159  dwr.engine.defaultErrorHandler = function(message, ex) {
160    dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true);
161    if (message == null || message == "") alert("A server error has occurred.");
162    // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
163    else if (message.indexOf("0x80040111") != -1) dwr.engine._debug(message);
164    else alert(message);
165  };
166
167  /**
168   * The default warning handler.
169   * @param {String} message The text of the error message
170   * @param {Object} ex An error object containing at least a name and message
171   * @see getahead.org/dwr/browser/engine/errors
172   */
173  dwr.engine.defaultWarningHandler = function(message, ex) {
174    dwr.engine._debug(message);
175  };
176
177  /**
178   * For reduced latency you can group several remote calls together using a batch.
179   * @see getahead.org/dwr/browser/engine/batch
180   */
181  dwr.engine.beginBatch = function() {
182    if (dwr.engine._batch) {
183      dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" });
184      return;
185    }
186    dwr.engine._batch = dwr.engine.batch.create();
187  };
188
189  /**
190   * Finished grouping a set of remote calls together. Go and execute them all.
191   * @param {Object} options A options object to customize processing
192   * @see getahead.org/dwr/browser/engine/batch
193   */
194  dwr.engine.endBatch = function(options) {
195    var batch = dwr.engine._batch;
196    if (batch == null) {
197      dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" });
198      return;
199    }
200    dwr.engine._batch = null;
201    if (batch.map.callCount == 0) {
202      return;
203    }
204
205    // The hooks need to be merged carefully to preserve ordering
206    if (options) {
207      dwr.engine.batch.merge(batch, options);
208    }
209
210    // In ordered mode, we don't send unless the list of sent items is empty
211    if (dwr.engine._ordered && dwr.engine._batchesLength != 0) {
212      dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch;
213    }
214    else {
215      return dwr.engine.transport.send(batch);
216    }
217  };
218
219  /**
220   * For use with file downloads. When a DWR function returns a binary download
221   * you can prompt the user to save it using this function
222   * @param {Object} data The binary data passed from DWR
223   */
224  dwr.engine.openInDownload = function(data) {
225    var div = document.createElement("div");
226    document.body.appendChild(div);
227    div.innerHTML = "<iframe width='0' height='0' scrolling='no' frameborder='0' src='" + data + "'></iframe>";
228  };
229
230  /**
231   * What is the current version DWR number
232   * DWR version numbers are of the form "Version 1.2.3.3128[.beta]", where:
233   * 1 is the major release number. Changes in major version number indicate
234   * significant enhancements in functionality
235   * 2 is the minor release number. Changes in minor version number indicate
236   * less significant changes in functionality
237   * 3 is the revision release number. Changes here typically indicate bug
238   * fixes only
239   * 3128 is the build number. This number increments for each build
240   * .beta is a release title that is generally only used for non production
241   * releases to indicate the purpose/quality of the release
242   * The label is these strings concatenated
243   */
244  dwr.version = {
245    /**
246     * Changes in major version number indicate significant enhancements
247     */
248    major:parseInt("3"),
249
250    /**
251     * Changes in minor version number indicate smaller enhancements
252     */
253    minor:parseInt("0"),
254
255    /**
256     * Changes with the revision number typically indicate bug-fixes only
257     */
258    revision:parseInt("0"),
259
260    /**
261     * The build number increments for each build
262     */
263    build:parseInt("116"),
264
265    /**
266     * Only used for non production releases to indicate the purpose/quality of
267     * the release. Example titles include 'milestone1' or 'beta3'.
268     */
269    title:"rc1",
270
271    /**
272     * The strings above concatenated
273     */
274    label:"3.0.0.116.rc1"
275  };
276
277  //==============================================================================
278  // Only private stuff below here
279  //==============================================================================
280
281  /** The session cookie name */
282  dwr.engine._sessionCookieName = "JSESSIONID"; // JSESSIONID
283
284  /** Is GET enabled for the benefit of Safari? */
285  dwr.engine._allowGetForSafariButMakeForgeryEasier = "false";
286
287  /** The script prefix to strip in the case of scriptTagProtection. */
288  dwr.engine._scriptTagProtection = "throw 'allowScriptTagRemoting is false.';";
289
290  /** The default path to the DWR servlet */
291  dwr.engine._pathToDwrServlet = "/dwr";
292
293  /** Do we use XHR for reverse ajax because we are not streaming? */
294  dwr.engine._pollWithXhr = "false";
295
296  /** These URLs can be configured from the server */
297  dwr.engine._ModePlainCall = "/call/plaincall/";
298  dwr.engine._ModePlainPoll = "/call/plainpoll/";
299  dwr.engine._ModeHtmlCall = "/call/htmlcall/";
300  dwr.engine._ModeHtmlPoll = "/call/htmlpoll/";
301
302  /** Do we make the calls async? Default to 'true' */
303  dwr.engine._async = Boolean("true");
304
305  /** The page id */
306  dwr.engine._scriptSessionId = null;
307
308  /** A function to be called before requests are marshalled. Can be null. */
309  dwr.engine._preHook = null;
310
311  /** A function to be called after replies are received. Can be null. */
312  dwr.engine._postHook = null;
313
314  /** A map of the batches that we have sent and are awaiting a reply on. */
315  dwr.engine._batches = {};
316
317  /** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */
318  dwr.engine._batchesLength = 0;
319
320  /** In ordered mode, the array of batches waiting to be sent */
321  dwr.engine._batchQueue = [];
322
323  /** Do we attempt to ensure that calls happen in the order in which they were
324  sent? This starts true until we have fetched the ids, when it is to false */
325  dwr.engine._ordered = true;
326
327  /** The current batch (if we are in batch mode) */
328  dwr.engine._batch = null;
329
330  /** The global timeout */
331  dwr.engine._timeout = 0;
332
333  /** Are we doing comet or polling? */
334  dwr.engine._activeReverseAjax = false;
335
336  /** The xhr object that we are using to poll */
337  dwr.engine._pollReq = null;
338
339  /** How many milliseconds between internal comet polls */
340  dwr.engine._pollCometInterval = 200;
341
342  /** How many times have we re-tried to poll? */
343  dwr.engine._pollRetries = 0;
344  dwr.engine._maxPollRetries = 10;
345
346  /** The intervals between successive retries in seconds */
347  dwr.engine._retryIntervals = [ 2, 5, 10, 60, 300 ];
348
349  /** Do we do a document.reload if we get a text/html reply? */
350  dwr.engine._textHtmlHandler = null;
351
352  /** If you wish to send custom headers with every request */
353  dwr.engine._headers = null;
354
355  /** If you wish to send extra custom request parameters with each request */
356  dwr.engine._parameters = null;
357
358  /** Batch ids allow us to know which batch the server is answering */
359  dwr.engine._nextBatchId = 0;
360
361  /** A list of the properties that need merging from calls to a batch */
362  dwr.engine._propnames = [ "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ];
363
364  /** Do we stream, or can be hacked to do so? */
365  dwr.engine._partialResponseNo = 0;
366  dwr.engine._partialResponseYes = 1;
367  dwr.engine._partialResponseFlush = 2;
368
369  /** Are we doing page unloading? */
370  dwr.engine._isNotifyServerOnPageUnload = false;
371
372  /**
373   * A map of all mapped classes whose class declarations have been loaded
374   * (dwrClassName -> constructor function)
375   * This could have been pre-created by interface scripts, so we need to check.
376   */
377  if (typeof dwr.engine['_mappedClasses'] == 'undefined') {
378    dwr.engine._mappedClasses = {};
379  }
380
381  /**
382   * Find the HTTP session id sent by the web server
383   * @private
384   */
385  dwr.engine._getHttpSessionId = function() {
386    // try to find the httpSessionId
387    var cookies = document.cookie.split(';');
388    for (var i = 0; i < cookies.length; i++) {
389      var cookie = cookies[i];
390      while (cookie.charAt(0) == ' ') cookie = cookie.substring(1, cookie.length);
391      if (cookie.indexOf(dwr.engine._sessionCookieName + "=") == 0) {
392        return cookie.substring(dwr.engine._sessionCookieName.length + 1, cookie.length);
393      }
394    }
395    return "";
396  };
397
398  /** A function to call if something fails. */
399  dwr.engine._errorHandler = dwr.engine.defaultErrorHandler;
400
401  /** For debugging when something unexplained happens. */
402  dwr.engine._warningHandler = dwr.engine.defaultWarningHandler;
403
404  /** Undocumented interceptors - do not use */
405  dwr.engine._postSeperator = "\n";
406  dwr.engine._defaultInterceptor = function(data) { return data; };
407  dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor;
408  dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor;
409  dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor;
410
411  /** Is this page in the process of unloading? */
412  dwr.engine._unloading = false;
413
414  /** @private Abort any XHRs in progress at page unload (solves zombie socket problems in IE). */
415  dwr.engine._unloader = function() {
416    dwr.engine._unloading = true;
417
418    // Empty queue of waiting ordered requests
419    dwr.engine._batchQueue.length = 0;
420
421    // Abort any ongoing XHRs and clear their batches
422    var batch;
423    for (var batchId in dwr.engine._batches) {
424      batch = dwr.engine._batches[batchId];
425      // Only process objects that look like batches (avoid prototype additions!)
426      if (batch && batch.map) {
427        if (batch.req) {
428          batch.req.abort();
429        }
430      }
431    }
432
433    // If we have used reverse ajax then we try to tell the server we are gone
434    if (dwr.engine._isNotifyServerOnPageUnload) {
435      dwr.engine._debug("calling unloader for: " + dwr.engine._scriptSessionId);
436      batch = {
437        map:{
438          callCount:1,
439          'c0-scriptName':'__System',
440          'c0-methodName':'pageUnloaded',
441          'c0-id':0
442        },
443        paramCount:0, isPoll:false, async:true,
444        headers:{}, preHooks:[], postHooks:[],
445        timeout:dwr.engine._timeout,
446        errorHandler:null, warningHandler:null, textHtmlHandler:null,
447        path:dwr.engine._pathToDwrServlet,
448        handlers:[{ exceptionHandler:null, callback:null }]
449      };
450      dwr.engine.transport.send(batch);
451      dwr.engine._isNotifyServerOnPageUnload = false;
452    }
453  };
454
455  // Now register the unload handler
456  if (!dwr.engine.isJaxerServer) {
457    if (window.addEventListener) window.addEventListener('unload', dwr.engine._unloader, false);
458    else if (window.attachEvent) window.attachEvent('onunload', dwr.engine._unloader);
459  }
460
461  /**
462   * Send a request. Called by the JavaScript interface stub
463   * @private
464   * @param path part of URL after the host and before the exec bit without leading or trailing /s
465   * @param scriptName The class to execute
466   * @param methodName The method on said class to execute
467   * @param func The callback function to which any returned data should be passed
468   *       if this is null, any returned data will be ignored
469   * @param args The parameters to passed to the above method
470   */
471  dwr.engine._execute = function(path, scriptName, methodName, args) {
472    var singleShot = false;
473    if (dwr.engine._batch == null) {
474      dwr.engine.beginBatch();
475      singleShot = true;
476    }
477
478    var batch = dwr.engine._batch;
479    // All the paths MUST be to the same servlet
480    if (batch.path == null) {
481      batch.path = path;
482    }
483    else {
484      if (batch.path != path) {
485        dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." });
486        return;
487      }
488    }
489
490    dwr.engine.batch.addCall(batch, scriptName, methodName, args);
491
492    // Now we have finished remembering the call, we increment the call count
493    batch.map.callCount++;
494    if (singleShot) {
495      return dwr.engine.endBatch();
496    }
497  };
498
499  /**
500   * Poll the server to see if there is any data waiting
501   * @private
502   */
503  dwr.engine._poll = function() {
504    if (!dwr.engine._activeReverseAjax) {
505      return;
506    }
507    var batch = dwr.engine.batch.createPoll();
508    dwr.engine.transport.send(batch);
509  };
510
511  /**
512   * Try to recover from polling errors
513   * @param {Object} msg
514   * @param {Object} ex
515   */
516  dwr.engine._pollErrorHandler = function(msg, ex) {
517    if (dwr.engine._pollRetries > dwr.engine._maxPollRetries) {
518      dwr.engine._activeReverseAjax = false;
519      dwr.engine._debug("Reverse Ajax poll failed (retries=" + dwr.engine._pollRetries + "). Giving Up: " + ex.name + " : " + ex.message);
520      dwr.engine._debug("Giving up.");
521      return;
522    }
523
524    var index = dwr.engine._pollRetries;
525    if (index >= dwr.engine._retryIntervals.length) {
526      index = dwr.engine._retryIntervals.length - 1;
527    }
528
529    dwr.engine._debug("Reverse Ajax poll failed (retries=" + dwr.engine._pollRetries + "). Trying again in " + dwr.engine._retryIntervals[index] + "s: " + ex.name + " : " + ex.message);
530    setTimeout(dwr.engine._poll, 1000 * dwr.engine._retryIntervals[index]);
531
532    dwr.engine._pollRetries++;
533  };
534
535  /** @private This is a hack to make the context be this window */
536  dwr.engine._eval = function(script) {
537    if (script == null) {
538      return null;
539    }
540    if (script == "") {
541      dwr.engine._debug("Warning: blank script", true);
542      return null;
543    }
544    // dwr.engine._debug("Exec: [" + script + "]", true);
545    return eval(script);
546  };
547
548  /** @private call all the post hooks for a batch */
549  dwr.engine._callPostHooks = function(batch) {
550    if (batch.postHooks) {
551      for (var i = 0; i < batch.postHooks.length; i++) {
552        batch.postHooks[i]();
553      }
554      batch.postHooks = null;
555    }
556  };
557
558  /**
559   * Generic error handling routing to save having null checks everywhere
560   * @private
561   * @param {Object} batch
562   * @param {Object} ex
563   */
564  dwr.engine._handleError = function(batch, ex) {
565    if (typeof ex == "string") ex = { name:"unknown", message:ex };
566    if (ex.message == null) ex.message = "";
567    if (ex.name == null) ex.name = "unknown";
568    if (batch && typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
569    else if (dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex);
570    if (batch) dwr.engine.batch.remove(batch);
571  };
572
573  /**
574   * Generic error handling routing to save having null checks everywhere
575   * @private
576   * @param {Object} batch
577   * @param {Object} ex
578   */
579  dwr.engine._handleWarning = function(batch, ex) {
580    if (typeof ex == "string") ex = { name:"unknown", message:ex };
581    if (ex.message == null) ex.message = "";
582    if (ex.name == null) ex.name = "unknown";
583    if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex);
584    else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex);
585    if (batch) dwr.engine.batch.remove(batch);
586  };
587
588  /**
589   * Used internally when some message needs to get to the programmer
590   * @private
591   * @param {String} message
592   * @param {Object} stacktrace
593   */
594  dwr.engine._debug = function(message, stacktrace) {
595    var written = false;
596    try {
597      if (window.console) {
598        if (stacktrace && window.console.trace) window.console.trace();
599        window.console.log(message);
600        written = true;
601      }
602      else if (window.opera && window.opera.postError) {
603        window.opera.postError(message);
604        written = true;
605      }
606      else if (window.Jaxer && Jaxer.isOnServer) {
607        Jaxer.Log.info(message);
608        written = true;
609      }
610    }
611    catch (ex) { /* ignore */ }
612
613    if (!written) {
614      var debug = document.getElementById("dwr-debug");
615      if (debug) {
616        var contents = message + "<br/>" + debug.innerHTML;
617        if (contents.length > 2048) contents = contents.substring(0, 2048);
618        debug.innerHTML = contents;
619      }
620    }
621  };
622
623  /**
624   * Functions called by the server
625   */
626  dwr.engine.remote = {
627    /**
628     * Execute a callback
629     * @private
630     * @param {int} batchId The ID of the batch that we are replying to
631     * @param {int} callId The call ID that the script relates to
632     * @param {String} reply The script to execute
633     */
634    handleCallback:function(batchId, callId, reply) {
635      var batch = dwr.engine._batches[batchId];
636      if (batch == null) {
637        dwr.engine._debug("Warning: batch == null in remoteHandleCallback for batchId=" + batchId, true);
638        return;
639      }
640
641      // We store the reply in the batch so that in sync mode we can return the data
642      batch.reply = reply;
643
644      // Error handlers inside here indicate an error that is nothing to do
645      // with DWR so we handle them differently.
646      try {
647        var handlers = batch.handlers[callId];
648        if (!handlers) {
649          dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true);
650        }
651        else {
652          batch.handlers[callId].completed = true;
653          if (typeof handlers.callback == "function") {
654            handlers.callback.apply(handlers.callbackScope, [ reply, handlers.callbackArg ]);
655          }
656        }
657      }
658      catch (ex) {
659        dwr.engine._handleError(batch, ex);
660      }
661    },
662
663    /**
664     * Called by the server when a JavascriptFunction is executed
665     * @param id The ID of the serialized function
666     * @param args The arguments to pass to the function
667     */
668    handleFunctionCall:function(id, args) {
669      var func = dwr.engine.serialize.remoteFunctions[id];
670      func.apply(window, args);
671    },
672
673    /**
674     * Called by the server when a JavascriptFunction is executed
675     * @param id The ID of the serialized function
676     * @param args The arguments to pass to the function
677     */
678    handleObjectCall:function(id, methodName, args) {
679      var obj = dwr.engine.serialize.remoteFunctions[id];
680      obj[methodName].apply(obj, args);
681    },
682
683    /**
684     * Called by the server when a JavascriptFunction is executed
685     * @param propertyName The ID of the serialized function
686     * @param data The arguments to pass to the function
687     */
688    handleSetCall:function(id, propertyName, data) {
689      var obj = dwr.engine.serialize.remoteFunctions[id];
690      obj[propertyName] = data;
691    },
692
693    /**
694     * Called by the server when a JavascriptFunction is closed
695     * @param id The ID of the serialized function
696     */
697    handleFunctionClose:function(id) {
698      delete dwr.engine.serialize.remoteFunctions[id];
699    },
700
701    /**
702     * Called by the server: Handle an exception for a call
703     * @private
704     * @param {int} batchId The ID of the batch that we are replying to
705     * @param {int} callId The call ID that the script relates to
706     * @param {String} reply The script to execute
707     */
708    handleException:function(batchId, callId, ex) {
709      var batch = dwr.engine._batches[batchId];
710      if (batch == null) {
711        dwr.engine._debug("Warning: null batch in remoteHandleException", true);
712        return;
713      }
714
715      var handlers = batch.handlers[callId];
716      batch.handlers[callId].completed = true;
717      if (handlers == null) {
718        dwr.engine._debug("Warning: null handlers in remoteHandleException", true);
719        return;
720      }
721
722      if (ex.message == undefined) {
723        ex.message = ""; 
724      }
725
726      if (typeof handlers.exceptionHandler == "function") {
727        handlers.exceptionHandler.call(handlers.exceptionScope, ex.message, ex, handlers.exceptionArg);
728      }
729      else if (typeof batch.errorHandler == "function") {
730        batch.errorHandler(ex.message, ex);
731      }
732    },
733
734    /**
735     * Called by the server: The whole batch is broken
736     * @private
737     * @param {Object} ex The data about what broke
738     * @param {int} batchId The ID of the batch that we are replying to
739     */
740    handleBatchException:function(ex, batchId) {
741      var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
742      if (searchBatch) {
743        dwr.engine._receivedBatch = dwr.engine._batches[batchId];
744      }
745      if (ex.message == undefined) ex.message = "";
746      dwr.engine._handleError(dwr.engine._receivedBatch, ex);
747      if (searchBatch) {
748        dwr.engine._receivedBatch = null;
749        dwr.engine.batch.remove(dwr.engine._batches[batchId]);
750      }
751    },
752
753    /**
754     * Called by the server when we need to set a new script session id
755     * @param {Object} newSessionId The new script session id to be used from now
756     */
757    handleNewScriptSession:function(newSessionId) {
758      if (dwr.engine._scriptSessionId != null) {
759        dwr.engine._debug("Server side script session id timed out. New session automatically created");
760      }
761      dwr.engine._scriptSessionId = newSessionId;
762    },
763
764    /**
765     * Called by the server when we need to set a new script session id
766     * @param {Object} newSessionId The new script session id to be used from now
767     */
768    handleNewWindowName:function(windowName) {
769      dwr.engine._debug("Setting new window name: " + windowName);
770      if (window.name != null && window.name != "") {
771        dwr.engine._debug("- Warning: This will override existing name of: " + window.name);
772      }
773      window.name = windowName;
774    },
775
776    /**
777     * Execute some script in a different window
778     * @param {Object} windowName The name of the window in which to eval the script
779     * @param {Object} script The script to eval elsewhere
780     */
781    handleForeign:function(windowName, script) {
782      var foreign = window.open(null, windowName);
783      if (foreign != null) {
784        if (foreign.dwr != null) {
785          foreign.dwr.engine._eval(script);
786        }
787        else {
788          dwr.engine._debug("Found window, but DWR did not exist in it");
789        }
790      }
791      else {
792        dwr.engine._debug("Could not find window");
793      }
794    },
795
796    /**
797     * Called by the server: Reverse ajax should not be used
798     * @private
799     * @param {Object} ex
800     * @param {int} batchId
801     */
802    pollCometDisabled:function(ex, batchId){
803      dwr.engine.setActiveReverseAjax(false);
804      var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
805      if (searchBatch) {
806        dwr.engine._receivedBatch = dwr.engine._batches[batchId];
807      }
808      if (ex.message == undefined) {
809        ex.message = "";
810      }
811      dwr.engine._handleError(dwr.engine._receivedBatch, ex);
812      if (searchBatch) {
813        dwr.engine._receivedBatch = null;
814        dwr.engine.batch.remove(dwr.engine._batches[batchId]);
815      }
816    },
817
818    /**
819     * Called by the server: Create a new object of a mapped class
820     * @private
821     * @param {string} dwrClassName the name of the mapped class
822     * @param {Object} memberMap the object's data members
823     */
824    newObject:function(dwrClassName, memberMap){
825      var classfunc = dwr.engine._mappedClasses[dwrClassName]; 
826      if (classfunc && classfunc.createFromMap) {
827        return classfunc.createFromMap(memberMap);
828      }
829      else {
830        memberMap.$dwrClassName = dwrClassName;
831        return memberMap;
832      }
833    }
834  };
835
836  /**
837   * Functions to serialize a data set into a list of parameters
838   */
839  dwr.engine.serialize = {
840    /**
841     * ActiveX objects to use when we want to convert an xml string into a DOM object
842     */
843    domDocument:[
844      "Msxml2.DOMDocument.6.0",
845      "Msxml2.DOMDocument.5.0",
846      "Msxml2.DOMDocument.4.0",
847      "Msxml2.DOMDocument.3.0",
848      "MSXML2.DOMDocument",
849      "MSXML.DOMDocument",
850      "Microsoft.XMLDOM"
851    ],
852
853    /**
854     * A holder for functions that we have serialized for remote calling.
855     */
856    remoteFunctions:{},
857
858    /**
859     * The ID of the next function that we serialize
860     */
861    funcId:0,
862
863    /**
864     * Convert a text representation of XML into a DOM element
865     * @param {String} xml An xml string
866     */
867    toDomElement:function(xml) {
868      return dwr.engine.serialize.toDomDocument(xml).documentElement;
869    },
870
871    /**
872     * Convert a text representation of XML into a DOM document
873     * @param {String} xml An xml string
874     */
875    toDomDocument:function(xml) {
876      var dom;
877      if (window.DOMParser) {
878        var parser = new DOMParser();
879        dom = parser.parseFromString(xml, "text/xml");
880        if (!dom.documentElement || dom.documentElement.tagName == "parsererror") {
881          var message = dom.documentElement.firstChild.data;
882          message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data;
883          throw message;
884        }
885        return dom;
886      }
887      else if (window.ActiveXObject) {
888        dom = dwr.engine.util.newActiveXObject(dwr.engine.serialize.domDocument);
889        dom.loadXML(xml); // What happens on parse fail with IE?
890        return dom;
891      }
892      else {
893        var div = document.createElement("div");
894        div.innerHTML = xml;
895        return div;
896      }
897    },
898
899    /**
900     * Marshall a data item
901     * @private
902     * @param batch A map of variables to how they have been marshalled
903     * @param referto An array of already marshalled variables to prevent recurrsion
904     * @param data The data to be marshalled
905     * @param name The name of the data being marshalled
906     */
907    convert:function(batch, referto, data, name, depth) {
908      if (data == null) {
909        batch.map[name] = "null:null";
910        return;
911      }
912
913      switch (typeof data) {
914      case "boolean":
915        batch.map[name] = "boolean:" + data;
916        break;
917      case "number":
918        batch.map[name] = "number:" + data;
919        break;
920      case "string":
921        batch.map[name] = "string:" + encodeURIComponent(data);
922        break;
923      case "object":
924        var ref = dwr.engine.serialize.lookup(referto, data, name);
925        var objstr = Object.prototype.toString.call(data);
926        if (data.$dwrByRef) batch.map[name] = dwr.engine.serialize.convertByReference(batch, referto, data, name, depth + 1);
927        else if (ref != null) batch.map[name] = ref;
928        else if (objstr == "[object String]") batch.map[name] = "string:" + encodeURIComponent(data);
929        else if (objstr == "[object Boolean]") batch.map[name] = "boolean:" + data;
930        else if (objstr == "[object Number]") batch.map[name] = "number:" + data;
931        else if (objstr == "[object Date]") batch.map[name] = "date:" + data.getTime();
932        else if (objstr == "[object Array]") batch.map[name] = dwr.engine.serialize.convertArray(batch, referto, data, name, depth + 1);
933        else if (data && data.tagName && data.tagName.toLowerCase() == "input" && data.type && data.type.toLowerCase() == "file") {
934          batch.fileUpload = true;
935          batch.map[name] = data;
936        }
937        else {
938          // This check for an HTML is not complete, but is there a better way?
939          // Maybe we should add: data.hasChildNodes typeof "function" == true
940          if (data.nodeName && data.nodeType) {
941            batch.map[name] = dwr.engine.serialize.convertXml(batch, referto, data, name, depth + 1);
942          }
943          else {
944            batch.map[name] = dwr.engine.serialize.convertObject(batch, referto, data, name, depth + 1);
945          }
946        }
947        break;
948      case "function":
949        // Ignore functions unless they are directly passed in
950        if (depth == 0) {
951          batch.map[name] = dwr.engine.serialize.convertByReference(batch, referto, data, name, depth + 1);
952        }
953        break;
954      default:
955        dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." });
956        batch.map[name] = "default:" + data;
957        break;
958      }
959    },
960
961    /**
962     * Marshall an object by reference
963     * @private
964     * @see dwr.engine.serialize.convert() for parameter details
965     */
966    convertByReference:function(batch, referto, data, name, depth) {
967      var funcId = "f" + dwr.engine.serialize.funcId;
968      dwr.engine.serialize.remoteFunctions[funcId] = data;
969      dwr.engine.serialize.funcId++;
970      return "byref:" + funcId;
971    },
972
973    /**
974     * Marshall an array
975     * @private
976     * @see dwr.engine.serialize.convert() for parameter details
977     */
978    convertArray:function(batch, referto, data, name, depth) {
979      var childName, i;
980      if (dwr.engine.isIE <= 7) {
981        // Use array joining on IE1-7 (fastest)
982        var buf = ["array:["];
983        for (i = 0; i < data.length; i++) {
984          if (i != 0) buf.push(",");
985          batch.paramCount++;
986          childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
987          dwr.engine.serialize.convert(batch, referto, data[i], childName, depth + 1);
988          buf.push("reference:");
989          buf.push(childName);
990        }
991        buf.push("]");
992        reply = buf.join("");
993      }
994      else {
995        // Use string concat on other browsers (fastest)
996        var reply = "array:[";
997        for (i = 0; i < data.length; i++) {
998          if (i != 0) reply += ",";
999          batch.paramCount++;
1000          childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
1001          dwr.engine.serialize.convert(batch, referto, data[i], childName, depth + 1);
1002          reply += "reference:";
1003          reply += childName;
1004        }
1005        reply += "]";
1006      }
1007
1008      return reply;
1009    },
1010
1011    /**
1012     * Marshall an object by value
1013     * @private
1014     * @see dwr.engine.serialize.convert() for parameter details
1015     */
1016    convertObject:function(batch, referto, data, name, depth) {
1017      // treat objects as an associative arrays
1018      var reply = "Object_" + dwr.engine.serialize.getObjectClassName(
1018data) + ":{";
1019      var elementset = (data.constructor && data.constructor.$dwrClassMembers ? data.constructor.$dwrClassMembers : data);
1020      var element;
1021      for (element in elementset) {
1022        if (typeof data[element] != "function") {
1023          batch.paramCount++;
1024          var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
1025          dwr.engine.serialize.convert(batch, referto, data[element], childName, depth + 1);
1026          reply += encodeURIComponent(element) + ":reference:" + childName + ", ";
1027        }
1028      }
1029
1030      if (reply.substring(reply.length - 2) == ", ") {
1031        reply = reply.substring(0, reply.length - 2);
1032      }
1033      reply += "}";
1034
1035      return reply;
1036    },
1037
1038    /**
1039     * Marshall an object
1040     * @private
1041     * @see dwr.engine.serialize.convert() for parameter details
1042     */
1043    convertXml:function(batch, referto, data, name, depth) {
1044      var output;
1045      if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data);
1046      else if (data.toXml) output = data.toXml;
1047      else output = data.innerHTML;
1048
1049      return "xml:" + encodeURIComponent(output);
1050    },
1051
1052    /**
1053     * Have we already converted this object?
1054     * @private
1055     * @see dwr.engine.serialize.convert() for parameter details
1056     */
1057    lookup:function(referto, data, name) {
1058      var lookup;
1059      // Can't use a map: getahead.org/ajax/javascript-gotchas
1060      for (var i = 0; i < referto.length; i++) {
1061        if (referto[i].data == data) {
1062          lookup = referto[i];
1063          break;
1064        }
1065      }
1066      if (lookup) {
1067        return "reference:" + lookup.name;
1068      }
1069      referto.push({ data:data, name:name });
1070      return null;
1071    },
1072
1073    /**
1074     * Returns the classname of supplied argument obj. Similar to typeof, but
1075     * which returns the name of the constructor that created the object rather
1076     * than 'object'
1077     * @private
1078     * @param {Object} obj The object to detect the type of
1079     * @return The name of the object
1080     */
1081    getObjectClassName:function(obj) {
1082      // Different handling depending on if, and what type of, class-mapping is used
1083      if (obj.$dwrClassName)
1084        return obj.$dwrClassName; // Light class-mapping uses the classname from a property on the instance
1085      else if (obj.constructor && obj.constructor.$dwrClassName)
1086        return obj.constructor.$dwrClassName; // Full class-mapping uses the classname from a property on the constructor function
1087      else
1088        return "Object";
1089    }
1090  };
1091
1092  /**
1093   * Functions to handle the various remoting transport
1094   */
1095  dwr.engine.transport = {
1096    /**
1097     * Actually send the block of data in the batch object.
1098     * @private
1099     * @param {Object} batch
1100     */
1101    send:function(batch) {
1102      dwr.engine.batch.prepareToSend(batch);
1103
1104      // Work out if we are going cross domain
1105      var isCrossDomain = false;
1106      if (batch.path == null) {
1107        batch.path = dwr.engine._pathToDwrServlet;
1108      }
1109      if (batch.path.indexOf("http://") == 0 || batch.path.indexOf("https://") == 0) {
1110        var dwrShortPath = dwr.engine._pathToDwrServlet.split("/", 3).join("/");
1111        var hrefShortPath = window.location.href.split("/", 3).join("/");
1112        isCrossDomain = (dwrShortPath != hrefShortPath);
1113      }
1114
1115      if (batch.fileUpload) {
1116        if (isCrossDomain) {
1117          throw new Error("Cross domain file uploads are not possible with this release of DWR");
1118        }
1119        batch.transport = dwr.engine.transport.iframe;
1120      }
1121      else if (isCrossDomain && !dwr.engine.isJaxerServer) {
1122        batch.transport = dwr.engine.transport.scriptTag;
1123      }
1124      // else if (batch.isPoll && dwr.engine.isIE) {
1125      //   batch.transport = dwr.engine.transport.htmlfile;
1126      // }
1127      else {
1128        batch.transport = dwr.engine.transport.xhr;
1129      }
1130
1131      return batch.transport.send(batch);
1132    },
1133
1134    /**
1135     * A generic function to remove all remoting artifacts
1136     * @param {Object} batch The batch that has completed
1137     */
1138    remove:function(batch) {
1139      dwr.engine.transport.iframe.remove(batch);
1140      dwr.engine.transport.xhr.remove(batch);
1141    },
1142
1143    /**
1144     * Called as a result of a request timeout
1145     * @private
1146     * @param {Object} batch The batch that is aborting
1147     */
1148    abort:function(batch) {
1149      if (batch && !batch.completed) {
1150        dwr.engine.batch.remove(batch);
1151
1152        if (batch.req) {
1153          batch.req.abort();
1154        }
1155
1156        dwr.engine.transport.remove(batch);
1157        dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" });
1158      }
1159    },
1160
1161    /**
1162     * Remoting through XHR
1163     */
1164    xhr:{
1165      /**
1166       * The default HTTP method to use
1167       */
1168      httpMethod:"POST",
1169
1170      /**
1171       * The ActiveX objects to use when we want to do an XMLHttpRequest call.
1172       * TODO: We arrived at this by trial and error. Other toolkits use
1173       * different strings, maybe there is an officially correct version?
1174       */
1175      XMLHTTP:["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"],
1176
1177      /**
1178       * Setup a batch for transfer through XHR
1179       * @param {Object} batch The batch to alter for XHR transmit
1180       */
1181      send:function(batch) {
1182        if (batch.isPoll) {
1183          batch.map.partialResponse = dwr.engine._partialResponseYes;
1184        }
1185
1186        // Do proxies or IE force us to use early closing mode?
1187        if (batch.isPoll && dwr.engine._pollWithXhr == "true") {
1188          batch.map.partialResponse = dwr.engine._partialResponseNo;
1189        }
1190        if (batch.isPoll && dwr.engine.isIE) {
1191          batch.map.partialResponse = dwr.engine._partialResponseNo;
1192        }
1193
1194        if (window.XMLHttpRequest) {
1195          batch.req = new XMLHttpRequest();
1196        }
1197        else if (window.ActiveXObject) {
1198          batch.req = dwr.engine.util.newActiveXObject(dwr.engine.transport.xhr.XMLHTTP);
1199        }
1200
1201        // Proceed using XMLHttpRequest
1202        if (batch.async == true) {
1203          batch.req.onreadystatechange = function() {
1204            if (typeof dwr != 'undefined') {
1205              dwr.engine.transport.xhr.stateChange(batch);
1206            }
1207          };
1208        }
1209
1210        // If we're polling, record this for monitoring
1211        if (batch.isPoll) {
1212          dwr.engine._pollReq = batch.req;
1213          // In IE XHR is an ActiveX control so you can't augment it like this
1214          if (!dwr.engine.isIE) batch.req.batch = batch;
1215        }
1216
1217        httpMethod = dwr.engine.transport.xhr.httpMethod;
1218
1219        // Workaround for Safari 1.x POST bug
1220        var indexSafari = navigator.userAgent.indexOf("Safari/");
1221        if (indexSafari >= 0) {
1222          var version = navigator.userAgent.substring(indexSafari + 7);
1223          if (parseInt(version, 10) < 400) {
1224            if (dwr.engine._allowGetForSafariButMakeForgeryEasier == "true") {
1225              httpMethod = "GET";
1226            }
1227            else {
1228              dwr.engine._handleWarning(batch, {
1229                name: "dwr.engine.oldSafari",
1230                message: "Safari GET support disabled. See getahead.org/dwr/server/servlet and allowGetForSafariButMakeForgeryEasier."
1231              });
1232            }
1233          }
1234        }
1235
1236        batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
1237        var request = dwr.engine.batch.constructRequest(batch, httpMethod);
1238
1239        try {
1240          batch.req.open(httpMethod, request.url, batch.async);
1241          try {
1242            for (var prop in batch.headers) {
1243              var value = batch.headers[prop];
1244              if (typeof value == "string") {
1245                batch.req.setRequestHeader(prop, value);
1246              }
1247            }
1248            if (!batch.headers["Content-Type"]) {
1249              batch.req.setRequestHeader("Content-Type", "text/plain");
1250            }
1251          }
1252          catch (ex) {
1253            dwr.engine._handleWarning(batch, ex);
1254          }
1255          batch.req.send(request.body);
1256          if (batch.async == false) {
1257            dwr.engine.transport.xhr.stateChange(batch);
1258          }
1259        }
1260        catch (ex) {
1261          dwr.engine._handleError(batch, ex);
1262        }
1263
1264        if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
1265          dwr.engine.transport.xhr.checkCometPoll();
1266        }
1267
1268        // This is only of any use in sync mode to return the reply data
1269        return batch.reply;
1270      },
1271
1272      /**
1273       * Called by XMLHttpRequest to indicate that something has happened
1274       * @private
1275       * @param {Object} batch The current remote operation
1276       */
1277      stateChange:function(batch) {
1278        var toEval;
1279
1280        if (batch.completed) {
1281          dwr.engine._debug("Error: _stateChange() with batch.completed");
1282          return;
1283        }
1284
1285        var req = batch.req;
1286        try {
1287          var readyState = req.readyState;
1288          var notReady = (req.readyState != 4);
1289          if (notReady) {
1290            return;
1291          }
1292        }
1293        catch (ex) {
1294          dwr.engine._handleWarning(batch, ex);
1295          // It's broken - clear up and forget this call
1296          dwr.engine.batch.remove(batch);
1297          return;
1298        }
1299
1300        if (dwr.engine._unloading && !dwr.engine.isJaxerServer) {
1301          dwr.engine._debug("Ignoring reply from server as page is unloading.");
1302          return;
1303        }
1304
1305        try {
1306          var reply = req.responseText;
1307          reply = dwr.engine._replyRewriteHandler(reply);
1308          var status = req.status; // causes Mozilla to except on page moves
1309
1310          if (reply == null || reply == "") {
1311            dwr.engine._handleWarning(batch, { name:"dwr.engine.missingData", message:"No data received from server" });
1312          }
1313          else if (status != 200) {
1314            dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:req.statusText });
1315          }
1316          else {
1317            var contentType = req.getResponseHeader("Content-Type");
1318            if (dwr.engine.isJaxerServer) {
1319              // HACK! Jaxer does something b0rken with Content-Type
1320              contentType = "text/javascript";
1321            }
1322            if (!contentType.match(/^text\/plain/) && !contentType.match(/^text\/javascript/)) {
1323              if (contentType.match(/^text\/html/) && typeof batch.textHtmlHandler == "function") {
1324                batch.textHtmlHandler({ status:status, responseText:reply, contentType:contentType });
1325              }
1326              else {
1327                dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" });
1328              }
1329            }
1330            else {
1331              // Comet replies might have already partially executed
1332             if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
1333                dwr.engine.transport.xhr.processCometResponse(reply, batch);
1334              }
1335              else {
1336                if (reply.search("//#DWR") == -1) {
1337                  dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" });
1338                }
1339                else {
1340                  toEval = reply;
1341                }
1342              }
1343            }
1344          }
1345        }
1346        catch (ex) {
1347          dwr.engine._handleWarning(batch, ex);
1348        }
1349
1350        dwr.engine._callPostHooks(batch);
1351
1352        // Outside of the try/catch so errors propagate normally:
1353        dwr.engine._receivedBatch = batch;
1354        if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, "");
1355        dwr.engine._eval(toEval);
1356        dwr.engine._receivedBatch = null;
1357        dwr.engine.batch.validate(batch);
1358        if (!batch.completed) dwr.engine.batch.remove(batch);
1359      },
1360
1361      /**
1362       * Check for reverse Ajax activity
1363       * @private
1364       */
1365      checkCometPoll:function() {
1366        if (dwr.engine._pollReq) {
1367          var req = dwr.engine._pollReq;
1368          var text = req.responseText;
1369          if (text != null) {
1370            dwr.engine.transport.xhr.processCometResponse(text, req.batch);
1371          }
1372        }
1373
1374        // If the poll resources are still there, come back again
1375        if (dwr.engine._pollReq) {
1376          setTimeout(dwr.engine.transport.xhr.checkCometPoll, dwr.engine._pollCometInterval);
1377        }
1378      },
1379
1380      /**
1381       * Some more text might have come in, test and execute the new stuff.
1382       * This method could also be called by the iframe transport
1383       * @private
1384       * @param {Object} response from xhr.responseText
1385       * @param {Object} batch The batch that the XHR object pertains to
1386       */
1387      processCometResponse:function(response, batch) {
1388        if (batch.charsProcessed == response.length) return;
1389        if (response.length == 0) {
1390          batch.charsProcessed = 0;
1391          return;
1392        }
1393
1394        var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed);
1395        if (firstStartTag == -1) {
1396          // dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'");
1397          batch.charsProcessed = response.length;
1398          return;
1399        }
1400        // if (firstStartTag > 0) {
1401        //   dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'");
1402        // }
1403
1404        var lastEndTag = response.lastIndexOf("//#DWR-END#");
1405        if (lastEndTag == -1) {
1406          // dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed);
1407          return;
1408        }
1409
1410        // Skip the end tag too for next time, remembering CR and LF
1411        if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) {
1412         batch.charsProcessed = lastEndTag + 13;
1413        }
1414        else {
1415          batch.charsProcessed = lastEndTag + 11;
1416        }
1417
1418        var exec = response.substring(firstStartTag + 13, lastEndTag);
1419
1420        try {
1421          dwr.engine._receivedBatch = batch;
1422          dwr.engine._eval(exec);
1423          dwr.engine._receivedBatch = null;
1424        }
1425        catch (ex) {
1426          // This is one of these annoying points where we might be executing
1427          // while the window is being destroyed. If dwr == null, bail out.
1428          if (dwr != null) {
1429            dwr.engine._handleError(batch, ex);
1430          }
1431        }
1432      },
1433
1434      /**
1435       * Tidy-up when an XHR call is done
1436       * @param {Object} batch
1437       */
1438      remove:function(batch) {
1439        // XHR tidyup: avoid IE handles increase
1440        if (batch.req) {
1441          // If this is a poll frame then stop comet polling
1442          if (batch.req == dwr.engine._pollReq) dwr.engine._pollReq = null;
1443          delete batch.req;
1444        }
1445      }
1446    },
1447
1448    /**
1449     * Functions for remoting through IFrame
1450     */
1451    iframe:{
1452      /**
1453       * Setup a batch for transfer through IFrame
1454       * @param {Object} batch The batch to alter for IFrame transmit
1455       */
1456      send:function(batch) {
1457        if (batch.fileUpload) {
1458          batch.httpMethod = "POST";
1459          batch.encType = "multipart/form-data";
1460        }
1461        var idname = dwr.engine.transport.iframe.getId(batch);
1462        batch.div = document.createElement("div");
1463        // Add the div to the document first, otherwise IE 6 will ignore onload handler.
1464        document.body.appendChild(batch.div);
1465        batch.div.innerHTML = "<iframe src='javascript:void(0)' frameborder='0' style='width:0px;height:0px;border:0;' id='" + idname + "' name='" + idname + "' onload='dwr.engine.transport.iframe.loadingComplete(" + batch.map.batchId + ");'></iframe>";
1466        batch.document = document;
1467        dwr.engine.transport.iframe.beginLoader(batch, idname);
1468      },
1469
1470      /**
1471       * Create a unique ID so multiple iframes can fire at the same time
1472       * @param {Object} batch A source of a unique number for the batch
1473       * @return {String} a name prefix for created elements
1474       */
1475      getId:function(batch) {
1476        return batch.isPoll ? "dwr-if-poll-" + batch.map.batchId : "dwr-if-" + batch.map.batchId;
1477      },
1478
1479      /**
1480       * Setup a form or construct a src attribute to use the iframe.
1481       * This is abstracted from send() because the same logic will do for htmlfile
1482       * @param {Object} batch
1483       */
1484      beginLoader:function(batch, idname) {
1485        batch.iframe = batch.document.getElementById(idname);
1486        batch.iframe.batch = batch;
1487        batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall;
1488        if (batch.isPoll) dwr.engine._outstandingIFrames.push(batch.iframe);
1489        var request = dwr.engine.batch.constructRequest(batch, batch.httpMethod);
1490        if (batch.httpMethod == "GET") {
1491          batch.iframe.setAttribute("src", request.url);
1492        }
1493        else {
1494          // TODO: On firefox we can now get the values of file fields, maybe we should use this
1495          // See http://soakedandsoaped.com/articles/read/firefox-3-native-ajax-file-upload
1496          // setting enctype via the DOM does not work in IE, create the form using innerHTML instead
1497          var formHtml = "<form id='dwr-form' action='" + request.url + "' target='" + idname + "' style='display:none;' method='" + batch.httpMethod + "'";
1498          if (batch.encType) formHtml += " enctype='" + batch.encType + "'";
1499          formHtml += "></form>";
1500          var div = batch.document.createElement("div");
1501          div.innerHTML = formHtml;
1502          batch.form = div.firstChild;
1503          for (var prop in batch.map) {
1504            var value = batch.map[prop];
1505            if (typeof value != "function") {
1506              if (value.tagName && value.tagName.toLowerCase() == "input" && value.type && value.type.toLowerCase() == "file") {
1507                // Since we can not set the value of a file object, we must post the actual file object
1508                // that the user clicked browse on. We will put a clone in it's place.
1509                var clone = value.cloneNode(true);
1510                value.removeAttribute("id", prop);
1511                value.setAttribute("name", prop);
1512                value.parentNode.insertBefore(clone, value);
1513                value.parentNode.removeChild(value);
1514                batch.form.appendChild(value);
1515              } else {
1516                var formInput = batch.document.createElement("input");
1517                formInput.setAttribute("type", "hidden");
1518                formInput.setAttribute("name", prop);
1519                formInput.setAttribute("value", value);
1520                batch.form.appendChild(formInput);
1521              }
1522            }
1523          }
1524          batch.document.body.appendChild(batch.form);
1525          batch.form.submit();
1526        }
1527      },
1528
1529      /**
1530       * Called from iframe onload, check batch using batch-id
1531       * @private
1532       * @param {int} batchId The id of the batch that has loaded
1533       */
1534      loadingComplete:function(batchId) {
1535        var batch = dwr.engine._batches[batchId];
1536        if (batch) dwr.engine.batch.validate(batch);
1537      },
1538
1539      /**
1540       * Functions designed to be called by the server
1541       */
1542      remote:{
1543        /**
1544         * Called by the server: An IFrame reply is about to start
1545         * @private
1546         * @param {Object} iframe
1547         * @param {int} batchId
1548         */
1549        beginIFrameResponse:function(iframe, batchId) {
1550          if (iframe != null) dwr.engine._receivedBatch = iframe.batch;
1551          dwr.engine._callPostHooks(dwr.engine._receivedBatch);
1552        },
1553
1554        /**
1555         * Called by the server: An IFrame reply is just completing
1556         * @private
1557         * @param {int} batchId
1558         */
1559        endIFrameResponse:function(batchId) {
1560          dwr.engine.batch.remove(dwr.engine._receivedBatch);
1561          dwr.engine._receivedBatch = null;
1562        }
1563      },
1564
1565      remove:function(batch) {
1566        // TODO: make it so that we don't need these if statements
1567        if (batch.div) {
1568            batch.div.parentNode.removeChild(batch.div);
1569        }
1570        if (batch.iframe) {
1571          batch.iframe.parentNode.removeChild(batch.iframe);
1572        }
1573        if (batch.form) {
1574          batch.form.parentNode.removeChild(batch.form);
1575        }
1576      }
1577
1578      /*
1579      // If we have an iframe comet solution where we need to read data streamed
1580      // into an iframe then we need code like this to slurp the data out.
1581      // Compare this with xhr.checkCometPoll()
1582      outstandingIFrames:[],
1583
1584      checkCometPoll:function() {
1585        for (var i = 0; i < dwr.engine.transport.iframe.outstandingIFrames.length; i++) {
1586          var text = "";
1587          var iframe = dwr.engine.transport.iframe.outstandingIFrames[i];
1588          try {
1589            text = dwr.engine.transport.iframe.getTextFromCometIFrame(iframe);
1590          }
1591          catch (ex) {
1592            dwr.engine._handleWarning(iframe.batch, ex);
1593          }
1594          if (text != "") dwr.engine.transport.xhr.processCometResponse(text, iframe.batch);
1595        }
1596
1597        if (dwr.engine.transport.iframe.outstandingIFrames.length > 0) {
1598          setTimeout(dwr.engine.transport.iframe.checkCometPoll, dwr.engine._pollCometInterval);
1599        }
1600      }
1601
1602      // We probably also need to update dwr.engine.remote.beginIFrameResponse()
1603      // to call checkCometPoll.
1604
1605      // Extract the whole (executed and all) text from the current iframe
1606      getTextFromCometIFrame:function(frameEle) {
1607        var body = frameEle.contentWindow.document.body;
1608        if (body == null) return "";
1609        var text = body.innerHTML;
1610        // We need to prevent IE from stripping line feeds
1611        if (text.indexOf("<PRE>") == 0 || text.indexOf("<pre>") == 0) {
1612          text = text.substring(5, text.length - 7);
1613        }
1614        return text;
1615      };
1616
1617      // And an addition to iframe.remove():
1618      {
1619        if (batch.iframe) {
1620          // If this is a poll frame then stop comet polling
1621          for (var i = 0; i < dwr.engine.transport.iframe.outstandingIFrames.length; i++) {
1622            if (dwr.engine.transport.iframe.outstandingIFrames[i] == batch.iframe) {
1623              dwr.engine.transport.iframe.outstandingIFrames.splice(i, 1);
1624            }
1625          }
1626        }
1627      }
1628      */
1629    },
1630
1631    /**
1632     * Functions for remoting through Script Tags
1633     */
1634    scriptTag:{
1635      /**
1636       * Setup a batch for transfer through a script tag
1637       * @param {Object} batch The batch to alter for script tag transmit
1638       */
1639      send:function(batch) {
1640        batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
1641        var request = dwr.engine.batch.constructRequest(batch, "GET");
1642        // The best option is DOM manipulation, but this only works after onload
1643        // has completed
1644        if (document.body) {
1645          batch.script = document.createElement("script");
1646          batch.script.id = "dwr-st-" + batch.map.
1646batchId;
1647          batch.script.src = request.url;
1648          batch.script.type = "text/javascript";
1649          document.body.appendChild(batch.script);
1650        }
1651        else {
1652          document.writeln("<scr" + "ipt type='text/javascript' id='dwr-st-" + batch.map["c0-id"] + "' src='" + request.url + "'> </scr" + "ipt>");
1653        }
1654      }
1655    },
1656
1657    /**
1658     * Remoting through IE's htmlfile ActiveX control
1659     */
1660    htmlfile:{
1661      /**
1662       * Setup a batch for transfer through htmlfile
1663       * @param {Object} batch The batch to alter for htmlfile transmit
1664       */
1665      send:function(batch) {
1666        var idname = dwr.engine.transport.iframe.getId(batch);
1667        batch.htmlfile = new window.ActiveXObject("htmlfile");
1668        batch.htmlfile.open();
1669        batch.htmlfile.write("<" + "html>");
1670        batch.htmlfile.write("<div><iframe className='wibble' src='javascript:void(0)' id='" + idname + "' name='" + idname + "' onload='dwr.engine.transport.iframe.loadingComplete(" + batch.map.batchId + ");'></iframe></div>");
1671        batch.htmlfile.write("</" + "html>");
1672        batch.htmlfile.close();
1673        batch.htmlfile.parentWindow.dwr = dwr;
1674        batch.document = batch.htmlfile;
1675
1676        dwr.engine.transport.iframe.beginLoader(batch, idname);
1677      }
1678    }
1679  };
1680
1681  /**
1682   * Functions to manipulate batches
1683   * @private
1684   */
1685  dwr.engine.batch = {
1686    /**
1687     * Generate a new standard batch
1688     * @private
1689     */
1690    create:function() {
1691      var batch = {
1692        async:dwr.engine._async,
1693        charsProcessed:0,
1694        handlers:[],
1695        isPoll:false,
1696        map:{ callCount:0, windowName:window.name },
1697        paramCount:0,
1698        preHooks:[],
1699        postHooks:[],
1700        timeout:dwr.engine._timeout,
1701        errorHandler:dwr.engine._errorHandler,
1702        warningHandler:dwr.engine._warningHandler,
1703        textHtmlHandler:dwr.engine._textHtmlHandler
1704      };
1705
1706      if (dwr.engine._preHook) {
1707        batch.preHooks.push(dwr.engine._preHook);
1708      }
1709      if (dwr.engine._postHook) {
1710        batch.postHooks.push(dwr.engine._postHook);
1711      }
1712
1713      dwr.engine.batch.populateHeadersAndParameters(batch);
1714      return batch;
1715    },
1716
1717    /**
1718     * Generate a new batch for polling
1719     * @private
1720     * @see dwr.engine.batch.create()
1721     */
1722    createPoll:function() {
1723      var batch = {
1724        async:true,
1725        charsProcessed:0,
1726        handlers:[{
1727          callback:function(pause) {
1728            dwr.engine._pollRetries = 0;
1729            setTimeout(dwr.engine._poll, pause);
1730          }
1731        }],
1732        isPoll:true,
1733        map:{ windowName:window.name },
1734        paramCount:0,
1735        path:dwr.engine._pathToDwrServlet,
1736        preHooks:[],
1737        postHooks:[],
1738        timeout:0,
1739        windowName:window.name,
1740        errorHandler:dwr.engine._pollErrorHandler,
1741        warningHandler:dwr.engine._pollErrorHandler,
1742        textHtmlHandler:dwr.engine._textHtmlHandler
1743      };
1744
1745      dwr.engine.batch.populateHeadersAndParameters(batch);
1746      return batch;
1747    },
1748
1749    /**
1750     * Copy the global headers and parameters into this batch object
1751     * @private
1752     * @param {Object} batch The destination
1753     */
1754    populateHeadersAndParameters:function(batch) {
1755      var propname, data;
1756      batch.headers = {};
1757      if (dwr.engine._headers) {
1758        for (propname in dwr.engine._headers) {
1759          data = dwr.engine._headers[propname];
1760          if (typeof data != "function") batch.headers[propname] = data;
1761        }
1762      }
1763      batch.parameters = {};
1764      if (dwr.engine._parameters) {
1765        for (propname in dwr.engine._parameters) {
1766          data = dwr.engine._parameters[propname];
1767          if (typeof data != "function") batch.parameters[propname] = data;
1768        }
1769      }
1770    },
1771
1772    /**
1773     * Augment this batch with a new call
1774     * @private
1775     */
1776    addCall:function(batch, scriptName, methodName, args) {
1777      // From the other params, work out which is the function (or object with
1778      // call meta-data) and which is the call parameters
1779      var callData, stopAt;
1780      var lastArg = args[args.length - 1];
1781      if (lastArg == null || typeof lastArg == "function") {
1782        callData = { callback:lastArg };
1783        stopAt = args.length - 1;
1784      }
1785      else if (typeof lastArg == "object" && (typeof lastArg.callback == "function"
1786        || typeof lastArg.exceptionHandler == "function" || typeof lastArg.callbackHandler == "function"
1787        || typeof lastArg.errorHandler == "function" || typeof lastArg.warningHandler == "function" )) {
1788        callData = lastArg;
1789        stopAt = args.length - 1;
1790      }
1791      else {
1792        callData = {};
1793        stopAt = args.length;
1794      }
1795
1796      // Merge from the callData into the batch
1797      dwr.engine.batch.merge(batch, callData);
1798      batch.handlers[batch.map.callCount] = {
1799        exceptionHandler:callData.exceptionHandler,
1800        exceptionArg:callData.exceptionArg || callData.arg || null,
1801        exceptionScope:callData.exceptionScope || callData.scope || window,
1802        callback:callData.callbackHandler || callData.callback,
1803        callbackArg:callData.callbackArg || callData.arg || null,      
1804        callbackScope:callData.callbackScope || callData.scope || window          
1805      };
1806
1807      // Copy to the map the things that need serializing
1808      var prefix = "c" + batch.map.callCount + "-";
1809      batch.map[prefix + "scriptName"] = scriptName;
1810      batch.map[prefix + "methodName"] = methodName;
1811      batch.map[prefix + "id"] = batch.map.callCount;
1812      var converted = [];
1813      for (var i = 0; i < stopAt; i++) {
1814        dwr.engine.serialize.convert(batch, converted, args[i], prefix + "param" + i, 0);
1815      }
1816    },
1817
1818    /**
1819     * Take further options and merge them into a batch
1820     * @private
1821     * @param {Object} batch The batch that we are altering
1822     * @param {Object}
1822 overrides The object containing properties to copy into batch
1823     */
1824    merge:function(batch, overrides) {
1825      var propname, data;
1826      for (var i = 0; i < dwr.engine._propnames.length; i++) {
1827        propname = dwr.engine._propnames[i];
1828        if (overrides[propname] != null) batch[propname] = overrides[propname];
1829      }
1830      if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook);
1831      if (overrides.postHook != null) batch.postHooks.push(overrides.postHook);
1832      if (overrides.headers) {
1833        for (propname in overrides.headers) {
1834          data = overrides.headers[propname];
1835          if (typeof data != "function") batch.headers[propname] = data;
1836        }
1837      }
1838      if (overrides.parameters) {
1839        for (propname in overrides.parameters) {
1840          data = overrides.parameters[propname];
1841          if (typeof data != "function") batch.map["p-" + propname] = "" + data;
1842        }
1843      }
1844    },
1845
1846    /**
1847     * Executed just before a transport sends the batch
1848     * @private
1849     * @param {Object} batch The batch to prepare for sending
1850     */
1851    prepareToSend:function(batch) {
1852      batch.map.batchId = dwr.engine._nextBatchId;
1853      dwr.engine._nextBatchId++;
1854      dwr.engine._batches[batch.map.batchId] = batch;
1855      dwr.engine._batchesLength++;
1856      batch.completed = false;
1857
1858      // security details are filled in late so previous batches have completed
1859      batch.map.page = encodeURIComponent(window.location.pathname + window.location.search);
1860      batch.map.httpSessionId = dwr.engine._getHttpSessionId();
1861      batch.map.scriptSessionId = dwr.engine._scriptSessionId;
1862      batch.map.windowName = window.name;
1863
1864      for (var i = 0; i < batch.preHooks.length; i++) {
1865        batch.preHooks[i]();
1866      }
1867      batch.preHooks = null;
1868      // Set a timeout
1869      if (batch.timeout && batch.timeout != 0) {
1870        batch.timeoutId = setTimeout(function() { dwr.engine.transport.abort(batch); }, batch.timeout);
1871      }
1872    },
1873
1874    /**
1875     * Work out what the URL should look like
1876     * @private
1877     * @param {Object} batch the data that we are sending
1878     * @param {Object} httpMethod Are we using GET/POST etc?
1879     */
1880    constructRequest:function(batch, httpMethod) {
1881      // A quick string to help people that use web log analysers
1882      var urlBuffer = [];
1883      urlBuffer.push(batch.path);
1884      urlBuffer.push(batch.mode);
1885      if (batch.isPoll) {
1886        urlBuffer.push("ReverseAjax.dwr");
1887      }
1888      else if (batch.map.callCount == 1) {
1889        urlBuffer.push(batch.map["c0-scriptName"]);
1890        urlBuffer.push(".");
1891        urlBuffer.push(batch.map["c0-methodName"]);
1892        urlBuffer.push(".dwr");
1893      }
1894      else {
1895        urlBuffer.push("Multiple.");
1896        urlBuffer.push(batch.map.callCount);
1897        urlBuffer.push(".dwr");
1898      }
1899      // Play nice with url re-writing
1900      var sessionMatch = location.href.match(/jsessionid=([^?]+)/);
1901      if (sessionMatch != null) {
1902        urlBuffer.push(";jsessionid=");
1903        urlBuffer.push(sessionMatch[1]);
1904      }
1905
1906      var request = {};
1907      var prop;
1908      if (httpMethod == "GET") {
1909        // Some browsers (Opera/Safari2) seem to fail to convert the callCount value
1910        // to a string in the loop below so we do it manually here.
1911        batch.map.callCount = "" + batch.map.callCount;
1912        urlBuffer.push("?");
1913        for (prop in batch.map) {
1914          if (typeof batch.map[prop] != "function") {
1915            urlBuffer.push(encodeURIComponent(prop));
1916            urlBuffer.push("=");
1917            urlBuffer.push(encodeURIComponent(batch.map[prop]));
1918            urlBuffer.push("&");
1919          }
1920        }
1921        urlBuffer.pop(); // remove the trailing &
1922        request.body = null;
1923      }
1924      else {
1925        // PERFORMANCE: for iframe mode this is thrown away.
1926        if (dwr.engine.isIE <= 7) {
1927          // Use array joining on IE1-7 (fastest)
1928          var buf = [];
1929          for (prop in batch.map) {
1930            if (typeof batch.map[prop] != "function") {
1931              buf.push(prop + "=" + batch.map[prop] + dwr.engine._postSeperator);
1932            }
1933          }
1934          request.body = buf.join("");
1935        }
1936        else {
1937          // Use string concat on other browsers (fastest)
1938          for (prop in batch.map) {
1939            if (typeof batch.map[prop] != "function") {
1940              request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator;
1941            }
1942          }
1943        }
1944        var bodyBuffer = [];
1945        for (prop in batch.map) {
1946          if (typeof batch.map[prop] != "function") {
1947            bodyBuffer.push(prop);
1948            bodyBuffer.push("=");
1949            bodyBuffer.push(batch.map[prop]);
1950            bodyBuffer.push(dwr.engine._postSeperator);
1951          }
1952        }
1953        request.body = dwr.engine._contentRewriteHandler(bodyBuffer.join(""));
1954      }
1955      request.url = dwr.engine._urlRewriteHandler(urlBuffer.join(""));
1956      return request;
1957    },
1958
1959    /**
1960     * @private This function is invoked when a batch reply is received.
1961     * It checks that there is a response for every call in the batch. Otherwise,
1962     * an error will be signaled (a call without a response indicates that the
1963     * server failed to send complete batch response).
1964     */
1965    validate:function(batch) {
1966      // If some call left unreplied, report an error.
1967      if (!batch.completed) {
1968        for (var i = 0; i < batch.map.callCount; i++) {
1969          if (batch.handlers[i].completed !== true) {
1970            dwr.engine._handleWarning(batch, { name:"dwr.engine.incompleteReply", message:"Incomplete reply from server" });
1971            break;
1972          }
1973        }
1974      }
1975    },
1976
1977    /**
1978     * A call has finished by whatever means and we need to shut it all down.
1979     * @private
1980     * @param {Object} batch The batch that we are altering
1981     */
1982    remove:function(batch) {
1983      if (!batch) {
1984        dwr.engine._debug("Warning: null batch in dwr.engine.batch.remove()", true);
1985        return;
1986      }
1987
1988      if (batch.completed == "true") {
1989        dwr.engine._debug("Warning: Double complete", true);
1990        return;
1991      }
1992      batch.completed = true;
1993
1994      // Transport tidyup
1995      dwr.engine.transport.remove(batch);
1996
1997      // Timeout tidyup
1998      if (batch.timeoutId) {
1999        clearTimeout(batch.timeoutId);
2000        delete batch.timeoutId;
2001      }
2002
2003      // TODO: co-locate all the functions that work on a set of batches
2004      if (batch.map && (batch.map.batchId || batch.map.batchId == 0)) {
2005        delete dwr.engine._batches[batch.map.batchId];
2006        dwr.engine._batchesLength--;
2007      }
2008
2009      // If there is anything on the queue waiting to go out, then send it.
2010      // We don't need to check for ordered mode, here because when ordered mode
2011      // gets turned off, we still process *waiting* batches in an ordered way.
2012      if (dwr.engine._batchQueue.length != 0) {
2013        var sendbatch = dwr.engine._batchQueue.shift();
2014        dwr.engine.transport.send(sendbatch);
2015      }
2016    }
2017  };
2018
2019  /**
2020   * Various utility functions
2021   * @private
2022   */
2023  dwr.engine.util = {
2024    /**
2025     * Create one of a number of ActiveX strings
2026     * @param {Object} axarray An array of strings to attempt to create ActiveX objects from
2027     */
2028    newActiveXObject:function(axarray) {
2029      var returnValue;
2030      for (var i = 0; i < axarray.length; i++) {
2031        try {
2032          returnValue = new ActiveXObject(axarray[i]);
2033          break;
2034        }
2035        catch (ex) { /* ignore */ }
2036      }
2037      return returnValue;
2038    }
2039  };
2040
2041  /**
2042   * Work out what type of browser we are working on
2043   */
2044  var userAgent = navigator.userAgent;
2045  var versionString = navigator.appVersion;
2046  var version = parseFloat(versionString);
2047
2048  dwr.engine.isOpera = (userAgent.indexOf("Opera") >= 0) ? version : 0;
2049  dwr.engine.isKhtml = (versionString.indexOf("Konqueror") >= 0) || (versionString.indexOf("Safari") >= 0) ? version : 0;
2050  dwr.engine.isSafari = (versionString.indexOf("Safari") >= 0) ? version : 0;
2051  dwr.engine.isJaxerServer = (window.Jaxer && Jaxer.isOnServer);
2052
2053  var geckoPos = userAgent.indexOf("Gecko");
2054  dwr.engine.isMozilla = ((geckoPos >= 0) && (!dwr.engine.isKhtml)) ? version : 0;
2055
2056  dwr.engine.isFF = 0;
2057  dwr.engine.isIE = 0;
2058  try {
2059    if (dwr.engine.isMozilla) {
2060      dwr.engine.isFF = parseFloat(userAgent.split("Firefox/")[1].split(" ")[0]);
2061    }
2062    if ((document.all) && (!dwr.engine.isOpera)) {
2063      dwr.engine.isIE = parseFloat(versionString.split("MSIE ")[1].split(";")[0]);
2064    }
2065  }
2066  catch(ex) { }
2067
2068  // Fetch the scriptSessionId from the server
2069  eval("dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'pageLoaded', [ function() { dwr.engine._ordered = false; }]);");
2070
2071  /**
2072   * Routines for the DWR pubsub hub
2073   */
2074  dwr.hub = {
2075    /**
2076     * Publish some data to a given topic
2077     * @param {Object} topicName The topic to publish to
2078     * @param {Object} data The data to publish
2079     */
2080    publish:function(topicName, data) {
2081      dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'publish', topicName, data, {});
2082    },
2083
2084    /**
2085     * Subscribe to get notifications of publish events to a given topic
2086     * @param {String} topicName The topic to subscribe to
2087     * @param {Function} callback The function to call when a publish happens
2088     * @param {Object} scope The 'this' object on which the callback executes (optional)
2089     * @param {Object} subscriberData Data that the subscriber wishes to remember (optional)
2090     * @return An opaque type for use with unsubscribe
2091     */
2092    subscribe:function(topicName, callback, scope, subscriberData) {
2093      var subscription = "" + dwr.hub._subscriptionId;
2094      dwr.hub._subscriptionId++;
2095      dwr.hub._subscriptions[subscription] = {
2096        callback:callback,
2097        scope:scope,
2098        subscriberData:subscriberData
2099      };
2100      dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'subscribe', topicName, subscription, {});
2101      return subscription;
2102    },
2103
2104    /**
2105     * Called by the server: A publish event has happened that we care about
2106     * @private
2107     * @param {Object} subscriptionId
2108     * @param {Object} publishData
2109     */
2110    _remotePublish:function(subscriptionId, publishData) {
2111      var subscriptionData = dwr.hub._subscriptions[subscriptionId];
2112      if (!subscriptionData) return;
2113      subscriptionData.callback.call(subscriptionData.scope, publishData, subscriptionData.subscriberData);
2114    },
2115
2116    /**
2117     * Each time we subscribe to something, we use a unique number
2118     */
2119    _subscriptionId:0,
2120
2121    /**
2122     * We need to remember what we are subscribed to so we can recall the callback
2123     */
2124    _subscriptions:{}
2125  };
2126})();
2127
2128/**
2129 * High level data-sync API for use by Widget libraries like a Dojo-Data-Store.
2130 * For full documentation see org.directwebremoting.export.Data
2131 */
2132dwr.data = {
2133
2134  /**
2135   * This is just documentation that defines how the listener parameter must act
2136   * in order to receive asynchronous updates
2137   */
2138  StoreChangeListener:{
2139    /**
2140     * Something has removed an item from the store
2141     * @param {StoreProvider} source The store from which it was moved
2142     * @param {string} itemId The ID of the item
2143     */
2144    itemRemoved:function(source, itemId) { },
2145  
2146    /**
2147     * Something has added an item to the store
2148     * @param {StoreProvider} source The store from which it was moved
2149     * @param {Item} item The thing that has changed
2150     */
2151    itemAdded:function(source, item) { },
2152  
2153    /**
2154     * Something has updated an item in the store
2155     * @param {StoreProvider} source The store from which it was moved
2156     * @param {Item} item The thing that has changed
2157     * @param {string[]} changedAttributes A list of changed attributes. If null then
2158     * you should assume that everything has changed
2159     */
2160    itemChanged:function(source, item, changedAttributes) { }
2161  },
2162
2163  /**
2164   * Create a cache object containing the functions to interact with a server
2165   * side StoreProvider
2166   * @param {string} storeId ID of server provided storage
2167   * @param {dwr.data.StoreChangeListener} listener See server documentation
2168   * This is likely to be true if dwr.engine.activeReverseAjax == true
2169   */
2170  Cache:function(storeId, listener) {
2171    this.storeId = storeId;
2172    this.listener = listener;
2173  }
2174};
2175
2176(function() {
2177  /**
2178   * Notes that there is a region of a page that wishes to subscribe to server
2179   * side data and registers a callback function to receive the data.
2180   * @param {Object} region filtering and sorting options. Includes:
2181   * - start: The beginning of the region of specific interest
2182   * - count: The number of items being viewed
2183   * - sort: The sort criteria
2184   * - query: The filter criteria
2185   * @param {function|object} callbackObj A standard DWR callback object
2186   * @return 
2187   */
2188  dwr.data.Cache.prototype.viewRegion = function(region, callbackObj) {
2189    if (!region) region = { };
2190    if (!region.start) region.start = 0;
2191    if (!region.count) region.count = -1;
2192    if (!region.sort) region.sort = [];
2193    if (!region.query) region.query = {};
2194
2195    return dwr.engine._execute(dwr.engine._pathToDwrServlet, '__Data', 'viewRegion', [ this.storeId, region, this.listener, callbackObj ]);
2196  };
2197
2198  /**
2199   * As dwr.data.Cache.viewRegion() except that we only want to see a single item.
2200   * @param {string} itemId ID of object within the given store
2201   * @param {function|object} callbackObj A standard DWR callback object
2202   */
2203  dwr.data.Cache.prototype.viewItem = function(itemId, callbackObj) {
2204    return dwr.engine._execute(dwr.engine._pathToDwrServlet, '__Data', 'viewItem', [ this.storeId, itemId, this.listener, callbackObj ]);
2205  };
2206
2207  /**
2208   * Undo the action of dwr.data.view()
2209   * @param {function|object} callbackObj A standard DWR callback object
2210   */
2211  dwr.data.Cache.prototype.unsubscribe = function(callbackObj) {
2212    if (this.listener) {
2213      return dwr.engine._execute(dwr.engine._pathToDwrServlet, '__Data', 'unsubscribe', [ this.storeId, this.listener, callbackObj ]);
2214    }
2215  };
2216
2217  /**
2218   * Request an update to server side data
2219   * @param {Object} items An array of update descriptions
2220   * @param {function|object} callbackObj A standard DWR callback object
2221   */
2222  dwr.data.Cache.prototype.update = function(items, callbackObj) {
2223    return dwr.engine._execute(dwr.engine._pathToDwrServlet, '__Data', 'update', [ this.storeId, items, callbackObj ]);
2224  };
2225
2226})();
2227

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.