PageSourceSearch

https://realtorchad.ca/__ext.js?c=8&r=js+weblets+responsive+menu.j…etV2.js$5,js+weblets+responsive+leadins.js,

js realtorchad.ca collected 2026-10-07 05:00:56 UTC 112,266 bytes, 3,298 lines download raw bytes

1
2
3/* ==== INCLUDE: /js/weblets/responsive/menu.js ==== */
4
5jQuery( function($) {
6	
7	function calcHorizontalLeaning( $menuitem ) {
8		
9		var left = $menuitem.offset().left;
10		var width = $menuitem.outerWidth();
11		var screen = $(document).width();
12		
13		var rightEdge = screen - ( left + width );
14		var tolerance = screen / 4; // quarter of screen
15		console.log( "rightedge", rightEdge, "tolerance", tolerance );
16		if( rightEdge < tolerance ) {
17			return "left";
18		}
19		else {
20			return "";
21		}
22	}
23		
24	$( document ).on ( "click", ".mrp-menu-resp-wrapper .top-handle", function( e ) {
25		e.stopImmediatePropagation();
26		$(this).toggleClass( "on" );
27		$("html").toggleClass( "mrp-menu-on" );
28		return false;
29	});
30	
31	$( document ).on( "click", ".mrp-menu-resp-wrapper li", function(e) {
32		//if( e.target && e.target.nodeName && e.target.nodeName.toLowerCase() == "a" ) {
33		if( e.target && e.target.nodeName && $(e.target).closest("a").length > 0 ) {
34			var $target = $(e.target).closest("a");
35			if( $target.attr( "href" ) && $target.attr( "href" ) != "javascript:;" ) {
36				return;
37			}
38		}
39		var $on = $( this ).closest( "li" );
40		var $menu = $(this).closest( ".mrp-menu-resp-wrapper" );
41		if( $on.hasClass( "on" ) ) {
42			$on.parent().removeClass( "on" );
43			$on.removeClass( "on" );
44			$on.find( ".on" ).removeClass( "on left" );
45			return false;
46		}
47		$menu.find( "li.on" ).filter( function() {
48			return !( $.contains( this, $on[0] ) ); // this not $(this)!! it's correct
49		}).removeClass( "on left" ).parent().removeClass( "on" );
50		
51		$on.addClass( "on" );
52		$on.addClass( calcHorizontalLeaning( $on ) );
53		$on.parent().addClass( "on" );
54		
55		return false;
56	});
57	
58	$( document ).on( "click.mrp-menu-close", function() {
59		$( ".mrp-menu-resp-wrapper .on" ).removeClass( "on left" );
60		$("html").removeClass( "mrp-menu-on" );
61	});
62});
63
64/* ==== INCLUDE: /js/weblets/responsive/image-banner.js ==== */
65
66function mrp_image_banner_rotate( id, index ) {
67
68	var $weblet = jQuery( "#" + id );
69	
70	if( index >= 0 ) {
71		var interval = $weblet.data( "animation-interval" );
72		if( !isNaN( interval ) ) {
73			clearInterval( interval );
74		}
75	}
76	
77	var $active = $weblet.find( "ul.images li.mrp-resp-banner-show" );
78	if( isNaN( index ) || index < 0 ) {
79		index = parseInt( $active.attr( "data-idx" ) );
80		if( isNaN( index ) ) {
81			return;
82		}
83		index++;
84	}
85	
86	var $next = $weblet.find( "ul.images li[data-idx=" + index + "]" );
87	if( $next.length == 0 ) {
88		// first
89		index = 0;
90	}
91	$next = $weblet.find( "ul.images li[data-idx=" + index + "]" );
92	
93	if( $next.length > 0 ) {
94		$weblet.find( "ul.images li" ).removeClass( "mrp-resp-banner-show" );
95		$next.addClass( "mrp-resp-banner-show" );
96		
97		$weblet.find( ".bullet-nav span.active" ).removeClass( "active" );
98		$weblet.find( ".bullet-nav span[data-idx=" + index + "]" ).addClass( "active" );
99	}
100}
101
102function mrp_banner_nav_next( id ) {
103
104	var $weblet = jQuery( "#" + id );
105	var $active = $weblet.find( "ul.images li.mrp-resp-banner-show" );
106	var idx = parseInt( $active.attr( "data-idx" ) ) || 0;
107	idx = idx + 1;
108	if( idx > $weblet.find( ".images > li img" ).length - 1 ) {
109		idx = 0;
110	}
111	mrp_image_banner_rotate( id, idx );
112	
113	try {
114		var interval = $weblet.data( "animation-interval" );
115		if( !isNaN( interval ) ) {
116			clearInterval( interval );
117		}
118	}
119	catch( e ) {}
120}
121
122function mrp_banner_nav_prev( id ) {
123	
124	var $weblet = jQuery( "#" + id );
125	var $active = $weblet.find( "ul.images li.mrp-resp-banner-show" );
126	var idx = parseInt( $active.attr( "data-idx" ) ) || 0;
127	idx = idx - 1;
128	if( idx < 0 ) {
129		idx = $weblet.find( ".images > li img" ).length - 1;
130	}
131	mrp_image_banner_rotate( id, idx );
132	
133
134	try {
135		var interval = $weblet.data( "animation-interval" );
136		if( !isNaN( interval ) ) {
137			clearInterval( interval );
138		}
139	}
140	catch( e ) {}
141}
142
143/* ==== INCLUDE: /w/w=realtorchad.ca/dwr/engine.js ==== */
144
145/*
146 * Copyright 2005 Joe Walker
147 *
148 * Licensed under the Apache License, Version 2.0 (the "License");
149 * you may not use this file except in compliance with the License.
150 * You may obtain a copy of the License at
151 *
152 *     http://www.apache.org/licenses/LICENSE-2.0
153 *
154 * Unless required by applicable law or agreed to in writing, software
155 * distributed under the License is distributed on an "AS IS" BASIS,
156 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
157 * See the License for the specific language governing permissions and
158 * limitations under the License.
159 */
160
161/**
162 * The DWR object is also defined by dwr.util etc.
163 */
164if (typeof dwr == 'undefined') dwr = {};
165
166(function() {
167  if (!dwr.engine) dwr.engine = {};
168
169  /**
170    * Set an alternative error handler from the default alert box.
171    * @param {Function} handler The function to call when an error happens
172    */
173  dwr.engine.setErrorHandler = function(handler) {
174    dwr.engine._errorHandler = handler;
175  };
176
177  /**
178    * Set an alternative warning handler from the default alert box.
179    * @param {Function} handler The function to call when a warning happens
180    */
181  dwr.engine.setWarningHandler = function(handler) {
182    dwr.engine._warningHandler = handler;
183  };
184
185  /**
186    * @deprecated - In favor of the errorHandler being called with an exception name:"dwr.engine.textHtmlReply"
187    * containing a responseText property if available.
188    *
189    * Setter for the text/html handler - what happens if a DWR request gets an HTML
190    * reply rather than the expected Javascript. Often due to login timeout
191    * @param {Function} handler The function to call on an unexpected text/html content type
192    */
193  dwr.engine.setTextHtmlHandler = function(handler) {
194    dwr.engine._textHtmlHandler = handler;
195  };
196
197  dwr.engine.setPollStatusHandler = function(handler) {
198    dwr.engine._pollStatusHandler = handler;
199  };
200
201  /**
202   * Set a default timeout value for all calls. 0 (the default) turns timeouts off.
203   * @param {Function} handler The function to call when we get bored of waiting for a call
204   */
205  dwr.engine.setTimeout = function(timeout) {
206    dwr.engine._timeout = timeout;
207  };
208
209  /**
210   * Set a timeout value for Reverse Ajax polls. 0 (the default) turns timeouts off.
211   */
212  dwr.engine.setPollTimeout = function(timeout) {
213    dwr.engine._pollTimeout = timeout;
214  };
215
216  /**
217    * The Pre-Hook is called before any DWR remoting is done.
218    * @param {Function} handler The function to call before any remote calls
219    */
220  dwr.engine.setPreHook = function(handler) {
221    dwr.engine._preHook = handler;
222  };
223
224  /**
225   * The Post-Hook is called after any DWR remoting is done.
226   * @param {Function} handler The function to call after any remote calls
227   */
228  dwr.engine.setPostHook = function(handler) {
229    dwr.engine._postHook = handler;
230  };
231
232  /**
233   * Set a custom path to the DWR servlet (may be a full URL for cross-domain usage)
234   * @param {string} path path or URL
235   */
236  dwr.engine.setOverridePath = function(path) {
237    dwr.engine._overridePath = path;
238  };
239
240  /**
241   * Set a custom contextPath (typically used when rewriting paths through a web server)
242   * @param {string} path path
243   */
244  dwr.engine.setOverrideContextPath = function(path) {
245    dwr.engine._overrideContextPath = path;
246  };
247
248  /**
249   * Extra attributes to append to the DWRSESSIONID cookie (domain, secure, etc)
250   * @param {string} attributeString attribute string according to cookie syntax
251   */
252  dwr.engine.setCookieAttributes = function(attributeString) {
253    dwr.engine._cookieAttributes = attributeString;
254  };
255
256  /**
257   * Custom headers for all DWR calls
258   * @param {Object} headers Object containing name/value pairs for extra headers
259   */
260  dwr.engine.setHeaders = function(headers) {
261    dwr.engine._headers = headers;
262  };
263
264  /**
265   * Custom attributes transferred to server for all DWR calls
266   * @param {Object} attributes Object containing name/value pairs for attributes
267   */
268  dwr.engine.setAttributes = function(attributes) {
269    dwr.engine._attributes = attributes;
270  };
271
272  /**
273   * Ensure that remote calls happen in the order in which they were sent? (Default: false)
274   * @param {boolean} ordered true to enable ordered processing
275   */
276  dwr.engine.setOrdered = function(ordered) {
277    dwr.engine._ordered = ordered;
278  };
279
280  /**
281   * Do we ask the XHR object to be asynchronous? (Default: true)
282   * Warning: it is <strong>highly</strong> advised to use the default of async
283   * processing, especially when dealing with Internet based requests.
284   * @param {boolean} async false to enable sync processing for XHR queries
285   */
286  dwr.engine.setAsync = function(async) {
287    dwr.engine._async = async;
288  };
289
290  /**
291   * Does the client actively check the server for updates? (Default: false)
292   * @param {boolean} async true to enable low latency reverse ajax
293   */
294  dwr.engine.setActiveReverseAjax = function(activeReverseAjax) {
295    if (activeReverseAjax) {
296      // Bail if we are already started
297      if (dwr.engine._activeReverseAjax) return;
298      // We always want a retry policy when reverse AJAX is enabled.
299      if (!dwr.engine._retryIntervals || dwr.engine._retryIntervals.length === 0) { dwr.engine._retryIntervals = dwr.engine._defaultRetryIntervals; }
300      dwr.engine._activeReverseAjax = true;
301      if (dwr.engine._initializing) return; // We'll start after init is done
302      dwr.engine._poll();
303    }
304    else {
305      // Can we cancel an existing request?
306      if (dwr.engine._activeReverseAjax && dwr.engine._pollBatch) {
307        dwr.engine.transport.abort(dwr.engine._pollBatch);
308      }
309      dwr.engine._activeReverseAjax = false;
310    }
311    // TODO: in iframe mode, if we start, stop, start then the second start may
312    // well kick off a second iframe while the first is still about to return
313    // we should cope with this but we don't
314  };
315
316  /**
317   * Turn server notification of page unload on and off
318   * @param {boolean} notify true or false depending on if we want to turn unload notification on or off
319   */
320  dwr.engine.setNotifyServerOnPageUnload = function(notify, asyncUnload) {
321    dwr.engine._asyncUnload = (asyncUnload !== undefined) ? asyncUnload : false;
322    dwr.engine._isNotifyServerOnPageUnload = notify;
323  };
324
325  /**
326   * Turn server notification of page load on and off
327   * @param {boolean} notify true or false depending on if we want to turn load notification on or off
328   */
329  dwr.engine.setNotifyServerOnPageLoad = function(notify) {
330    dwr.engine._isNotifyServerOnPageLoad = notify;
331    if (notify && !dwr.engine._initializing && !dwr.engine._isNotifyServerOnPageLoadSent) {
332      eval("dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'pageLoaded', [ function() { dwr.engine._ordered = false; }]);");
333      dwr.engine._isNotifyServerOnPageLoadSent = true;
334    }
335  };
336
337  /*
338   * The maximum number of retries, when reached we will no longer attempt to check for server status.
339   * @param - maxRetries
340   */
341  dwr.engine.setMaxRetries = function(maxRetries) {
342    dwr.engine._maxRetries = maxRetries;
343  };
344
345  /*
346   * The intervals between successive retries in seconds
347   * @param - array of integers representing the retry interval in seconds.
348   */
349  dwr.engine.setRetryIntervals = function(intervalsArray) {
350    dwr.engine._retryIntervals = intervalsArray;
351  };
352
353  /**
354   * The default message handler.
355   * @param {String} message The text of the error message
356   * @param {Object} ex An error object containing at least a name and message
357   */
358  dwr.engine.defaultErrorHandler = function(message, ex) {
359    dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true);
360    if (!message) message = "A server error has occurred.";
361    // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
362    if (message.indexOf("0x80040111") != -1) return;
363    if ("true" == "true") alert(message);
364  };
365
366  /**
367   * The default warning handler.
368   * @param {String} message The text of the error message
369   * @param {Object} ex An error object containing at least a name and message
370   */
371  dwr.engine.defaultWarningHandler = function(message, ex) {
372    dwr.engine._debug(message);
373  };
374
375  /**
376   * The default poll status handler.
377   * @param {boolean} newStatus - true = online, false = offline
378   * @param {object} ex - The exception if one exists (offline).
379   */
380  dwr.engine.defaultPollStatusHandler = function(newStatus, ex) {
381    dwr.engine.util.logHandlerEx(function() {
382      if (newStatus === false && dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex);
383    });
384  };
385
386  /**
387   * For reduced latency you can group several remote calls together using a batch.
388   */
389  dwr.engine.beginBatch = function() {
390    if (dwr.engine._batch) {
391      dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" });
392      return;
393    }
394    dwr.engine._batch = dwr.engine.batch.create();
395  };
396
397  /**
398   * Finished grouping a set of remote calls together. Go and execute them all.
399   * @param {Object} options A options object to customize processing
400   */
401  dwr.engine.endBatch = function(options) {
402    var batch = dwr.engine._batch;
403    if (batch == null) {
404      dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" });
405      return;
406    }
407    dwr.engine._batch = null;
408    if (batch.map.callCount === 0) {
409      return;
410    }
411
412    // The hooks need to be merged carefully to preserve ordering
413    if (options) {
414      dwr.engine.batch.merge(batch, options);
415    }
416
417    // In ordered mode, we don't send unless the list of sent items is empty
418    // (queuing is not possible for sync calls so let them slip through)
419    if (batch.async && (dwr.engine._ordered || dwr.engine._internalOrdered) && dwr.engine._batchesLength !== 0) {
420      dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch;
421    }
422    else {
423      return dwr.engine.transport.send(batch);
424    }
425  };
426
427  /**
428   * For use with file downloads. When a DWR function returns a binary download
429   * you can prompt the user to save it using this function
430   * @param {Object} data The binary data passed from DWR
431   */
432  dwr.engine.openInDownload = function(data) {
433    var div = document.createElement("div");
434    document.body.appendChild(div);
435    div.innerHTML = "<iframe width='0' height='0' scrolling='no' frameborder='0' src='" + data + "'></iframe>";
436  };
437
438  /**
439   * What is the current version DWR number
440   * DWR version numbers are of the form "Version 1.2.3.3128[.beta]", where:
441   * 1 is the major release number. Changes in major version number indicate
442   * significant enhancements in functionality
443   * 2 is the minor release number. Changes in minor version number indicate
444   * less significant changes in functionality
445   * 3 is the revision release number. Changes here typically indicate bug
446   * fixes only
447   * 3128 is the build number. This number increments for each build
448   * .beta is a release title that is generally only used for non production
449   * releases to indicate the purpose/quality of the release
450   * The label is these strings concatenated
451   */
452  dwr.version = {
453    /**
454     * Changes in major version number indicate significant enhancements
455     */
456    major:parseInt("3", 10),
457
458    /**
459     * Changes in minor version number indicate smaller enhancements
460     */
461    minor:parseInt("0", 10),
462
463    /**
464     * Changes with the revision number typically indicate bug-fixes only
465     */
466    revision:parseInt("3", 10),
467
468    /**
469     * The build number increments for each build
470     */
471    build:parseInt("-1", 10),
472
473    /**
474     * Only used for non production releases to indicate the purpose/quality of
475     * the release. Example titles include 'milestone1' or 'beta3'.
476     */
477    title:"dev",
478
479    /**
480     * The strings above concatenated
481     */
482    label:"3.0.3-dev"
483  };
484
485  //==============================================================================
486  // Only private stuff below here
487  //==============================================================================
488
489  /** Keep track of page load initialization */
490  dwr.engine._initializing = true;
491
492  /** Is GET enabled? */
493  dwr.engine._allowGetButMakeForgeryEasier = "true";
494
495  /** The script prefix to strip in the case of scriptTagProtection. */
496  dwr.engine._scriptTagProtection = "throw 'allowScriptTagRemoting is false.';";
497
498  /** The default path to the DWR servlet */
499  dwr.engine._pathToDwrServlet = "/w/w=realtorchad.ca/dwr";
500
501  /** Custom path to the DWR servlet */
502  dwr.engine._overridePath = "";
503  if (typeof pathToDwrServlet != "undefined") {
504    dwr.engine._overridePath = pathToDwrServlet;
505  }
506  
507  dwr.engine._effectivePath = function() {
508    return dwr.engine._overridePath || dwr.engine._pathToDwrServlet;
509  };
510
511  /** The webapp's contextPath */
512  dwr.engine._contextPath = "";
513
514  /** Custom contextPath */
515  dwr.engine._overrideContextPath = "";
516
517  dwr.engine._effectiveContextPath = function() {
518    return dwr.engine._overrideContextPath || dwr.engine._contextPath;
519  };
520
521  /** Extra attributes to append to the DWRSESSIONID cookie (domain, secure, etc) */
522  dwr.engine._cookieAttributes = "";
523
524  /** Do we use XHR for reverse ajax because we are not streaming? */
525  dwr.engine._useStreamingPoll = "true";
526
527  dwr.engine._pollOnline = true;
528
529  /** These URLs can be configured from the server */
530  dwr.engine._ModePlainCall = "/call/plaincall/";
531  dwr.engine._ModePlainPoll = "/call/plainpoll/";
532  dwr.engine._ModeHtmlCall = "/call/htmlcall/";
533  dwr.engine._ModeHtmlPoll = "/call/htmlpoll/";
534
535  /** Do we make the calls async? Default to 'true' */
536  dwr.engine._async = Boolean("true");
537
538  /** The local page id */
539  dwr.engine._pageId = null;
540
541  /** The browser instance id */
542  dwr.engine._dwrSessionId = null;
543
544  /** The global page id (browser instance + local page id) */
545  dwr.engine._scriptSessionId = ""; // we must use "" and not null
546
547  /** A function to be called before requests are marshalled. Can be null. */
548  dwr.engine._preHook = null;
549
550  /** A function to be called after replies are received. Can be null. */
551  dwr.engine._postHook = null;
552
553  /** A map of the batches that we have sent and are awaiting a reply on. */
554  dwr.engine._batches = {};
555
556  /** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */
557  dwr.engine._batchesLength = 0;
558
559  /** In ordered mode, the array of batches waiting to be sent */
560  dwr.engine._batchQueue = [];
561
562  /** User setting for saying that calls should execute one by one in the order in which they were sent */
563  dwr.engine._ordered = false;
564
565  /** Internal state forcing calls to execute one by one in the order in which they were sent */
566  dwr.engine._internalOrdered = false;
567
568  /** The current batch (if we are in batch mode) */
569  dwr.engine._batch = null;
570
571  /** The global timeout */
572  dwr.engine._timeout = 0;
573
574  /** Are we doing comet or polling? */
575  dwr.engine._activeReverseAjax = false;
576  
577  /** All reverse ajax scripts before this index have been received and executed */
578  dwr.engine._nextReverseAjaxIndex = 0;
579  
580  /** Queue of reverse ajax scripts (functions) that have been received but not yet executed due to receiving in wrong order */
581  dwr.engine._reverseAjaxQueue = {};
582  
583  /** The batch that we are using to poll */
584  dwr.engine._pollBatch = null;
585
586  /** Maximum time we wait for a poll to return (0 = no timeout) */
587  dwr.engine._pollTimeout = 0;
588
589  /** How many milliseconds between internal comet polls */
590  dwr.engine._pollCometInterval = 200;
591
592  /** How many times have we re-tried a call? */
593  dwr.engine._retries = 1;
594  dwr.engine._maxRetries = -1;
595
596  /** The intervals between successive retries in seconds */
597  dwr.engine._retryIntervals = [];
598
599  /** Used as the default for reverse ajax/polling
600   *  Retry immediately twice first with a one second interval, next with a 3 second interval, then go offline.
601   *  Therafter, retry every 3 seconds.
602   */
603  dwr.engine._defaultRetryIntervals = [ 1, 3, 3 ];
604
605  dwr.engine._textHtmlHandler = null;
606
607  /** If you wish to send custom headers with every request */
608  dwr.engine._headers = null;
609
610  /** If you wish to send extra custom request attributes with each request */
611  if (typeof attributes != "undefined") {
612    dwr.engine._attributes = attributes;
613  }
614  else {
615    dwr.engine._attributes = null;
616  }
617
618  /** Batch ids allow us to know which batch the server is answering */
619  dwr.engine._nextBatchId = 0;
620
621  /** The instance id helps us distinguish multiple engine.js loaded in one page */
622  dwr.engine._instanceId = -1;
623
624  /** A list of the properties that need merging from calls to a batch */
625  dwr.engine._propnames = [ "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ];
626
627  /** Do we stream, or can be hacked to do so? */
628  dwr.engine._partialResponseNo = 0;
629  dwr.engine._partialResponseYes = 1;
630
631  /** Are we notifying the server on page unload? */
632  dwr.engine._isNotifyServerOnPageUnload = false;
633
634  /** Are we notifying the server on page load? */
635  dwr.engine._isNotifyServerOnPageLoad = false;
636  dwr.engine._isNotifyServerOnPageLoadSent = false;
637
638  /** Should the unload call be asynchronous?  If true it may not be called by the browser. */
639  dwr.engine._asyncUnload = false;
640
641  /**
642   * A map of all mapped classes whose class declarations have been loaded
643   * (dwrClassName -> constructor function)
644   */
645  dwr.engine._mappedClasses = {};
646
647  /** A function to call if something fails. */
648  dwr.engine._errorHandler = dwr.engine.defaultErrorHandler;
649
650  /** For debugging when something unexplained happens. */
651  dwr.engine._warningHandler = dwr.engine.defaultWarningHandler;
652
653  dwr.engine._pollStatusHandler = dwr.engine.defaultPollStatusHandler;
654
655  /** Undocumented interceptors - do not use */
656  dwr.engine._postSeperator = "\n";
657  dwr.engine._defaultInterceptor = function(data) { return data; };
658  dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor;
659  dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor;
660  dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor;
661
662  /** Object attributes to ignore when serializing */
663  dwr.engine._excludeObjectAttributes = {
664    "$dwrClassName": true,
665    "$dwrByRef": true,
666    "$_dwrConversionRef": true
667  };
668
669  dwr.engine._ieConditions = {};
670  
671  /** Is this page doing beforeunload processing? */
672  dwr.engine._beforeUnloading = false;
673
674  /** A place to temporarily store exception before reporting */
675  dwr.engine._queuedBatchException = null;
676
677  /** @private Keep beforeunload flag set during beforeunload processing (including potential
678   *  "Are you sure?" popups). */
679  dwr.engine._beforeUnloader = function() {
680    dwr.engine._beforeUnloading = true;
681    // We want to reset the flag if unloading is aborted by "Are you sure?" popup. We detect
682    // return to interactive mode with a timeout that normally will execute directly after
683    // unload is aborted through popup.
684    setTimeout(function() {
685      // Some browsers (f ex Firefox) will execute the timeout even if unload is not aborted.
686      // To keep a continuous unloading state we therefore prolong the beforeunload state for
687      // a second so the unload flag gets time to trigger.
688      setTimeout(function() {
689        dwr.engine._beforeUnloading = false;
690      }, 1000);
691    }, 1);
692  };
693
694  /** Is this page in the process of unloading? */
695  dwr.engine._unloading = false;
696
697  /** @private Abort any XHRs in progress at page unload (solves zombie socket problems in IE). */
698  dwr.engine._unloader = function() {
699    dwr.engine._unloading = true;
700
701    // Empty queue of waiting ordered requests
702    dwr.engine._batchQueue.length = 0;
703
704    // Abort any ongoing requests and clear their batches
705    var batch;
706    for (var batchId in dwr.engine._batches) {
707      batch = dwr.engine._batches[batchId];
708      if (batch.transport && batch.transport.abort) {
709        batch.transport.abort(batch);
710      }
711    }
712
713    // If we have used reverse ajax then we try to tell the server we are gone
714    if (dwr.engine._isNotifyServerOnPageUnload) {
715      dwr.engine._debug("calling unloader for: " + dwr.engine._scriptSessionId);
716      batch = {
717        map:{
718          callCount:1,
719          'c0-scriptName':'__System',
720          'c0-methodName':'pageUnloaded',
721          'c0-id':0
722        },
723        paramCount:0, isPoll:false, async:dwr.engine._asyncUnload,
724        headers:{}, preHooks:[], postHooks:[],
725        timeout:dwr.engine._timeout,
726        errorHandler:null, globalErrorHandler:dwr.engine._errorHandler, warningHandler:null, textHtmlHandler:null, globalTextHtmlHandler:null,
727        handlers:[{ exceptionHandler:null, callback:null }]
728      };
729      dwr.engine.transport.send(batch);
730      dwr.engine._isNotifyServerOnPageUnload = false;
731    }
732  };
733
734  function ignoreIfUnloading(batch, f) {
735    // Do nothing if we are unloading
736    if (dwr.engine._unloading) return;
737    // Retry later if beforeunload is in progress and this is not a sync call
738    if (dwr.engine._beforeUnloading && (batch == null || batch.async)) {
739      setTimeout(function() {
740        ignoreIfUnloading(batch, f);
741      }, 100);
742    }
743    // Otherwise trigger the target func
744    else {
745      return f();
746    }
747  }
748
749  dwr.engine._initializer = {
750    /*
751     * Load-time initializations
752     */
753    preInit: function() {
754      // Make random local page id
755      dwr.engine._pageId = dwr.engine.util.tokenify(new Date().getTime()) + "-" + dwr.engine.util.tokenify(Math.random() * 1E16);
756
757      // Reuse any existing dwr session
758      dwr.engine.transport.updateDwrSessionFromCookie();
759
760      // Register unload handlers
761      dwr.engine.util.addEventListener(window, 'beforeunload', dwr.engine._beforeUnloader);
762      dwr.engine.util.addEventListener(window, 'unload', dwr.engine._unloader);
763
764      // Register this engine instance globally
765      var g = dwr.engine._global;
766      if (!g.dwr) g.dwr = {};
767      if (!g.dwr._) g.dwr._ = [];
768      dwr.engine._instanceId = g.dwr._.length;
769      g.dwr._[dwr.engine._instanceId] = dwr;
770    },
771
772    loadDwrConfig: function() {
773      if (typeof dwrConfig != "undefined") {
774        for(p in dwrConfig) {
775          var methodName = "set" + p.charAt(0).toUpperCase() + p.substring(1);
776          var setter = dwr.engine[methodName];
777          if (setter) setter(dwrConfig[p]);
778        }
779      }
780    },
781    
782    init: function() {
783      dwr.engine._initializer.preInit();
784      dwr.engine._initializer.loadDwrConfig();
785      dwr.engine._initializing = false;
786      // Now we can start sending requests
787      if (dwr.engine._isNotifyServerOnPageLoad) {
788        eval("dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'pageLoaded', [ function() { dwr.engine._ordered = false; }]);");
789        dwr.engine._isNotifyServerOnPageLoadSent = true;
790      }
791      if (dwr.engine._activeReverseAjax) {
792        dwr.engine._poll();
793      }
794    }
795  };
796
797  /**
798   * Send a request. Called by the JavaScript interface stub
799   * @private
800   * @param path part of URL after the host and before the exec bit without leading or trailing /s
801   * @param scriptName The class to execute
802   * @param methodName The method on said class to execute
803   * @param func The callback function to which any returned data should be passed
804   *       if this is null, any returned data will be ignored
805   * @param args The parameters to passed to the above method
806   */
807  dwr.engine._execute = function(overridePath, scriptName, methodName, args) {
808    var path = overridePath || dwr.engine._effectivePath();
809    dwr.engine._singleShot = false;
810    if (dwr.engine._batch == null) {
811      dwr.engine.beginBatch();
812      dwr.engine._singleShot = true;
813    }
814
815    var batch = dwr.engine._batch;
816
817    // All the paths MUST be to the same servlet
818    if (batch.path == null) {
819      batch.path = path;
820    }
821    else {
822      if (batch.path != path) {
823        dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." });
824        return;
825      }
826    }
827
828    dwr.engine.batch.addCall(batch, scriptName, methodName, args);
829
830    if (dwr.engine._isHeartbeatBatch(batch)) {
831      // Heartbeats should fail fast.
832      batch.timeout = 750;
833    }
834
835    // Now we have finished remembering the call, we increment the call count
836    batch.map.callCount++;
837    if (dwr.engine._singleShot) {
838      return dwr.engine.endBatch();
839    }
840  };
841
842  /**
843   * Poll the server to see if there is any data waiting
844   * @private
845   */
846  dwr.engine._poll = function() {
847    if (!dwr.engine._activeReverseAjax) {
848      return;
849    }
850    dwr.engine._pollBatch = dwr.engine.batch.createPoll();
851    dwr.engine.transport.send(dwr.engine._pollBatch);
852  };
853
854  /** @private Utility to execute incoming scripts */
855  dwr.engine._executeScript = function(script) {
856    if (script == null) {
857      return null;
858    }
859    if (script === "") {
860      dwr.engine._debug("Warning: blank script", true);
861      return null;
862    }
863    // Using Function instead of eval as the latter has memory problems on IE9
864    (new Function("dwr", script))(dwr);
865  };
866
867  /** @private call all the post hooks for a batch */
868  dwr.engine._callPostHooks = function(batch) {
869    if (batch.postHooks) {
870      for (var i = 0; i < batch.postHooks.length; i++) {
871        batch.postHooks[i]();
872      }
873      batch.postHooks = null;
874    }
875  };
876
877  /**
878   * Is this a batch that is checking for a heartbeat?
879   * If so we want to ignore errors/warnings, etc.
880   * @private
881   * @param {Object} batch
882   */
883  dwr.engine._isHeartbeatBatch = function(batch) {
884    return (batch.map && batch.map['c0-methodName'] === 'checkHeartbeat' && batch.map['c0-scriptName'] === '__System');
885  };
886
887  /**
888   * Generic error handling routing to save having null checks everywhere
889   * @private
890   * @param {Object} batch
891   * @param {Object} ex
892   */
893  dwr.engine._handleError = function(batch, ex) {
894    if (batch && dwr.engine._isHeartbeatBatch(batch)) { // Ignore errors that happen while checking for a heartbeat.
895      return;
896    }
897    var errorHandlers = [];
898    var anyCallWithoutErrorHandler = false;
899    if (batch && batch.isPoll) { // No error reporting and only retry for polls
900      dwr.engine._handlePollRetry(batch, ex);
901    } else {
902      // Perform error cleanup synchronously
903      if (batch) {
904        for (var i = 0; i < batch.map.callCount; i++) {
905          var handlers = batch.handlers[i];
906          if (!handlers.completed) {
907            if (typeof handlers.errorHandler == "function")
908              errorHandlers.push(handlers.errorHandler);
909            else
910              anyCallWithoutErrorHandler = true;
911            handlers.completed = true;
912          }
913        }
914      }
915      // Perform error reporting asynchronously (possibly)
916      ignoreIfUnloading(batch, function() {
917        if (batch) {
918          var textHtmlHandler = batch.textHtmlHandler || batch.globalTextHtmlHandler;
919          if (ex.name === "dwr.engine.textHtmlReply" && textHtmlHandler) {
920            dwr.engine.util.logHandlerEx(function() {
921              textHtmlHandler(ex);
922            });
923            return;
924          }
925        }
926        dwr.engine._prepareException(ex);
927        var errorHandler;
928        while(errorHandlers.length > 0) {
929          errorHandler = errorHandlers.shift();
930          dwr.engine.util.logHandlerEx(function() {
931            errorHandler(ex.message, ex);
932          });
933        }
934        if (batch) {
935          dwr.engine.util.logHandlerEx(function() {
936            if (typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
937            else if (anyCallWithoutErrorHandler) {
938              if (typeof batch.globalErrorHandler == "function") batch.globalErrorHandler(ex.message, ex);
939            }
940          });
941          dwr.engine.batch.remove(batch);
942        }
943      });
944    }
945  };
946
947  /**
948   * Handle retries for polling as well as online/offline status.
949   * @private
950   * @param {Object} batch
951   * @param {Object} ex
952   */
953  dwr.engine._handlePollRetry = function(batch, ex) {
954      var retryInterval = dwr.engine._getRetryInterval();
955      // If number of retries is equal to the retryIntervals array length, call the supplied pollStatusHandler and go offline.
956      if (dwr.engine._retries === dwr.engine._retryIntervals.length - 1) {
957        dwr.engine._debug("poll retry - going offline: " + retryInterval/1000 + " seconds");
958        dwr.engine._handlePollStatusChange(false, ex, batch);
959        // We are offline, continue to check for a heartbeat until _retries is > _maxRetries.
960        var heartbeatInterval = setInterval(function() {
961          if (dwr.engine._maxRetries === -1 || dwr.engine._retries < dwr.engine._maxRetries) {
962            dwr.engine._execute(null, '__System', 'checkHeartbeat', [ function() {
963              // We found a heartbeat, we are back online!
964              clearInterval(heartbeatInterval);
965              dwr.engine._poll();
966            }]);
967            dwr.engine._retries++;
968            dwr.engine._debug("DWR Offline - poll retry - interval: " + retryInterval/1000 + " seconds");
969          } else {
970            // maxRetries has been reached, stop the heartbeat check.
971            clearInterval(heartbeatInterval);
972            dwr.engine._debug("max retries reached, stop polling for server status.");
973            dwr.engine._handlePollStatusChange(false, ex, batch);
974          }
975        }, retryInterval);
976      } else { // We are still online, poll again.
977        dwr.engine._retries++;
978        dwr.engine.batch.remove(batch);
979        dwr.engine._debug("DWR Online - poll retry - interval: " + retryInterval/1000 + " seconds");
980        setTimeout(dwr.engine._poll, retryInterval);
981      }
982  };
983
984  dwr.engine._getRetryInterval = function() {
985    var retryInterval;
986    if (dwr.engine._retries < dwr.engine._retryIntervals.length) {
987      retryInterval = dwr.engine._retryIntervals[dwr.engine._retries] * 1000;
988    } else {
989      // The last interval in retryIntervals is the number that will be used to poll when offline.
990      retryInterval = dwr.engine._retryIntervals[dwr.engine._retryIntervals.length - 1] * 1000;
991    }
992    return retryInterval;
993  };
994
995  /**
996   * Handles polling status changes - online or offline.
997   * @param {boolean} newStatus - true = online, false = offline
998   * @param {object} ex - The exception if one exists (offline).
999   */
1000  dwr.engine._handlePollStatusChange = function(newStatus, ex, batch) {
1001  if (batch.isPoll || dwr.engine._isHeartbeatBatch(batch)) {
1002      var changed = dwr.engine._pollOnline !== newStatus;
1003      var maxRetriesReached = dwr.engine._maxRetries === dwr.engine._retries;
1004      dwr.engine._pollOnline = newStatus;
1005      if ((changed || maxRetriesReached) && typeof dwr.engine._pollStatusHandler === "function") 
1006        dwr.engine.util.logHandlerEx(function() {
1007          dwr.engine._pollStatusHandler(newStatus, ex, maxRetriesReached);
1008        });
1009      if (newStatus) {
1010        dwr.engine._retries = 1;
1011      }
1012  }
1013  };
1014
1015  /**
1016   * Generic error handling routing to save having null checks everywhere.
1017   * @private
1018   * @param {Object} batch
1019   * @param {Object} ex
1020   */
1021  dwr.engine._handleWarning = function(batch, ex) {
1022    if (batch && dwr.engine._isHeartbeatBatch(batch)) { // Ignore warnings that happen while checking for a heartbeat.
1023      return;
1024    }
1025    ignoreIfUnloading(batch, function() {
1026      // If this is a poll, we should retry!
1027      dwr.engine._prepareException(ex);
1028      dwr.engine.util.logHandlerEx(function() {
1029        if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex);
1030        else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex);
1031      });
1032      if (batch) dwr.engine.batch.remove(batch);
1033    });
1034  };
1035
1036  /**
1037   * Prepares an exception for an error/warning handler.
1038   * @private
1039   * @param {Object} ex
1040   */
1041  dwr.engine._prepareException = function(ex) {
1042    if (typeof ex == "string") ex = { name:"unknown", message:ex };
1043    if (ex.message == null) ex.message = "";
1044    if (ex.name == null) ex.name = "unknown";
1045  };
1046
1047  /**
1048   * Create a new object that delegates to obj
1049   * @param obj
1050   */
1051  dwr.engine._delegate = (function() {
1052    function F(){}
1053    return (function(obj){
1054      F.prototype = obj;
1055      return new F();
1056    });
1057  })();
1058
1059  /**
1060   * Create a new object that delegates to obj
1061   * @param obj
1062   */
1063  dwr.engine._createFromMap = function(map) {
1064    var obj = new this(); // this should be set to a constructor function!
1065    for(prop in map) if (map.hasOwnProperty(prop)) obj[prop] = map[prop];
1066    return obj;
1067  };
1068
1069  /**
1070   * A reference to the global context (window when in a browser)
1071   */
1072  dwr.engine._global = (function(){return this;}).call(null);
1073
1074  /**
1075   * Navigates properties from the global scope and down to fetch a property
1076   * value.
1077   * @param prop hierarchical property name
1078   * @return property value, or undefined if doesn't exist
1079   */
1080  dwr.engine._getObject = function(prop) {
1081    var parts = prop.split(".");
1082    var value = undefined;
1083    var scope = dwr.engine._global;
1084    while(parts.length > 0) {
1085      var currprop = parts.shift();
1086      if (!scope) return undefined;
1087      value = scope[currprop];
1088      if (value && value.tagName && document.getElementById(currprop) == value) return undefined;
1089      scope = value;
1090    }
1091    return value;
1092  };
1093
1094  /**
1095   * Navigates properties from the global scope and down to set a value.
1096   * @param prop hierarchical property name
1097   * @param obj property value to set
1098   */
1099  dwr.engine._setObject = function(prop, obj) {
1100    var parts = prop.split(".");
1101    var level;
1102    var scope = dwr.engine._global;
1103    while(parts.length > 0) {
1104      var currprop = parts.shift();
1105      if (parts.length === 0) {
1106        scope[currprop] = obj;
1107      }
1108      else {
1109        level = scope[currprop];
1110        if (level == null || level.tagName && document.getElementById(currprop) == level) {
1111          scope[currprop] = level = {};
1112        }
1113        scope = level;
1114      }
1115    }
1116  };
1117
1118  /**
1119   * Used internally when some message needs to get to the programmer
1120   * @private
1121   * @param {String} message
1122   * @param {Object} stacktrace
1123   */
1124  dwr.engine._debug = function(message, stacktrace) {
1125    var written = false;
1126    try {
1127      if (window.console) {
1128        if (stacktrace && window.console.trace) window.console.trace();
1129        if (window.console.log) window.console.log(message);
1130        written = true;
1131      }
1132      else if (window.opera && window.opera.postError) {
1133        window.opera.postError(message);
1134        written = true;
1135      }
1136    }
1137    catch (ex) { /* ignore */ }
1138
1139    if (!written) {
1140      var debug = document.getElementById("dwr-debug");
1141      if (debug) {
1142        var contents = message + "<br/>" + debug.innerHTML;
1143        if (contents.length > 2048) contents = contents.substring(0, 2048);
1144        debug.innerHTML = contents;
1145      }
1146    }
1147  };
1148
1149  /**
1150   * Functions called by the server
1151   */
1152  dwr.engine.remote = {
1153    /**
1154     * Execute a callback
1155     * @private
1156     * @param {int} batchId The ID of the batch that we are replying to
1157     * @param {int} callId The call ID that the script relates to
1158     * @param {String} reply The script to execute
1159     */
1160    handleCallback:function(batchId, callId, reply) {
1161      var batch = dwr.engine._batches[batchId];
1162      if (!batch) return;
1163
1164      // We store the reply in the batch so we can return the data when in sync mode
1165      batch.reply = reply;
1166
1167      // Error handlers inside here indicate an error that is nothing to do
1168      // with DWR so we handle them differently.
1169      try {
1170        var handlers = batch.handlers[callId];
1171        if (!handlers) {
1172          dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true);
1173        }
1174        else {
1175          batch.handlers[callId].completed = true;
1176          if (typeof handlers.callback == "function") {
1177            dwr.engine.util.logHandlerEx(function() {
1178              if(handlers.callbackArg !== undefined)
1179        	    handlers.callback.call(handlers.callbackScope, reply, handlers.callbackArg);
1180              else
1181                handlers.callback.call(handlers.callbackScope, reply);
1182            });
1183          }
1184        }
1185      }
1186      catch (ex) {
1187        dwr.engine._handleError(batch, ex);
1188      }
1189    },
1190
1191    /**
1192     * Called by the server when a JavascriptFunction is executed
1193     * @param id The ID of the serialized function
1194     * @param args The arguments to pass to the function
1195     */
1196    handleFunctionCall:function(id, args) {
1197      var func = dwr.engine.serialize.remoteFunctions[id];
1198      func.apply(window, args);
1199    },
1200
1201    /**
1202     * Called by the server when a JavascriptFunction is executed
1203     * @param id The ID of the serialized function
1204     * @param args The arguments to pass to the function
1205     */
1206    handleObjectCall:function(id, methodName, args) {
1207      var obj = dwr.engine.serialize.remoteFunctions[id];
1208      obj[methodName].apply(obj, args);
1209    },
1210
1211    /**
1212     * Called by the server when a JavascriptFunction is executed
1213     * @param propertyName The ID of the serialized function
1214     * @param data The arguments to pass to the function
1215     */
1216    handleSetCall:function(id, propertyName, data) {
1217      var obj = dwr.engine.serialize.remoteFunctions[id];
1218      obj[propertyName] = data;
1219    },
1220
1221    /**
1222     * Called by the server when a JavascriptFunction is closed
1223     * @param id The ID of the serialized function
1224     */
1225    handleFunctionClose:function(id) {
1226      delete dwr.engine.serialize.remoteFunctions[id];
1227    },
1228
1229    /**
1230     * Called by the server: Handle an exception for a call
1231     * @private
1232     * @param {int} batchId The ID of the batch that we are replying to
1233     * @param {int} callId The call ID that the script relates to
1234     * @param {String} reply The script to execute
1235     */
1236    handleException:function(batchId, callId, ex) {
1237      var batch = dwr.engine._batches[batchId];
1238      if (!batch) return;
1239
1240      var handlers = batch.handlers[callId];
1241      batch.handlers[callId].completed = true;
1242      if (handlers == null) {
1243        dwr.engine._debug("Warning: null handlers in remoteHandleException", true);
1244        return;
1245      }
1246
1247      if (ex.message === undefined) {
1248        ex.message = "";
1249      }
1250
1251      dwr.engine.util.logHandlerEx(function() {
1252        if (typeof handlers.exceptionHandler == "function") {
1253          if (handlers.exceptionArg !== undefined)
1254            handlers.exceptionHandler.call(handlers.exceptionScope, ex.message, ex, handlers.exceptionArg);
1255          else
1256            handlers.exceptionHandler.call(handlers.exceptionScope, ex.message, ex);
1257        }
1258        else if (typeof handlers.errorHandler == "function") {
1259          handlers.errorHandler(ex.message, ex);
1260        }
1261        else if (typeof batch.errorHandler == "function") {
1262          batch.errorHandler(ex.message, ex);
1263        }
1264        else if (typeof batch.globalErrorHandler == "function") {
1265          batch.globalErrorHandler(ex.message, ex);
1266        }
1267      });
1268    },
1269
1270    /**
1271     * Called by the server when a new Reverse Ajax script arrives
1272     */
1273    handleReverseAjax:function(reverseAjaxIndex, reverseAjaxFunc) {
1274      if (reverseAjaxIndex < dwr.engine._nextReverseAjaxIndex) return;
1275      dwr.engine._reverseAjaxQueue[reverseAjaxIndex] = reverseAjaxFunc;
1276      while(true) {
1277        var nextFunc = dwr.engine._reverseAjaxQueue[dwr.engine._nextReverseAjaxIndex];
1278        if (!nextFunc) return;
1279        dwr.engine.util.logHandlerEx(function() {
1280          nextFunc();
1281        });
1282        delete dwr.engine._reverseAjaxQueue[dwr.engine._nextReverseAjaxIndex];
1283        dwr.engine._nextReverseAjaxIndex++;
1284      }
1285    },
1286    
1287    /**
1288     * Called by the server: The whole batch is broken
1289     * @private
1290     * @param {Object} ex The data about what broke
1291     * @param {int} batchId The ID of the batch that we are replying to
1292     */
1293    handleBatchException:function(ex, batchId) {
1294      var batch = dwr.engine._batches[batchId];
1295      if (ex.message === undefined) ex.message = "";
1296      if (batch) {
1297        dwr.engine._handleError(batch, ex);
1298      } else {
1299        dwr.engine._queuedBatchException = ex;
1300      }
1301    },
1302
1303    /**
1304     * Called by the server: Reverse ajax should not be used
1305     * @private
1306     * @param {Object} ex
1307     * @param {int} batchId
1308     */
1309    pollCometDisabled:function(ex, batchId){
1310      dwr.engine.setActiveReverseAjax(false);
1311      var batch = dwr.engine._batches[batchId];
1312      if (ex.message === undefined) {
1313        ex.message = "";
1314      }
1315      dwr.engine._handleError(batch, ex);
1316    },
1317
1318    /**
1319     * Called by the server: Create a new object of a mapped class
1320     * @private
1321     * @param {string} dwrClassName the name of the mapped class
1322     * @param {Object} memberMap the object's data members
1323     */
1324    newObject:function(dwrClassName, memberMap){
1325      var classfunc = dwr.engine._mappedClasses[dwrClassName];
1326      if (classfunc && classfunc.createFromMap) {
1327        return classfunc.createFromMap(memberMap);
1328      }
1329      else {
1330        memberMap.$dwrClassName = dwrClassName;
1331        return memberMap;
1332      }
1333    }
1334  };
1335
1336  /**
1337   * Functions to serialize a data set into a list of parameters
1338   */
1339  dwr.engine.serialize = {
1340    /**
1341     * ActiveX objects to use when we want to convert an xml string into a DOM object
1342     */
1343    domDocument:[
1344      "Msxml2.DOMDocument.6.0",
1345      "Msxml2.DOMDocument.5.0",
1346      "Msxml2.DOMDocument.4.0",
1347      "Msxml2.DOMDocument.3.0",
1348      "MSXML2.DOMDocument",
1349      "MSXML.DOMDocument",
1350      "Microsoft.XMLDOM"
1351    ],
1352
1353    /**
1354     * A holder for functions that we have serialized for remote calling.
1355     */
1356    remoteFunctions:{},
1357
1358    /**
1359     * The ID of the next function that we serialize
1360     */
1361    funcId:0,
1362
1363    /**
1364     * Convert a text representation of XML into a DOM element
1365     * @param {String} xml An xml string
1366     */
1367    toDomElement:function(xml) {
1368      return dwr.engine.serialize.toDomDocument(xml).documentElement;
1369    },
1370
1371    /**
1372     * Convert a text representation of XML into a DOM document
1373     * @param {String} xml An xml string
1374     */
1375    toDomDocument:function(xml) {
1376      var dom;
1377      if (window.DOMParser) {
1378        var parser = new DOMParser();
1379        dom = parser.parseFromString(xml, "text/xml");
1380        if (!dom.documentElement || dom.documentElement.tagName == "parsererror") {
1381          var message = dom.documentElement.firstChild.data;
1382          message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data;
1383          throw message;
1384        }
1385        return dom;
1386      }
1387      else if (window.ActiveXObject) {
1388        dom = dwr.engine.util.newActiveXObject(dwr.engine.serialize.domDocument);
1389        dom.loadXML(xml); // What happens on parse fail with IE?
1390        return dom;
1391      }
1392      else {
1393        var div = document.createElement("div");
1394        div.innerHTML = xml;
1395        return div;
1396      }
1397    },
1398
1399    /**
1400     * Marshall a data item
1401     * @private
1402     * @param batch A map of variables to how they have been marshalled
1403     * @param directrefmap A map of already marshalled variables to prevent recurrsion (these have direct links from their data object)
1404     * @param otherrefmap A map of already marshalled variables to prevent recursion (these have no links from data objects)
1405     * @param data The data to be marshalled
1406     * @param name The name of the data being marshalled
1407     */
1408    convert:function(batch, directrefmap, otherrefmap, data, name, depth) {
1409      if (data == null) {
1410        batch.map[name] = "null:null";
1411        return;
1412      }
1413
1414      switch (typeof data) {
1415      case "boolean":
1416        batch.map[name] = "boolean:" + data;
1417        break;
1418      case "number":
1419        batch.map[name] = "number:" + data;
1420        break;
1421      case "string":
1422        batch.map[name] = "string:" + encodeURIComponent(data);
1423        break;
1424      case "object":
1425        var ref = dwr.engine.serialize.lookup(directrefmap, otherrefmap, data, name);
1426        var objstr = Object.prototype.toString.call(data);
1427        if (data.$dwrByRef) batch.map[name] = dwr.engine.serialize.convertByReference(batch, directrefmap, otherrefmap, data, name, depth + 1);
1428        else if (ref != null) batch.map[name] = ref;
1429        else if (objstr == "[object String]") batch.map[name] = "string:" + encodeURIComponent(data);
1430        else if (objstr == "[object Boolean]") batch.map[name] = "boolean:" + data;
1431        else if (objstr == "[object Number]") batch.map[name] = "number:" + data;
1432        else if (objstr == "[object Date]") batch.map[name] = "date:" + data.getTime();
1433        else if (objstr == "[object Array]") batch.map[name] = dwr.engine.serialize.convertArray(batch, directrefmap, otherrefmap, data, name, depth + 1);
1434        else if (data && data.tagName && data.tagName.toLowerCase() == "input" && data.type && data.type.toLowerCase() == "file") {
1435          batch.fileUpload = true;
1436          batch.map[name] = data;
1437        }
1438        else {
1439          // This check for an HTML is not complete, but is there a better way?
1440          // Maybe we should add: data.hasChildNodes typeof "function" == true
1441          if (data.nodeName && data.nodeType) {
1442            batch.map[name] = dwr.engine.serialize.convertDom(data);
1443          }
1444          else {
1445            batch.map[name] = dwr.engine.serialize.convertObject(batch, directrefmap, otherrefmap, data, name, depth + 1);
1446          }
1447        }
1448        break;
1449      case "function":
1450        // Ignore functions unless they are directly passed in
1451        if (depth === 0) {
1452          batch.map[name] = dwr.engine.serialize.convertByReference(batch, directrefmap, otherrefmap, data, name, depth + 1);
1453        }
1454        break;
1455      default:
1456        dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." });
1457        batch.map[name] = "default:" + data;
1458        break;
1459      }
1460    },
1461
1462    /**
1463     * Marshall an object by reference
1464     * @private
1465     * @see dwr.engine.serialize.convert() for parameter details
1466     */
1467    convertByReference:function(batch, directrefmap, otherrefmap, data, name, depth) {
1468      var funcId = "f" + dwr.engine.serialize.funcId;
1469      dwr.engine.serialize.remoteFunctions[funcId] = data;
1470      dwr.engine.serialize.funcId++;
1471      return "byref:" + funcId;
1472    },
1473
1474    /**
1475     * Marshall an array
1476     * @private
1477     * @see dwr.engine.serialize.convert() for parameter details
1478     */
1479    convertArray:function(batch, directrefmap, otherrefmap, data, name, depth) {
1480      var childName, i;
1481      if (dwr.engine.util.ieCondition("if lte IE 7")) {
1482        // Use array joining on IE1-7 (fastest)
1483        var buf = ["array:["];
1484        for (i = 0; i < data.length; i++) {
1485          if (i !== 0) buf.push(",");
1486          batch.paramCount++;
1487          childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
1488          dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[i], childName, depth + 1);
1489          buf.push("reference:");
1490          buf.push(childName);
1491        }
1492        buf.push("]");
1493        reply = buf.join("");
1494      }
1495      else {
1496        // Use string concat on other browsers (fastest)
1497        var reply = "array:[";
1498        for (i = 0; i < data.length; i++) {
1499          if (i !== 0) reply += ",";
1500          batch.paramCount++;
1501          childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
1502          dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[i], childName, depth + 1);
1503          reply += "reference:";
1504          reply += childName;
1505        }
1506        reply += "]";
1507      }
1508
1509      return reply;
1510    },
1511
1512    /**
1513     * Marshall an object by value
1514     * @private
1515     * @see dwr.engine.serialize.convert() for parameter details
1516     */
1517    convertObject:function(batch, directrefmap, otherrefmap, data, name, depth) {
1518      // treat objects as an associative arrays
1519      var reply = "Object_" + dwr.engine.serialize.getObjectClassName(
1519data).replace(/:/g, "?") + ":{";
1520      var elementset = (data.constructor && data.constructor.$dwrClassMembers ? data.constructor.$dwrClassMembers : data);
1521      var element;
1522      for (element in elementset) {
1523        if (typeof data[element] != "function" && !dwr.engine._excludeObjectAttributes[element]) {
1524          batch.paramCount++;
1525          var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
1526          dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[element], childName, depth + 1);
1527          reply += encodeURIComponent(element) + ":reference:" + childName + ", ";
1528        }
1529      }
1530
1531      if (reply.substring(reply.length - 2) == ", ") {
1532        reply = reply.substring(0, reply.length - 2);
1533      }
1534      reply += "}";
1535
1536      return reply;
1537    },
1538
1539    /**
1540     * Marshall an object
1541     * @private
1542     * @see dwr.engine.serialize.convert() for parameter details
1543     */
1544    convertDom:function(data) {
1545      return "xml:" + encodeURIComponent(dwr.engine.serialize.serializeDom(data));
1546    },
1547    serializeDom:function(data) {
1548      var output;
1549      if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data);
1550      else if (data.xml) output = data.xml;
1551      else throw new Error("The browser doesn't support XML serialization of: " + data);
1552      return output;
1553    },
1554
1555    /**
1556     * Have we already converted this object?
1557     * @private
1558     * @see dwr.engine.serialize.convert() for parameter details
1559     */
1560    lookup:function(directrefmap, otherrefmap, data, name) {
1561      // Look for reference to previous conversion
1562      var ref;
1563      if ("$_dwrConversionRef" in data) {
1564        ref = data.$_dwrConversionRef;
1565        if (ref && directrefmap[ref] != data) ref = null;
1566      }
1567      if (ref == null) {
1568        for(r in otherrefmap) {
1569          if (otherrefmap[r] == data) {
1570            ref = r;
1571            break;
1572          }
1573        }
1574      }
1575      if (ref != null) return "reference:" + ref;
1576      // This was a new conversion - save the reference!
1577      try {
1578        data.$_dwrConversionRef = name;
1579        directrefmap[name] = data;
1580      }
1581      catch(err) {
1582        otherrefmap[name] = data;
1583      }
1584      return null;
1585    },
1586
1587    /**
1588     * Clean up our conversion markers from user data
1589     * @private
1590     */
1591    cleanup:function(directrefmap) {
1592      for(var refName in directrefmap) {
1593        var data = directrefmap[refName];
1594        try {
1595          delete data.$_dwrConversionRef;
1596        }
1597        catch(err) {
1598          data.$_dwrConversionRef = undefined;
1599        }
1600      }
1601    },
1602
1603    /**
1604     * Returns the classname of supplied argument obj. Similar to typeof, but
1605     * which returns the name of the constructor that created the object rather
1606     * than 'object'
1607     * @private
1608     * @param {Object} obj The object to detect the type of
1609     * @return The name of the object
1610     */
1611    getObjectClassName:function(obj) {
1612      // Different handling depending on if, and what type of, class-mapping is used
1613      if (obj.$dwrClassName)
1614        return obj.$dwrClassName; // Light class-mapping uses the classname from a property on the instance
1615      else if (obj.constructor && obj.constructor.$dwrClassName)
1616        return obj.constructor.$dwrClassName; // Full class-mapping uses the classname from a property on the constructor function
1617      else
1618        return "Object";
1619    }
1620  };
1621
1622  /**
1623   * Functions to handle the various remoting transport
1624   */
1625  dwr.engine.transport = {
1626    /**
1627     * Manage the DWR session and then send.
1628     * @private
1629     * @param {Object} batch
1630     */
1631    send:function(batch) {
1632      if (batch.path == null) {
1633        batch.path = dwr.engine._effectivePath();
1634      }
1635      dwr.engine.transport.updateDwrSessionFromCookie();
1636      if (!dwr.engine._dwrSessionId) {
1637        dwr.engine._internalOrdered = true;
1638        var retval;
1639        var idbatch = {
1640          map:{
1641            callCount:1,
1642            'c0-scriptName':'__System',
1643            'c0-methodName':'generateId',
1644            'c0-id':0
1645          },
1646          paramCount:0, isPoll:false, async:batch.async,
1647          headers:{}, preHooks:[],
1648          postHooks:[function() {
1649            dwr.engine._internalOrdered = false;
1650          }],
1651          timeout:dwr.engine._timeout,
1652          errorHandler:batch.errorHandler, globalErrorHandler:batch.globalErrorHandler, warningHandler:batch.warningHandler, textHtmlHandler:batch.textHtmlHandler, globalTextHtmlHandler:batch.globalTextHtmlHandler,
1653          path:batch.path,
1654          handlers:[{
1655            exceptionHandler:null,
1656            callback:function(id) {
1657              dwr.engine.transport.updateDwrSessionFromCookie();
1658              if (!dwr.engine._dwrSessionId) {
1659                dwr.engine.transport.setDwrSession(id);
1660              }
1661              retval = dwr.engine.transport.send2(batch);
1662            }
1663          }]
1664        };
1665        dwr.engine.transport.send2(idbatch);
1666        return retval;
1667      }
1668      else {
1669        return dwr.engine.transport.send2(batch);
1670      }
1671    },
1672
1673    /**
1674     * Actually send the block of data in the batch object.
1675     * @private
1676     * @param {Object} batch
1677     */
1678    send2:function(batch) {
1679      dwr.engine.batch.prepareToSend(batch);
1680
1681      // Work out if we are going cross domain
1682      var isCrossDomain = false;
1683      if (batch.path.indexOf("http://") === 0 || batch.path.indexOf("https://") === 0) {
1684        var dwrShortPath = batch.path.split("/", 3).join("/");
1685        var hrefShortPath = window.location.href.split("/", 3).join("/");
1686        isCrossDomain = (dwrShortPath != hrefShortPath);
1687      }
1688
1689      if (batch.fileUpload) {
1690        if (isCrossDomain) {
1691          throw new Error("Cross domain file uploads are not possible with this release of DWR");
1692        }
1693        batch.transport = dwr.engine.transport.iframe;
1694      }
1695      else if (isCrossDomain) {
1696        batch.transport = dwr.engine.transport.scriptTag;
1697      }
1698      else if (batch.isPoll && dwr.engine._useStreamingPoll == "true" && dwr.engine.util.ieCondition("if (IE 8)|(IE 9)")) {
1699        batch.transport = dwr.engine.transport.iframe;
1700      }
1701      else {
1702        batch.transport = dwr.engine.transport.xhr;
1703      }
1704
1705      return batch.transport.send(batch);
1706    },
1707
1708    /**
1709     * Called to signal that the batch response has been delivered
1710     */
1711    complete:function(batch) {
1712        dwr.engine.batch.validate(batch);
1713        dwr.engine.transport.remove(batch);
1714    },
1715
1716    /**
1717     * Called as a result of a request timeout
1718     * @private
1719     * @param {Object} batch The batch that is aborting
1720     */
1721    abort:function(batch) {
1722      var transport = batch.transport;
1723      if (transport.abort) {
1724        transport.abort(batch);
1725      }
1726      dwr.engine.transport.remove(batch);
1727    },
1728
1729    /**
1730     * Remove all remoting artifacts
1731     * @param {Object} batch The batch that has completed
1732     */
1733    remove:function(batch) {
1734      if (batch.transport) {
1735        dwr.engine._callPostHooks(batch);
1736        batch.transport.remove(batch);
1737        batch.transport = null;
1738      }
1739      dwr.engine.batch.remove(batch);
1740    },
1741
1742    setDwrSession:function(dwrsess) {
1743      dwr.engine._dwrSessionId = dwrsess;
1744      var attrs = "";
1745      if (!dwr.engine._cookieAttributes.match(/^path=/i)) {
1746        attrs = "; path=" + (dwr.engine._effectiveContextPath() || "/");
1747      }
1748      if (dwr.engine._cookieAttributes) {
1749        attrs += "; " + dwr.engine._cookieAttributes;
1750      }
1751      document.cookie = "DWRSESSIONID=" + dwrsess + attrs;
1752      dwr.engine._scriptSessionId = dwrsess + "/" + dwr.engine._pageId;
1753    },
1754
1755    updateDwrSessionFromCookie:function() {
1756      if (!dwr.engine._dwrSessionId) {
1757        var match = document.cookie.match(/(?:^|; )DWRSESSIONID=([^;]+)/);
1758        if (match) {
1759          dwr.engine.transport.setDwrSession(match[1]);
1760        }
1761      }
1762    },
1763
1764    /**
1765     * Remoting through XHR
1766     */
1767    xhr:{
1768      /**
1769       * The default HTTP method to use
1770       */
1771      httpMethod:"POST",
1772
1773      /**
1774       * The ActiveX objects to use when we want to do an XMLHttpRequest call.
1775       * TODO: We arrived at this by trial and error. Other toolkits use
1776       * different strings, maybe there is an officially correct version?
1777       */
1778      XMLHTTP:["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"],
1779
1780      /**
1781       * Setup a batch for transfer through XHR
1782       * @param {Object} batch The batch to alter for XHR transmit
1783       */
1784      send:function(batch) {
1785        if (batch.isPoll) {
1786          batch.map.partialResponse = dwr.engine._partialResponseYes;
1787        }
1788
1789        // Are we told to use early closing mode?
1790        if (batch.isPoll && dwr.engine._useStreamingPoll == "false") {
1791          batch.map.partialResponse = dwr.engine._partialResponseNo;
1792        }
1793        if (batch.isPoll && dwr.engine.util.ieCondition("if lte IE 9")) {
1794          batch.map.partialResponse = dwr.engine._partialResponseNo;
1795        }
1796
1797        if (window.XMLHttpRequest) {
1798          batch.req = new XMLHttpRequest();
1799        }
1800        else if (window.ActiveXObject) {
1801          batch.req = dwr.engine.util.newActiveXObject(dwr.engine.transport.xhr.XMLHTTP);
1802        }
1803
1804        // Proceed using XMLHttpRequest
1805        if (batch.async === true) {
1806          batch.req.onreadystatechange = function() {
1807            if (typeof dwr != 'undefined') {
1808              dwr.engine.transport.xhr.stateChange(batch);
1809            }
1810          };
1811        }
1812
1813        httpMethod = dwr.engine.transport.xhr.httpMethod;
1814
1815        batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
1816        var request = dwr.engine.batch.constructRequest(batch, httpMethod);
1817
1818        try {
1819          batch.req.open(httpMethod, request.url, batch.async);
1820          try {
1821            for (var prop in batch.headers) {
1822              var value = batch.headers[prop];
1823              if (typeof value == "string") {
1824                batch.req.setRequestHeader(prop, value);
1825              }
1826            }
1827            if (!batch.headers["Content-Type"]) {
1828              batch.req.setRequestHeader("Content-Type", "text/plain");
1829            }
1830          }
1831          catch (ex) {
1832            dwr.engine._handleWarning(batch, ex);
1833          }
1834
1835          batch.req.send(request.body);
1836          if (batch.async === false) {
1837            dwr.engine.transport.xhr.stateChange(batch);
1838          }
1839        }
1840        catch (ex2) {
1841          dwr.engine._handleError(batch, ex2);
1842        }
1843
1844        if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
1845          dwr.engine.transport.xhr.checkCometPoll();
1846        }
1847
1848        // This is only of any use in sync mode to return the reply data
1849        return batch.reply;
1850      },
1851
1852      /**
1853       * Called by XMLHttpRequest to indicate that something has happened
1854       * @private
1855       * @param {Object} batch The current remote operation
1856       */
1857      stateChange:function(batch) {
1858        var toEval;
1859
1860        if (batch.aborted) return;
1861        if (batch.completed) {
1862          if (batch.transport) dwr.engine._debug("Error: _stateChange() with batch.completed");
1863          return;
1864        }
1865
1866        // Try to get the response HTTP status if applicable
1867        var req = batch.req;
1868        var status = 0;
1869        try {
1870          if (req.readyState >= 2) {
1871            status = req.status; // causes Mozilla to except on page moves
1872          }
1873        }
1874        catch(ignore) {}
1875
1876        // If we couldn't get the status we bail out, unless the request is
1877        // complete, which means error (handled further below)
1878        if (status === 0 && req.readyState < 4) {
1879          return;
1880        }
1881
1882        // If the status is 200, we are now online.
1883        // Future improvement per Mike W. - A solution where we only use the callbacks/handlers of the poll call to trigger
1884        // the retry handling would be ideal.  We would need something like a new internal callback that reports
1885        // progress back to the caller, and the design should be compatible with getting it to work with iframes as well.
1886        if (status === 200) {
1887          dwr.engine._handlePollStatusChange(true, null, batch);
1888        }
1889
1890        // The rest of this function only deals with request completion
1891        if (req.readyState != 4) {
1892          return;
1893        }
1894
1895        if (dwr.engine._unloading) {
1896          dwr.engine._debug("Ignoring reply from server as page is unloading.");
1897          return;
1898        }
1899
1900        try {
1901          var reply = req.responseText;
1902          reply = dwr.engine._replyRewriteHandler(reply);
1903          var contentType = req.getResponseHeader("Content-Type");
1904          if (status >= 200 && status < 300) {
1905            if (contentType.indexOf("text/plain") < 0 && contentType.indexOf("text/javascript") < 0) {
1906              if (contentType.indexOf("text/html") == 0) {
1907                dwr.engine._handleError(batch, { name:"dwr.engine.textHtmlReply", message:"HTML reply from the server.", responseText:reply||"" });
1908              } else {
1909                dwr.engine._handleError(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" });
1910              }
1911            } else if (reply == null || reply === "") {
1912              dwr.engine._handleError(batch, { name:"dwr.engine.missingData", message:"No data received from server" });
1913            } else {
1914              if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
1915                dwr.engine.transport.xhr.processCometResponse(reply, batch);
1916              } else {
1917                if (reply.search("//#DWR") === -1) {
1918                  dwr.engine._handleError(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" });
1919                } else {
1920                  toEval = reply;
1921                }
1922              }
1923            }
1924          } else if (status >= 400) {
1925            var statusText = "statusText could not be read.";
1926            try {
1927              statusText = req.statusText;
1928            } catch (ex) {
1929             // Eat this, if the server just went down an exception can occur reading statusText.
1930            }
1931            dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:statusText });
1932          }
1933
1934          if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, "");
1935          dwr.engine._executeScript(toEval);
1936          dwr.engine.transport.complete(batch);
1937
1938        } catch (ex2) {
1939          dwr.engine._handleError(batch, ex2);
1940        }
1941      },
1942
1943      /**
1944       * Check for reverse Ajax activity
1945       * @private
1946       */
1947      checkCometPoll:function() {
1948        var req = dwr.engine._pollBatch && dwr.engine._pollBatch.req;
1949        if (req) {
1950          var text = req.responseText;
1951          if (text != null) {
1952            dwr.engine.transport.xhr.processCometResponse(text, dwr.engine._pollBatch);
1953          }
1954        }
1955        // If the poll resources are still there, come back again
1956        if (dwr.engine._pollBatch) {
1957          setTimeout(dwr.engine.transport.xhr.checkCometPoll, dwr.engine._pollCometInterval);
1958        }
1959      },
1960
1961      /**
1962       * Some more text might have come in, test and execute the new stuff.
1963       * This method could also be called by the iframe transport
1964       * @private
1965       * @param {Object} response from xhr.responseText
1966       * @param {Object} batch The batch that the XHR object pertains to
1967       */
1968      processCometResponse:function(response, batch) {
1969        if (batch.charsProcessed == response.length) return;
1970        if (response.length === 0) {
1971          batch.charsProcessed = 0;
1972          return;
1973        }
1974
1975        var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed);
1976        if (firstStartTag == -1) {
1977          // dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'");
1978          return;
1979        }
1980        // if (firstStartTag > 0) {
1981        //   dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'");
1982        // }
1983
1984        var lastEndTag = response.lastIndexOf("//#DWR-END#");
1985        if (lastEndTag == -1) {
1986          // dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed);
1987          return;
1988        }
1989
1990        // Skip the end tag too for next time, remembering CR and LF
1991        if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) {
1992          batch.charsProcessed = lastEndTag + 13;
1993        }
1994        else {
1995          batch.charsProcessed = lastEndTag + 11;
1996        }
1997
1998        var exec = response.substring(firstStartTag + 13, lastEndTag);
1999
2000        try {
2001          dwr.engine._executeScript(exec);
2002        }
2003        catch (ex) {
2004          // This is one of these annoying points where we might be executing
2005          // while the window is being destroyed. If dwr == null, bail out.
2006          if (dwr != null) {
2007            dwr.engine._handleError(batch, ex);
2008          }
2009        }
2010      },
2011
2012      /**
2013       * Aborts ongoing request (for timeouts etc)
2014       */
2015      abort:function(batch) {
2016        if (batch.req) {
2017          batch.aborted = true;
2018          batch.req.abort();
2019        }
2020      },
2021
2022      /**
2023       * Tidy-up when an XHR call is done
2024       * @param {Object} batch
2025       */
2026      remove:function(batch) {
2027        // XHR tidyup: avoid IE handles increase
2028        if (batch.req) {
2029          delete batch.req;
2030        }
2031      }
2032    },
2033
2034    /**
2035     * Functions for remoting through IFrame
2036     */
2037    iframe:{
2038      /**
2039       * The default HTTP method to use
2040       */
2041      httpMethod:"POST",
2042
2043      /**
2044       * Setup a batch for transfer through IFrame
2045       * @param {Object} batch The batch to alter for IFrame transmit
2046       */
2047      send:function(batch) {
2048        if (document.body == null) {
2049          setTimeout(function(){dwr.engine.transport.iframe.send(batch);}, 100);
2050          return;
2051        }
2052        batch.httpMethod = dwr.engine.transport.iframe.httpMethod;
2053        if (batch.fileUpload) {
2054          batch.httpMethod = "POST";
2055          batch.encType = "multipart/form-data";
2056        }
2057        batch.loadingStarted = false;
2058        if (window.location.hostname != document.domain) batch.map.documentDomain = document.domain;
2059        var idname = dwr.engine.transport.iframe.getId(batch);
2060        batch.div1 = document.createElement("div");
2061        batch.div1.innerHTML = "<iframe frameborder='0' style='width:0px;height:0px;border:0;display:none;' id='" + idname + "' name='" + idname + "'></iframe>";
2062        batch.iframe = batch.div1.firstChild;
2063        batch.document = document;
2064        batch.iframe.batch = batch;
2065        batch.fileInputs = [];
2066        dwr.engine.util.addEventListener(batch.iframe, "load", function(ev) {
2067          // Bail out if this is a premature load event (IE) 
2068          if (!batch.loadingStarted) return;
2069          // Put back file inputs
2070          while(batch.fileInputs.length > 0) {
2071            var entry = batch.fileInputs.pop();
2072            entry.original.setAttribute("id", entry.clone.getAttribute("id"));
2073            entry.original.setAttribute("name", entry.clone.getAttribute("name"));
2074            entry.original.style.display = entry.clone.style.display;
2075            entry.clone.parentNode.replaceChild(entry.original, entry.clone);
2076          }
2077          // Bail out if the batch was completed the normal way 
2078          if (batch.completed) return;
2079          // Check for a queued batch exception
2080          if (dwr.engine._queuedBatchException) {
2081            dwr.engine._handleError(batch, dwr.engine._queuedBatchException);
2082            dwr.engine._queuedBatchException = null;
2083            return;
2084          }
2085          // The batch hasn't been completed the normal way, trigger a textHtmlReply error
2086          try {
2087            var contentDoc = batch.iframe.contentDocument || batch.iframe.contentWindow.document;
2088          } catch(ex) {
2089            var contentEx = ex; // may be caused by a HTML error page reply when document.domain is used
2090          }
2091          if (typeof dwr != "undefined") {
2092            var htmlResponse = contentDoc && contentDoc.documentElement ? contentDoc.documentElement.outerHTML : "(Could not extract HTML response: " + contentEx.message + ")";
2093            dwr.engine._handleError(batch, {name:"dwr.engine.textHtmlReply", message:"HTML reply from the server.", responseText:htmlResponse});
2094          }
2095        });
2096        // This needs to be after the event listener is added, for IE.
2097        // http://stackoverflow.com/questions/18414964/load-event-for-iframe-does-not-fire-in-ie
2098        document.body.appendChild(batch.div1);
2099        dwr.engine.transport.iframe.beginLoader(batch, idname);
2100      },
2101
2102      /**
2103       * Create a unique ID so multiple iframes can fire at the same time
2104       * @param {Object} batch A source of a unique number for the batch
2105       * @return {String} a name prefix for created elements
2106       */
2107      getId:function(batch) {
2108        return batch.isPoll ? "dwr-if-poll-" + dwr.engine._instanceId + "-" + batch.map.batchId : "dwr-if-" + dwr.engine._instanceId + "-" + batch.map.batchId;
2109      },
2110
2111      /**
2112       * Setup a form or construct a src attribute to use the iframe.
2113       * @param {Object} batch
2114       */
2115      beginLoader:function(batch, idname) {
2116        if (batch.isPoll) {
2117          batch.map.partialResponse = dwr.engine._partialResponseYes;
2118        }
2119        batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall;
2120        var request = dwr.engine.batch.constructRequest(batch, batch.httpMethod);
2121        if (batch.httpMethod === "GET") {
2122          batch.iframe.setAttribute("src", request.url);
2123        }
2124        else {
2125          // TODO: On firefox we can now get the values of file fields, maybe we should use this
2126          // See http://soakedandsoaped.com/articles/read/firefox-3-native-ajax-file-upload
2127          // setting enctype via the DOM does not work in IE, create the form using innerHTML instead
2128          batch.div2 = document.createElement("div");
2129          document.body.appendChild(batch.div2);
2130          batch.div2.innerHTML = "<form" + (batch.encType ? " encType='" + batch.encType + "' encoding='" + batch.encType + "'" : "") + "></form>";
2131          batch.form = batch.div2.firstChild;
2132          batch.form.setAttribute("action", request.url);
2133          batch.form.setAttribute("target", idname);
2134          batch.form.setAttribute("style", "display:none");
2135          batch.form.setAttribute("method", batch.httpMethod);
2136          for (var prop in batch.map) {
2137            var value = batch.map[prop];
2138            if (typeof value != "function") {
2139              if (value && value.tagName && value.tagName.toLowerCase() == "input" && value.type && value.type.toLowerCase() == "file") {
2140                // Since we can not set the value of a file object, we must post the actual file object
2141                // that the user clicked browse on. We will put a clone in it's place that we will restore later.
2142                // (note that the clone doesn't retain the selected file in most browsers)
2143                if (value.parentNode) {
2144                  var clone = value.cloneNode(true);
2145                  value.parentNode.replaceChild(clone, value);
2146                  batch.fileInputs.push({original:value, clone:clone});
2147                }
2148                value.removeAttribute("id");
2149                value.setAttribute("name", prop);
2150                value.style.display = "none";
2151                batch.form.appendChild(value);
2152              } else {
2153                var formInput = batch.document.createElement("input");
2154                formInput.setAttribute("type", "hidden");
2155                formInput.setAttribute("name", prop);
2156                formInput.setAttribute("value", value);
2157                batch.form.appendChild(formInput);
2158              }
2159            }
2160          }
2161          batch.loadingStarted = true;
2162          batch.form.submit();
2163        }
2164      },
2165
2166      /**
2167       * Functions designed to be called by the server
2168       */
2169      remote:{
2170        /**
2171         * Called by the server: An IFrame reply is about to start
2172         * @private
2173         * @param {Object} iframe
2174         * @param {int} batchId
2175         */
2176        beginIFrameResponse:function(iframe, batchId) {
2177        },
2178        
2179        /**
2180         * Called by the server: An IFrame script tag has completed
2181         * @private
2182         * @param {Object} iframe
2183         */
2184        endChunk:function(iframeWindow) {
2185          setTimeout(function() {
2186            // Delete the first script tag in the iframe
2187            var scriptTags = iframeWindow.document.body.getElementsByTagName("script");
2188            if (scriptTags.length > 0) {
2189              var s = scriptTags[0];
2190              s.parentNode.removeChild(s);
2191            }
2192          }, 0);
2193        },
2194
2195        /**
2196         * Called by the server: An IFrame reply is just completing
2197         * @private
2198         * @param {int} batchId
2199         */
2200        endIFrameResponse:function(batchId) {
2201          var batch = dwr.engine._batches[batchId];
2202          if (batch) dwr.engine.transport.complete(batch);
2203        }
2204      },
2205
2206      remove:function(batch) {
2207        // Safari 3 and Chrome 1 will show endless loading spinner if removing
2208        // iframe during execution of iframe script, so we delay it a bit
2209        setTimeout(function(){
2210          if (batch.iframe && batch.iframe.parentNode) {
2211            batch.iframe.parentNode.removeChild(batch.iframe);
2212            batch.iframe = null;
2213          }
2214          if (batch.div1 && batch.div1.parentNode) {
2215            batch.div1.parentNode.removeChild(batch.div1);
2216            batch.div1 = null;
2217          }
2218          if (batch.form && batch.form.parentNode) {
2219            batch.form.parentNode.removeChild(batch.form);
2220            batch.form = null;
2221          }
2222          if (batch.div2 && batch.div2.parentNode) {
2223            batch.div2.parentNode.removeChild(batch.div2);
2224            batch.div2 = null;
2225          }
2226        }, 100);
2227      }
2228    },
2229
2230    /**
2231     * Functions for remoting through Script Tags
2232     */
2233    scriptTag:{
2234      /**
2235       * Setup a batch for transfer through a script tag
2236       * @param {Object} batch The batch to alter for script tag transmit
2237       */
2238      send:function(batch) {
2239        if (batch.isPoll) {
2240          batch.map.partialResponse = dwr.engine._partialResponseNo;
2241        }
2242        batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
2243        var request = dwr.engine.batch.constructRequest(batch, "GET");
2244        // The best option is DOM manipulation
2245        batch.script = document.createElement("script");
2246        batch.script.id = "dwr-st-" + batch.map.batchId;
2247        batch.script.src = request.url;
2248        batch.script.type = "text/javascript";
2249        batch.script.async = true;
2250        dwr.engine.util.addEventListener(batch.script, "load", function(ev) {
2251          if (typeof dwr != "undefined") dwr.engine.transport.scriptTag.complete(batch);
2252        });
2253        dwr.engine.util.addEventListener(batch.script, "error", function(ev) {
2254          if (typeof dwr != "undefined") dwr.engine.transport.scriptTag.complete(batch);
2255        });
2256        dwr.engine.util.addEventListener(batch.script, "readystatechange", function(ev) {
2257          if (typeof dwr != "undefined") {
2258            if (batch.completed) return;
2259            if (batch.script.readyState == "complete" || batch.script.readyState == "loaded") {
2260              dwr.engine.transport.scriptTag.complete(batch);
2261            }
2262          }
2263        });
2264        document.getElementsByTagName("head")[0].appendChild(batch.script);
2265      },
2266
2267      /**
2268       * Notified when the script tag has been loaded and executed
2269       */
2270      complete:function(batch) {
2271        dwr.engine.transport.complete(batch);
2272      },
2273
2274      /**
2275       * Cleanup script tag
2276       */
2277      remove:function(batch) {
2278        // Bail out if we were already called
2279        if (!batch.script) return;
2280
2281        // Cleanup script tag
2282        batch.script.parentNode.removeChild(batch.script);
2283        batch.script = null;
2284      }
2285    }
2286  };
2287
2288  /**
2289   * Functions to manipulate batches
2290   * @private
2291   */
2292  dwr.engine.batch = {
2293    /**
2294     * Generate a new standard batch
2295     * @private
2296     */
2297    create:function() {
2298      var batch = {
2299        async:dwr.engine._async,
2300        charsProcessed:0,
2301        handlers:[],
2302        isPoll:false,
2303        map:{ callCount:0 },
2304        paramCount:0,
2305        preHooks:[],
2306        postHooks:[],
2307        timeout:dwr.engine._timeout,
2308        errorHandler:null,
2309        globalErrorHandler:dwr.engine._errorHandler,
2310        warningHandler:dwr.engine._warningHandler,
2311        textHtmlHandler:null,
2312        globalTextHtmlHandler:dwr.engine._textHtmlHandler
2313      };
2314      if (!dwr.engine._activeReverseAjax) batch.map.nextReverseAjaxIndex = dwr.engine._nextReverseAjaxIndex;
2315      
2316      if (dwr.engine._preHook) {
2317        batch.preHooks.push(dwr.engine._preHook);
2318      }
2319      if (dwr.engine._postHook) {
2320        batch.postHooks.push(dwr.engine._postHook);
2321      }
2322
2323      dwr.engine.batch.populateHeadersAndAttributes(batch);
2324      return batch;
2325    },
2326
2327    /**
2328     * Generate a new batch for polling
2329     * @private
2330     * @see dwr.engine.batch.create()
2331     */
2332    createPoll:function() {
2333      var batch = {
2334        async:true,
2335        charsProcessed:0,
2336        handlers:[{
2337          callback:function(pause) {
2338            dwr.engine._pollBatch = null;
2339            setTimeout(dwr.engine._poll, pause);
2340          }
2341        }],
2342        isPoll:true,
2343        map:{ callCount:1, nextReverseAjaxIndex:dwr.engine._nextReverseAjaxIndex},
2344        paramCount:0,
2345        preHooks:[],
2346        postHooks:[],
2347        timeout:dwr.engine._pollTimeout
2348      };
2349      dwr.engine.batch.populateHeadersAndAttributes(batch);
2350      return batch;
2351    },
2352
2353    /**
2354     * Copy the global headers and parameters into this batch object
2355     * @private
2356     * @param {Object} batch The destination
2357     */
2358    populateHeadersAndAttributes:function(batch) {
2359      var propname, data;
2360      batch.headers = {};
2361      if (dwr.engine._headers) {
2362        for (propname in dwr.engine._headers) {
2363          data = dwr.engine._headers[propname];
2364          if (typeof data != "function") batch.headers[propname] = data;
2365        }
2366      }
2367      batch.attributes = {}; // props to add as request attributes
2368      if (dwr.engine._attributes) {
2369        for (propname in dwr.engine._attributes) {
2370          data = dwr.engine._attributes[propname];
2371          if (typeof data != "function") batch.attributes[propname] = data;
2372        }
2373      }
2374    },
2375
2376    /**
2377     * Augment this batch with a new call
2378     * @private
2379     */
2380    addCall:function(batch, scriptName, methodName, args) {
2381      // From the other params, work out which is the function (or object with
2382      // call meta-data) and which is the call parameters
2383      var callData, stopAt;
2384      var lastArg = args[args.length - 1];
2385      if (lastArg == null || typeof lastArg == "function") {
2386        callData = { callback:lastArg };
2387        stopAt = args.length - 1;
2388      }
2389      else if (dwr.engine.util.isCallOptionArgument(lastArg)) {
2390        callData = lastArg;
2391        stopAt = args.length - 1;
2392      }
2393      else {
2394        callData = {};
2395        stopAt = args.length;
2396      }
2397
2398      // Merge from the callData into the batch
2399      if (dwr.engine._singleShot) dwr.engine.batch.merge(batch, callData);
2400      batch.handlers[batch.map.callCount] = {
2401        callback:callData.callbackHandler || callData.callback,
2402        callbackArg:callData.callbackArg || callData.arg,
2403        callbackScope:callData.callbackScope || callData.scope || window,
2404        exceptionHandler:callData.exceptionHandler,
2405        exceptionArg:callData.exceptionArg || callData.arg,
2406        exceptionScope:callData.exceptionScope || callData.scope || window,
2407        errorHandler:callData.errorHandler
2408      };
2409
2410      // Copy to the map the things that need serializing
2411      var prefix = "c" + batch.map.callCount + "-";
2412      batch.map[prefix + "scriptName"] = scriptName;
2413      batch.map[prefix + "methodName"] = methodName;
2414      batch.map[prefix + "id"] = batch.map.callCount;
2415      var directrefmap = {}, otherrefmap = {};
2416      for (var i = 0; i < stopAt; i++) {
2417        dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, args[i], prefix + "param" + i, 0);
2418      }
2419      dwr.engine.serialize.cleanup(directrefmap);
2420    },
2421
2422    /**
2423     * Take further options and merge them into a batch
2424     * @private
2425     * @param {Object} batch The batch that we are altering
2426     * @param {Object}
2426 overrides The object containing properties to copy into batch
2427     */
2428    merge:function(batch, overrides) {
2429      var propname, data;
2430      for (var i = 0; i < dwr.engine._propnames.length; i++) {
2431        propname = dwr.engine._propnames[i];
2432        if (dwr.engine._singleShot && propname == "errorHandler") continue;
2433        if (overrides[propname] != null) batch[propname] = overrides[propname];
2434      }
2435      if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook);
2436      if (overrides.postHook != null) batch.postHooks.push(overrides.postHook);
2437      if (overrides.headers) {
2438        for (propname in overrides.headers) {
2439          data = overrides.headers[propname];
2440          if (typeof data != "function") batch.headers[propname] = data;
2441        }
2442      }
2443      var attrs = null;
2444      if (overrides.attributes) attrs = overrides.attributes;
2445      if (attrs) {
2446        for (propname in attrs) {
2447          data = attrs[propname];
2448          if (typeof data != "function") batch.attributes[propname] = data;
2449        }
2450      }
2451    },
2452
2453    /**
2454     * Executed just before a transport sends the batch
2455     * @private
2456     * @param {Object} batch The batch to prepare for sending
2457     */
2458    prepareToSend:function(batch) {
2459      batch.map.batchId = dwr.engine._nextBatchId;
2460      dwr.engine._nextBatchId++;
2461      dwr.engine._batches[batch.map.batchId] = batch;
2462      dwr.engine._batchesLength++;
2463      batch.completed = false;
2464      batch.map.instanceId = dwr.engine._instanceId;
2465
2466      // security details are filled in late so previous batches have completed
2467      batch.map.page = encodeURIComponent(window.location.pathname + window.location.search);
2468      batch.map.scriptSessionId = dwr.engine._scriptSessionId;
2469
2470      for (var i = 0; i < batch.preHooks.length; i++) {
2471        batch.preHooks[i]();
2472      }
2473      batch.preHooks = null;
2474      // Set a timeout
2475      if (batch.timeout && batch.timeout !== 0) {
2476        batch.timeoutId = setTimeout(function() { 
2477          dwr.engine.transport.abort(batch); 
2478          dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" });
2479        }, batch.timeout);
2480      }
2481    },
2482
2483    /**
2484     * Work out what the URL should look like
2485     * @private
2486     * @param {Object} batch the data that we are sending
2487     * @param {Object} httpMethod Are we using GET/POST etc?
2488     */
2489    constructRequest:function(batch, httpMethod) {
2490      // A quick string to help people that use web log analysers
2491      var urlBuffer = [];
2492      urlBuffer.push(batch.path);
2493      urlBuffer.push(batch.mode);
2494      if (batch.isPoll) {
2495        urlBuffer.push("ReverseAjax.dwr");
2496      }
2497      else if (batch.map.callCount == 1) {
2498        urlBuffer.push(batch.map["c0-scriptName"]);
2499        urlBuffer.push(".");
2500        urlBuffer.push(batch.map["c0-methodName"]);
2501        urlBuffer.push(".dwr");
2502      }
2503      else {
2504        urlBuffer.push("Multiple.");
2505        urlBuffer.push(batch.map.callCount);
2506        urlBuffer.push(".dwr");
2507      }
2508      // Play nice with url re-writing
2509      var sessionMatchExpr = new RegExp(
2510        "^" +            // start of string
2511        "[^;\\?#]+" +    // protocol, host, and path (up to first of ; ? or #)
2512        "(;[^\\?#]+)");  // group 1: ; and sessionid (up to first of ? or #)
2513      var sessionMatch = location.href.match(sessionMatchExpr);
2514      if (sessionMatch != null) {
2515        urlBuffer.push(sessionMatch[1]);
2516      }
2517
2518      // Add attributes to the output map
2519      if (batch.attributes) {
2520        for (var attrname in batch.attributes) {
2521          var data = batch.attributes[attrname];
2522          if (typeof data != "function") batch.map["a-" + attrname] = "" + data;
2523        }
2524      }
2525
2526      var request = {};
2527      var prop;
2528      if (httpMethod == "GET") {
2529        // Some browsers (Opera/Safari2) seem to fail to convert the callCount value
2530        // to a string in the loop below so we do it manually here.
2531        batch.map.callCount = "" + batch.map.callCount;
2532        urlBuffer.push("?");
2533        for (prop in batch.map) {
2534          if (typeof batch.map[prop] != "function") {
2535            urlBuffer.push(encodeURIComponent(prop));
2536            urlBuffer.push("=");
2537            urlBuffer.push(encodeURIComponent(batch.map[prop]));
2538            urlBuffer.push("&");
2539          }
2540        }
2541        urlBuffer.pop(); // remove the trailing &
2542        request.body = null;
2543      }
2544      else {
2545        // PERFORMANCE: for iframe mode this is thrown away.
2546        request.body = "";
2547        if (dwr.engine.util.ieCondition("if lte IE 7")) {
2548          // Use array joining on IE1-7 (fastest)
2549          var buf = [];
2550          for (prop in batch.map) {
2551            if (typeof batch.map[prop] != "function") {
2552              buf.push(prop + "=" + batch.map[prop] + dwr.engine._postSeperator);
2553            }
2554          }
2555          request.body = buf.join("");
2556        }
2557        else {
2558          // Use string concat on other browsers (fastest)
2559          for (prop in batch.map) {
2560            if (typeof batch.map[prop] != "function") {
2561              request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator;
2562            }
2563          }
2564        }
2565        request.body = dwr.engine._contentRewriteHandler(request.body);
2566      }
2567      request.url = dwr.engine._urlRewriteHandler(urlBuffer.join(""));
2568      return request;
2569    },
2570
2571    /**
2572     * @private This function is invoked when a batch reply is received.
2573     * It checks that there is a response for every call in the batch. Otherwise,
2574     * an error will be signaled (a call without a response indicates that the
2575     * server failed to send complete batch response).
2576     */
2577    validate:function(batch) {
2578      if (!batch.completed) {
2579        var repliesReceived = 0;
2580        for (var i = 0; i < batch.map.callCount; i++) {
2581          if (batch.handlers[i].completed === true) {
2582            repliesReceived++;
2583          }
2584        }
2585        if (repliesReceived == 0 && dwr.engine._queuedBatchException) {
2586          dwr.engine._handleError(batch, dwr.engine._queuedBatchException);
2587          dwr.engine._queuedBatchException = null;
2588        } else if (repliesReceived < batch.map.callCount) {
2589          dwr.engine._handleError(batch, { name:"dwr.engine.incompleteReply", message:"Incomplete reply from server" });
2590        }
2591      }
2592    },
2593
2594    /**
2595     * A call has finished by whatever means and we need to shut it all down.
2596     * @private
2597     * @param {Object} batch The batch that we are altering
2598     */
2599    remove:function(batch) {
2600      if (!batch) {
2601        dwr.engine._debug("Warning: null batch in dwr.engine.batch.remove()", true);
2602        return;
2603      }
2604
2605      if (batch.completed) {
2606        return;
2607      }
2608      batch.completed = true;
2609
2610      // Transport tidyup
2611      dwr.engine.transport.remove(batch);
2612
2613      // Timeout tidyup
2614      if (batch.timeoutId != null) {
2615        clearTimeout(batch.timeoutId);
2616        delete batch.timeoutId;
2617      }
2618
2619      // TODO: co-locate all the functions that work on a set of batches
2620      if (batch.map && (batch.map.batchId != null)) {
2621        delete dwr.engine._batches[batch.map.batchId];
2622        dwr.engine._batchesLength--;
2623      }
2624      if (batch == dwr.engine._batch) dwr.engine._batch = null;
2625      if (batch == dwr.engine._pollBatch) dwr.engine._pollBatch = null;
2626
2627      // If there is anything on the queue waiting to go out, then send it.
2628      // We don't need to check for ordered mode, here because when ordered mode
2629      // gets turned off, we still process *waiting* batches in an ordered way.
2630      if (dwr.engine._batchQueue.length !== 0) {
2631        var sendbatch = dwr.engine._batchQueue.shift();
2632        dwr.engine.transport.send(sendbatch);
2633      }
2634    }
2635  };
2636
2637  /**
2638   * Various utility functions
2639   * @private
2640   */
2641  dwr.engine.util = {
2642    /**
2643     * Create one of a number of ActiveX strings
2644     * @param {Object} axarray An array of strings to attempt to create ActiveX objects from
2645     */
2646    newActiveXObject:function(axarray) {
2647      var returnValue;
2648      for (var i = 0; i < axarray.length; i++) {
2649        try {
2650          returnValue = new ActiveXObject(axarray[i]);
2651          break;
2652        }
2653        catch (ex) { /* ignore */ }
2654      }
2655      return returnValue;
2656    },
2657
2658    ieCondition:function(cond) {
2659      if (!(cond in dwr.engine._ieConditions)) {
2660        var div = document.createElement("div");
2661        div.innerHTML = "<!--[" + cond + "]><p><![endif]-->";
2662        dwr.engine._ieConditions[cond] = !!(div.getElementsByTagName("p").length);
2663      }
2664      return dwr.engine._ieConditions[cond];
2665    },
2666    
2667    /**
2668     * Transform a number into a token string suitable for ids
2669     */
2670    tokenify: function(number) {
2671      var tokenbuf = [];
2672      var charmap = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*$";
2673      var remainder = number;
2674      while (remainder > 0)
2675      {
2676        tokenbuf.push(charmap.charAt(remainder & 0x3F));
2677        remainder = Math.floor(remainder / 64); // Can't use shift operator due to 32-bit limit in JS
2678      }
2679      return tokenbuf.join("");
2680    },
2681
2682    addEventListener: function(elem, name, func) {
2683      if (elem.addEventListener)
2684        elem.addEventListener(name, func, false);
2685      else
2686        elem.attachEvent("on" + name, func);
2687    },
2688
2689    isCallOptionArgument: function(lastArg) {
2690      return (typeof lastArg === "object" && (typeof lastArg.callback === "function" ||
2691        typeof lastArg.exceptionHandler === "function" || typeof lastArg.callbackHandler === "function" ||
2692        typeof lastArg.errorHandler === "function" || typeof lastArg.warningHandler === "function" || lastArg.hasOwnProperty("async")));
2693    },
2694
2695    logHandlerEx: function(func) {
2696      try {
2697        func();
2698      } catch(ex) {
2699        dwr.engine._debug("Exception occured in user-specified handler:");
2700        dwr.engine._debug(ex);
2701      }
2702    }
2703  };
2704
2705  // Initialize DWR.
2706  dwr.engine._initializer.init();
2707
2708  /**
2709   * Routines for the DWR pubsub hub
2710   */
2711  dwr.hub = {
2712    /**
2713     * Publish some data to a given topic
2714     * @param {Object} topicName The topic to publish to
2715     * @param {Object} data The data to publish
2716     */
2717    publish:function(topicName, data) {
2718      dwr.engine._execute(null, '__System', 'publish', topicName, data, {});
2719    },
2720
2721    /**
2722     * Subscribe to get notifications of publish events to a given topic
2723     * @param {String} topicName The topic to subscribe to
2724     * @param {Function} callback The function to call when a publish happens
2725     * @param {Object} scope The 'this' object on which the callback executes (optional)
2726     * @param {Object} subscriberData Data that the subscriber wishes to remember (optional)
2727     * @return An opaque type for use with unsubscribe
2728     */
2729    subscribe:function(topicName, callback, scope, subscriberData) {
2730      var subscription = "" + dwr.hub._subscriptionId;
2731      dwr.hub._subscriptionId++;
2732      dwr.hub._subscriptions[subscription] = {
2733        callback:callback,
2734        scope:scope,
2735        subscriberData:subscriberData
2736      };
2737      dwr.engine._execute(null, '__System', 'subscribe', topicName, subscription, {});
2738      return subscription;
2739    },
2740
2741    /**
2742     * Called by the server: A publish event has happened that we care about
2743     * @private
2744     * @param {Object} subscriptionId
2745     * @param {Object} publishData
2746     */
2747    _remotePublish:function(subscriptionId, publishData) {
2748      var subscriptionData = dwr.hub._subscriptions[subscriptionId];
2749      if (!subscriptionData) return;
2750      subscriptionData.callback.call(subscriptionData.scope, publishData, subscriptionData.subscriberData);
2751    },
2752
2753    /**
2754     * Each time we subscribe to something, we use a unique number
2755     */
2756    _subscriptionId:0,
2757
2758    /**
2759     * We need to remember what we are subscribed to so we can recall the callback
2760     */
2761    _subscriptions:{}
2762  };
2763
2764/**
2765 * High level data-sync API for use by Widget libraries like a Dojo-Data-Store.
2766 * For full documentation see org.directwebremoting.export.Data
2767 */
2768dwr.data = {
2769
2770  /**
2771   * This is just documentation that defines how the listener parameter must act
2772   * in order to receive asynchronous updates
2773   */
2774  StoreChangeListener:{
2775    /**
2776     * Something has removed an item from the store
2777     * @param {StoreProvider} source The store from which it was moved
2778     * @param {string} itemId The ID of the item
2779     */
2780    itemRemoved:function(source, itemId) { },
2781
2782    /**
2783     * Something has added an item to the store
2784     * @param {StoreProvider} source The store from which it was moved
2785     * @param {Item} item The thing that has changed
2786     */
2787    itemAdded:function(source, item) { },
2788
2789    /**
2790     * Something has updated an item in the store
2791     * @param {StoreProvider} source The store from which it was moved
2792     * @param {Item} item The thing that has changed
2793     * @param {string[]} changedAttributes A list of changed attributes. If null then
2794     * you should assume that everything has changed
2795     */
2796    itemChanged:function(source, item, changedAttributes) { }
2797  },
2798
2799  /**
2800   * Create a cache object containing the functions to interact with a server
2801   * side StoreProvider
2802   * @param {string} storeId ID of server provided storage
2803   * @param {dwr.data.StoreChangeListener} listener See server documentation
2804   * This is likely to be true if dwr.engine.activeReverseAjax == true
2805   */
2806  Cache:function(storeId, listener) {
2807    this.storeId = storeId;
2808    this.listener = listener;
2809  }
2810};
2811
2812  /**
2813   * Notes that there is a region of a page that wishes to subscribe to server
2814   * side data and registers a callback function to receive the data.
2815   * @param {Object} region filtering and sorting options. Includes:
2816   * - start: The beginning of the region of specific interest
2817   * - count: The number of items being viewed
2818   * - sort: The sort criteria
2819   * - query: The filter criteria
2820   * @param {function|object} callbackObj A standard DWR callback object
2821   * @return
2822   */
2823  dwr.data.Cache.prototype.viewRegion = function(region, callbackObj) {
2824    if (!region) region = { };
2825    if (!region.start) region.start = 0;
2826    if (!region.count) region.count = -1;
2827    if (!region.sort) region.sort = [];
2828    else {
2829      for (var index = 0; index < region.sort.length; index++) {
2830        if (typeof region.sort[index].descending == "undefined") {
2831          region.sort[index].descending = false;
2832        }
2833      }
2834    }
2835    if (!region.query) region.query = {};
2836
2837    return dwr.engine._execute(null, '__Data', 'viewRegion', [ this.storeId, region, this.listener, callbackObj ]);
2838  };
2839
2840  /**
2841   * As dwr.data.Cache.viewRegion() except that we only want to see a single item.
2842   * @param {string} itemId ID of object within the given store
2843   * @param {function|object} callbackObj A standard DWR callback object
2844   */
2845  dwr.data.Cache.prototype.viewItem = function(itemId, callbackObj) {
2846    return dwr.engine._execute(null, '__Data', 'viewItem', [ this.storeId, itemId, this.listener, callbackObj ]);
2847  };
2848
2849  /**
2850   * Undo the action of dwr.data.view()
2851   * @param {function|object} callbackObj A standard DWR callback object
2852   */
2853  dwr.data.Cache.prototype.unsubscribe = function(callbackObj) {
2854    if (this.listener) {
2855      return dwr.engine._execute(null, '__Data', 'unsubscribe', [ this.storeId, this.listener, callbackObj ]);
2856    }
2857  };
2858
2859  /**
2860   * Request an update to server side data
2861   * @param {Object} items An array of update descriptions
2862   * @param {function|object} callbackObj A standard DWR callback object
2863   */
2864  dwr.data.Cache.prototype.update = function(items, callbackObj) {
2865    return dwr.engine._execute(null, '__Data', 'update', [ this.storeId, items, callbackObj ]);
2866  };
2867
2868})();
2869
2870
2871
2872/* ==== INCLUDE: /js/weblets/FeaturedListingsWebletV2.js ==== */
2873
2874document.featuredListingsRefreshInterval = 5000;
2875document.featuredListingsPhotoRefreshInterval = 5000;
2876
2877try
2878{
2879	dwr.engine._allowGetForSafariButMakeForgeryEasier = true;
2880}
2881catch( e )
2882{	
2883}
2884
2885// Effect copied from rico
2886/**
2887  *
2888  *  Copyright 2005 Sabre Airline Solutions
2889  *
2890  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
2891  *  file except in compliance with the License. You may obtain a copy of the License at
2892  *
2893  *         http://www.apache.org/licenses/LICENSE-2.0
2894  *
2895  *  Unless required by applicable law or agreed to in writing, software distributed under the
2896  *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
2897  *  either express or implied. See the License for the specific language governing permissions
2898  *  and limitations under the License.
2899  **/
2900if ( window.Effect == undefined )
2901   Effect = {};
2902
2903Effect.FadeTo = function() {
2904	this.initialize.apply(this, arguments);
2905};
2906
2907Effect.FadeTo.prototype = {
2908
2909   initialize: function( element, opacity, duration, steps, options) {
2910	   if( typeof element == "string" ) {
2911		   this.element = document.getElementById( element );
2912	   }
2913	   else {
2914		   this.element  = element;
2915	   }
2916      
2917      this.opacity  = opacity;
2918      this.duration = duration;
2919      this.steps    = steps;
2920      this.options  = arguments[4] || {};
2921      this.fadeTo();
2922   },
2923
2924   fadeTo: function() {
2925      if (this.isFinished()) {
2926      	this.element.ricoOpacity = null;
2927         if(this.options.complete) this.options.complete(this);
2928         return;
2929      }
2930      if (this.timer)
2931         clearTimeout(this.timer);
2932
2933      var stepDuration = Math.round(this.duration/this.steps) ;
2934      var currentOpacity = this.getElementOpacity();
2935      var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0;
2936
2937      this.changeOpacityBy(delta);
2938      this.duration -= stepDuration;
2939      this.steps--;
2940
2941      this.timer = setTimeout(this.fadeTo.bind(this), stepDuration);
2942   },
2943
2944   changeOpacityBy: function(v) {
2945      var currentOpacity = this.getElementOpacity();
2946      var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1));
2947      this.element.ricoOpacity = newOpacity;
2948      // bill: bug note: in IE changing opacity appears to whack seemingly random elements
2949      // on page, disabling for IE, assining imgs[i].id == ... also seems to contribute
2950      // not sure what this is!!!, see another bug not below
2951      if( !browser.isIE ) {
2952      	//this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")";
2953      	this.element.style.opacity = newOpacity; /*//*/;
2954      }
2955   },
2956
2957   isFinished: function() {
2958      return this.steps <= 0;
2959   },
2960
2961   getElementOpacity: function() {
2962      if ( this.element.ricoOpacity == undefined ) {
2963         var opacity;
2964         if ( this.element.currentStyle ) {
2965            opacity = this.element.currentStyle.opacity;
2966         }
2967         else if ( document.defaultView.getComputedStyle != undefined ) {
2968            opacity = document.defaultView.getComputedStyle(this.element, null).getPropertyValue('opacity');
2969         }
2970
2971         this.element.ricoOpacity = opacity != undefined ? opacity : 1.0;
2972      }
2973
2974      return parseFloat(this.element.ricoOpacity);
2975   }
2976}
2977
2978
2979function rotateFeaturedListingsPhotos( id, interval )
2980{
2981	if( document.hidden ) {
2982		return;
2983	}
2984	if( !interval ) {
2985		interval = document.featuredListingsPhotoRefreshInterval;
2986	}
2987	for( var listingId in document.featuredListingsHash[id].images ) {
2988		var images = document.featuredListingsHash[id].images[listingId];
2989		if( images.length > 1 ) {
2990			setTimeout( "showFeaturedListingPhoto1('" + id + "', '" + listingId + 
2991				"', 1, " + interval + " )", interval );
2992		}
2993	}
2994}
2995
2996function showFeaturedListingPhoto1( id, listingId, index, interval )
2997{
2998	var w = document.getElementById(id);
2999	var imgId = id + "-" + listingId + "-image";
3000	var imgs = w.getElementsByTagName( "img" );
3001	var img = null;
3002	if( imgs && imgs.length > 0 ) {
3003		for( var i=0; i<imgs.length; ++i ) {
3004
3005			
3006			if( imgs[i].src && !imgs[i].classList.contains( "leadin_overlay" ) &&
3007					//imgs[i].src.indexOf( "/" + listingId + "/" ) != -1
3008					imgs[i].classList.contains("mrp-auto-image") &&
3009					imgs[i].closest('.mrp-leadin').querySelector('a').getAttribute('href').indexOf( listingId ) !== -1 &&
3010					imgs[i].getAttribute("id")==null ) {
3011				
3012				// if( imgs[i] && imgs[i].parentNode && imgs[i].parentNode.getAttribute( "data-auto-image" ) != "ready" ) {
3013				// 	setTimeout( function() {
3014				// 		showFeaturedListingPhoto1( id, listingId, index, interval );
3015				// 	}, 1000 );
3016				// 	return;
3017				// }
3018				
3019				// bill: bug note: instead of assining id, we are assigning to "rel"
3020				// connected to disappearing elements in IE bug, see above
3021				imgs[i].rel = imgId;
3022				img = imgs[i];	
3023				break;
3024			}
3025		}
3026	}
3027	if( !img ) {
3028		return;
3029	}
3030	// img.removeAttribute('srcset');
3031	// img.removeAttribute('sizes');
3032	var bg = document.getElementById(id + "-" + listingId + "-shadow");
3033	if (!bg) {
3034		bg = new Image();
3035		bg.setAttribute("id", id + "-" + listingId + "-shadow");
3036		bg.style.position = "absolute";
3037		bg.style.display = "block";
3038		bg.style.visibility = "visible";
3039		bg.style.margin = img.margin;
3040		bg.style.border = img.border;
3041		bg.className = "mrpl-image";
3042		img.parentNode.insertBefore( bg, img );
3043	}
3044	bg.style.left = img.style.left;
3045	bg.style.top = img.style.top;
3046	bg.style.width = img.style.width;
3047	bg.style.height = img.style.height;
3048	bg.style.opacity = "0"; // bill; this effectively negates teh bg trick, but it's due to flicker
3049	bg.src = img.src;
3050	// console.log( 'bg src', bg.src );
3051	new Effect.FadeTo( img, 0, 1, 1, {
3052		complete: function() { showFeaturedListingPhoto2( id, listingId, index, interval ); }
3053	} );
3054}
3055
3056function showFeaturedListingPhoto2( id, listingId, index, interval )
3057{
3058	if( !interval ) {
3059		interval = document.featuredListingsPhotoRefreshInterval;
3060	}
3061	
3062	var imgId = id + "-" + listingId + "-image";
3063	
3064	var images = document.featuredListingsHash[id].images[listingId];
3065	if( !images ) {
3066		return;
3067	}
3068	if( images.length <= index ) {
3069		index = 0;
3070	}
3071	
3072	var img = null;
3073	var imgId = id + "-" + listingId + "-image";
3074	var w =  document.getElementById(id);
3075	var imgs = w.getElementsByTagName( "img" );
3076	if( imgs && imgs.length > 0 ) {
3077		for( var i=0; i<imgs.length; ++i ) {
3078			if( imgs[i].rel == imgId ) {
3079				img = imgs[i];
3080				break;
3081			}
3082		}
3083	}
3084	
3085	//var img = $(id + "-" + listingId + "-image");
3086	if( !img ) {
3087		return;
3088	}
3089	img.onload = function() {
3090		new Effect.FadeTo( img, 1, 500, 50 );
3091
3092		setTimeout( "showFeaturedListingPhoto1('" + id + "', '" + listingId + "', " + index + ", " + interval + " )", interval );
3093	};
3094	index++;
3095	// console.log( 'img src before', img.src );
3096	setTimeout(function() {
3097		if( img.src != images[index]) {
3098			img.src = images[index];
3099		}
3100	}, 100 );
3101	// console.log( 'img src after', img.src );
3102
3103
3104}
3105
3106var prefetchedListingsNG = {};
3107
3108function rotateFeaturedListingsNG( id, listingIds, interval )
3109{
3110	if( document.hidden ) {
3111		return;
3112	}
3113	var w = document.getElementById(id);
3114	webletGateway.renderWeblet( document.pageName, id, 
3115		{ "ListingIds": listingIds, "ajaxGetAll" : "true" }, {
3116		callback: function( result ) {
3117			if( result.content ) {
3118			
3119				// strip the wrapper
3120				var content = result.content;
3121				var idx = content.indexOf( '>' );
3122				if( idx != -1 ) {
3123					content = content.substring( idx + 1 );
3124					idx = content.indexOf( '>' );
3125					if( idx != -1 ) {
3126						content = content.substring( idx + 1 );
3127					}
3128				}
3129				idx = content.lastIndexOf( "</div></div>" );
3130				if( idx != -1 ) {
3131					content = content.substring( 0, idx );
3132				}
3133			
3134				var tks = content.split( "\=\_\=\_\=\_\=\_\=" );
3135				prefetchedListingsNG[id] = tks;
3136				
3137				var pos = 1;
3138				if( tks.length == 1 ) {
3139					pos = 0;
3140				}
3141				
3142				new Effect.FadeTo( id, 0.1, 100, 10, { 
3143					complete: function() { rotateFeaturedListingsAfterFadeNG( id, pos, interval ); }
3144					});
3145			}
3146		},
3147		errorHandler: function( error ) {
3148			return;
3149		}
3150	});
3151}
3152
3153function getMatches(string, regex, index) {
3154	index || (index = 1); // default to the first capturing group
3155	var matches = [];
3156	var match;
3157	while (match = regex.exec(string)) {
3158		matches.push(match[index]);
3159	}
3160	return matches;
3161}
3162
3163function rotateFeaturedListingsAfterFadeNG( id, pos, interval )
3164{
3165	if( !prefetchedListingsNG[id] ) {
3166		return;
3167	}
3168	if( pos >= prefetchedListingsNG[id].length ) {
3169		return; // bail: something is wrong
3170	}
3171	var content = prefetchedListingsNG[id][pos];
3172	var scripts = getMatches(content, /<script[^>]*>(.*?)<\/script>/gmi).join('\n');
3173	content = content.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gmi, "")
3174	/*var idx = content.indexOf( "<script>" );
3175	if( idx != -1 ) {
3176		content = content.substring( 0, idx );
3177	}*/
3178	
3179	var elem = document.getElementById(id);
3180	
3181	elem = elem.children[0]; // inner-weblet
3182	
3183	var h2 = null;
3184	var viewAll = null;
3185	
3186	var h2s = elem.getElementsByTagName( "h2" );
3187	if( h2s && h2s.length > 0 && h2s[0] && h2s[0].className === "weblet_featured-listings-title" ) {
3188		h2 = h2s[0];
3189	}
3190	
3191	if( elem.children[elem.children.length-1] && 
3192			elem.children[elem.children.length-1].className === "weblet_featured-listings-view-all" ) {
3193		viewAll = elem.children[elem.children.length-1];
3194	}
3195	
3196	if( !elem.existingRRectId ) {
3197		var existingRRectId = null;
3198		var divs = elem.getElementsByTagName("div");
3199		for( var i=0; i<divs.length; ++i ) {
3200			if( divs[i].className && 
3201				divs[i].className.indexOf( "leadin_style" ) != -1 &&
3202				divs[i].id ) {
3203				existingRRectId = divs[i].id;
3204				break;
3205			}
3206		}
3207		elem.existingRRectId = existingRRectId;
3208	}
3209	
3210	elem.innerHTML = content;
3211	if( h2 ) {
3212		elem.insertBefore( h2, elem.children[0] );
3213	}
3214	
3215	if( viewAll ) {
3216		// no need to re-append what is already there
3217		//elem.appendChild( viewAll );
3218	}
3219	eval(scripts);
3220
3221	if( elem.existingRRectId ) {
3222		setTimeout( function() {
3223			var fx = allFx[elem.existingRRectId];
3224			if( fx && fx.applyOnResizeFx ) {
3225				var divs = elem.getElementsByTagName("div");
3226				for( var i=0; i<divs.length; ++i ) {
3227					if( divs[i].className && 
3228						divs[i].className.indexOf( "leadin_style" ) != -1 &&
3229						divs[i].id ) {
3230						fx.applyOnLoadFx( divs[i].id );
3231					}
3232				}
3233			}
3234		}, 102 );
3235	}
3236	
3237	new Effect.FadeTo( id, 1, 100, 10 );
3238	var newPos = pos+1;
3239	if( newPos >= prefetchedListingsNG[id].length ) {
3240		newPos = 0;
3241	}
3242	
3243	if( !interval ) {
3244		interval = document.featuredListingsRefreshInterval;
3245	}
3246	
3247	if( window.location.href.indexOf( 'm=__manage__' ) != -1 ) {
3248		return;
3249	}
3250	
3251	setTimeout( function() {
3252		new Effect.FadeTo( id, 0.1, 100, 10, { 
3253				complete: function() { rotateFeaturedListingsAfterFadeNG( id, newPos, interval ); }
3254				});
3255	}, interval );
3256}
3257
3258function rotateFeaturedListings( id, listingIds, interval )
3259{
3260	if( window.location.href.indexOf( 'm=__manage__' ) != -1 ) {
3261		return;
3262	}
3263
3264	if( true ) {
3265		rotateFeaturedListingsNG( id, listingIds, interval );
3266		return;
3267	}
3268}
3269
3270/* ==== INCLUDE: /js/weblets/responsive/leadins.js ==== */
3271
3272/* leadins js */
3273jQuery( function() {
3274	jQuery( document ).on( "click", ".mrp-leadin *", function(e) {
3275		var $p = jQuery( this ).closest( ".mrp-leadin" );
3276
3277		if( $(this).hasClass( 'ed3-editable') && $("body").hasClass( "ed3-inline-edit-on" ) ) {
3278			return false;
3279		}
3280		var $target = jQuery( e.target );
3281		if( $target && $target.length > 0 && "A" == $target.prop("tagName" ) || $target.closest( "a" ).length > 0 ) {
3282			return true;
3283		}
3284		var url = $p.attr( "data-link" );
3285		if( url ) {
3286			e.stopImmediatePropagation();
3287			if( $p.attr( "data-target" ) ) {
3288				window.open( url );
3289			}
3290			else {
3291				window.location = url;
3292			}
3293			console.log( url );
3294			return false;
3295		}
3296		return true;
3297	});
3298});

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.