我的测试dwr + jsf

资料1:Getting Started with DWR

There are several ways to get started with DWR. We recommend the following:

  1. Follow the 5 steps outlined below.
  2. Download the dwr.war file and experiment with the examples.
  3. Once DWR is up and running learn how to write JavaScript for DWR.

DWR in 5 steps
1. Install the DWR JAR file

Download the dwr.jar file. Place it into the WEB-INF/lib directory of your web application.

2. Install the Commons Logging JAR file

DWR depends on Commons LoggingDownload the commons-logging.jar and place it into the WEB-INF/lib directory of your web application.

3. Add the DWR servlet definition and mapping to your application's web.xml

Add the following lines to your web application's deployment descriptor (WEB-INF/web.xml). The <servlet> section needs to go with any existing <servlet> sections, and likewise with the <servlet-mapping> section.

<servlet>
  <display-name>DWR Servlet</display-name>
  <servlet-name>dwr-invoker</servlet-name>  
  <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
  <init-param>
     <param-name>debug</param-name>
     <param-value>true</param-value>
  </init-param>
</servlet>

<servlet-mapping>
  <servlet-name>dwr-invoker</servlet-name>
  <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
4. Create the DWR configuration file (dwr.xml)

Create a new file in you web application's WEB-INF directory (alongside web.xml) named dwr.xml.

A simple way to start is with something like this:

<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN"
    "http://getahead.org/dwr/dwr30.dtd">

<dwr>
  <allow>
    <create creator="new" javascript="JDate">
      <param name="class" value="java.util.Date"/>
    </create>
    <create creator="new" javascript="Demo">
      <param name="class" value="your.java.Bean"/>
    </create>
  </allow>
</dwr>

The DWR config file defines what classes DWR can create and remote for use by Javascript. In the example above we are defining 2 classes that are remoted and giving the classes names in Javascript.

The new creator that we used above uses the public no-args constructor that all Java Beans must have. It is also worth remembering that DWR has one restriction:

  • Avoid reserved JavaScript words; Methods named after reserved words are automatically excluded. Most JavaScript reserved words are also Java reserved words, so you won't be having a method called "try()" anyway. However the most common gotcha is "delete()", which has special meaning in JavaScript but not Java.

Visit the dwr.xml documentation for a detailed description of the dwr.xml file, creatorsand converters.

5. Take DWR for a test drive!

Go to the following URL:

http://localhost:8080/[YOUR-WEBAPP-CONTEXT]/dwr/

You should see a page showing you the classes you configured in Step 4. Having followed a link you should see an index of all the methods ready for calling - simply enter the required parameters (if parameters are required) and click the execute button. These pages are valuable debugging tools and dynamically generated by DWR when the debug init-param is set to true.


------hanxinis---

http://localhost:8080/WebProject.war/dwr/index.html 显示表示成功

Modules known to DWR:

此时的配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="dwr" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name></display-name>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>

<servlet>
<display-name>DWR Servlet</display-name>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

</web-app>
------------------

How to make use of this from your web application

We have a whole section on writing Javascript with DWR, and there are a number of examples in the sidebar that demonstrate how to dynamically alter the text in web pages, update lists, manipulate forms and perform live table editing. Each has a description of how it works.

Another way to get started is to look at the source from the pages that you just viewed:

  • Go to http://localhost:8080/[YOUR-WEBAPP-CONTEXT]/dwr/ and click on your class
  • View source and find the line that executes the method that you are interested in.
  • Paste the text into an HTML or JSP page in your web-app.
  • Include links to the DWR JavaScript files that make the magic happen:
<script src='/[YOUR-WEBAPP-CONTEXT]/dwr/interface/[YOUR-SCRIPT].js'></script>
<script src='/[YOUR-WEBAPP-CONTEXT]/dwr/engine.js'></script>

You can omit the /[YOUR-WEBAPP-CONTEXT]/ section and use relative paths in your web pages if you wish.

Next Step: Learn about writing Javascript that interacts with DWR.

What if it doesn't work?

We have a page containing troubleshooting advice and common problems and their fixes. This is the first place to look.

If you are still having problems please join the mailing list and ask there.


资料2:jsf支持

JSF Integration

DWR contains 2 extension points for JSF, a creator and a ServletFilter. The JSF integration is not included in the dwr.jar. It is currently only available in the dwr-noncla.jar which is available for download.

The 'jsf' Creator

An experimental JsfCreator is included with the normal DWR download. You can use this from dwr.xml as follows:

<init>
  <creator id="jsf" class="org.directwebremoting.faces.JsfCreator"/>
</init>

<allow>
  ...
  <create creator="jsf" javascript="ScriptName">
    <param name="managedBeanName" value="beanName"/>
    <param name="class" value="your.class"/>
  </create>
  ...
</allow>

This will allow you to make calls into your ManagedBeans from DWR. Please note the init section. This section is required because the JSF creator is not defined in the standard dwr.xml (because of the non-cla issue).

The Servlet Filter

The DWR/Faces filter allows you to access beans from your FacesContext without being officially part of the JSF lifecycle.

In order to use the JsfCreator, you should add the DWR/Faces filter to your web.xml file.

<filter>
  <filter-name>DwrFacesFilter</filter-name>
  <filter-class>org.directwebremoting.faces.FacesExtensionFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>DwrFacesFilter</filter-name>
  <url-pattern>/dwr/*</url-pattern>
</filter-mapping>

The 2 elements will need to be placed alongside other filter and filter-mapping elements in your web.xml file.

This JSF integration was written by Pierpaolo Follia, with inspiration from an article from thoughtsabout.net.



我的环境搭建问题记录:


一.firefox察看如下:

[22:07:18.637] HTML 文档的字符编码未声明。如果该文件包含 US-ASCII 范围之外的字符,该文件将在某些浏览浏览器配置中呈现为乱码。页面的字符编码必须在文档或传输协议层声明。 @ http://localhost:8080/WebProject.war/TestDWR.face
--
[22:07:32.315] GET http://localhost:8080/WebProject.war/TestDWR.faces [HTTP/1.1 200 OK 9ms]
[22:07:32.347] GET http://localhost:8080/WebProject.war/dwr/engine.js [HTTP/1.1 304 Not Modified 4ms]--------------已经将js发送到浏览器了
[22:07:32.358] GET http://localhost:8080/WebProject.war/dwr/util.js [HTTP/1.1 304 Not Modified 4ms] ---------------已经将js发送到浏览器了
[22:07:32.361] GET http://localhost:8080/WebProject.war/dwr/interface/TestDWRBean.js [HTTP/1.1 501 Error. Details logged to the console 4ms] ----发生了错误
[22:07:32.346] 未知属性 'zoom'。  声明被丢弃。 @ http://localhost:8080/WebProject.war/layout.css:21
[22:07:32.383] POST http://localhost:8080/WebProject.war/dwr/call/plaincall/__System.pageLoaded.dwr [HTTP/1.1 200 OK 2ms]
[22:07:32.392] GET http://localhost:8080/WebProject.war/dwr/interface/TestDWRBean.js [HTTP/1.1 501 Error. Details logged to the console 1ms]


2.添加jsf支持:


----web.xml----
<filter>
<filter-name>DwrFacesFilter</filter-name>
<filter-class>dwr-noncla.org.directwebremoting.faces.FacesExtensionFilter</filter-class>
</filter>
<filter-mapping>

<filter-name>DwrFacesFilter</filter-name>
<url-pattern>/dwr/*</url-pattern>
</filter-mapping>

----dwr.xml----

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd">

<dwr>
<init>
<creator id="jsf" class="org.directwebremoting.faces.JsfCreator" />
</init>
<allow>
<create creator="jsf" javascript="TestDWRBean">
<param name="managedBeanName" value="TestDWRBean" />
<param name="class" value="TestDWRBean" />
</create>
</allow>
</dwr>


3.还是会报错找不到js,同时部署glassfish时候:

2012-08-08T22:16:53.661+0800 javax.enterprise.system.tools.deployment.org.glassfish.deployment.common
INFO: Autoundeploying application :WebProject
2012-08-08T22:16:53.680+0800 javax.enterprise.system.tools.deployment.org.glassfish.deployment.common
INFO: [AutoDeploy] Successfully autoundeployed : /root/glassfish3/glassfish/domains/domain1/autodeploy/WebProject.war.
2012-08-08T22:16:55.658+0800 javax.enterprise.system.tools.deployment.org.glassfish.deployment.common
INFO: [AutoDeploy] Selecting file /root/glassfish3/glassfish/domains/domain1/autodeploy/WebProject.war for autodeployment.
2012-08-08T22:16:55.874+0800 javax.enterprise.resource.webcontainer.jsf.config
INFO: 初始化上下文 '/WebProject.war' 的 Mojarra 2.1.6 (SNAPSHOT 20111206)
2012-08-08T22:16:55.939+0800 javax.enterprise.system.container.web.com.sun.enterprise.web
SEVERE: WebModule[/WebProject.war]PWC1270: Exception starting filter DwrFacesFilter
java.lang.InstantiationException
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:124)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4685)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:5377)
at com.sun.enterprise.web.WebModule.start(WebModule.java:498)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:917)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:901)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:733)
at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:2018)
at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1669)
at com.sun.enterprise.web.WebApplication.start(WebApplication.java:109)
at org.glassfish.internal.data.EngineRef.start(EngineRef.java:130)
at org.glassfish.internal.data.ModuleInfo.start(ModuleInfo.java:269)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:301)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:461)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:240)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:389)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:353)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:363)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1085)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl.java:95)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1291)
at org.glassfish.deployment.autodeploy.AutoOperation.run(AutoOperation.java:145)
at org.glassfish.deployment.autodeploy.AutoDeployer.deploy(AutoDeployer.java:575)
at org.glassfish.deployment.autodeploy.AutoDeployer.deployAll(AutoDeployer.java:461)
at org.glassfish.deployment.autodeploy.AutoDeployer.run(AutoDeployer.java:389)
at org.glassfish.deployment.autodeploy.AutoDeployer.run(AutoDeployer.java:380)
at org.glassfish.deployment.autodeploy.AutoDeployService$1.run(AutoDeployService.java:220)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
Caused by: java.lang.ClassNotFoundException: dwr-noncla.org.directwebremoting.faces.FacesExtensionFilter
at org.glassfish.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1509)
at org.glassfish.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1359)
at org.apache.catalina.core.ApplicationFilterConfig.loadFilterClass(ApplicationFilterConfig.java:280)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:250)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:120)
... 28 more
2012-08-08T22:16:55.942+0800 javax.enterprise.system.container.web.com.sun.enterprise.web
INFO: WEB0671: Loading application [WebProject] at [/WebProject.war]
2012-08-08T22:16:55.947+0800 javax.enterprise.system.tools.admin.org.glassfish.deployment.admin
INFO: WebProject was successfully deployed in 284 milliseconds.
2012-08-08T22:16:55.948+0800 javax.enterprise.system.tools.deployment.org.glassfish.deployment.common
INFO: [AutoDeploy] Successfully autodeployed : /root/glassfish3/glassfish/domains/domain1/autodeploy/WebProject.war.


部署成功解决://不要擅自做主该路径:org.directwebremoting.faces.FacesExtensionFilter是对 的,而非dwr-noncla.org.directwebremoting.faces.FacesExtensionFilter

<filter>
<filter-name>DwrFacesFilter</filter-name>
<filter-class>org.directwebremoting.faces.FacesExtensionFilter</filter-class>
</filter>


还是找不到我的bean对应的js?

[22:44:21.227] GET http://localhost:8080/WebProject.war/TestDWR.faces [HTTP/1.1 200 OK 19ms]
[22:44:21.262] GET http://localhost:8080/WebProject.war/dwr/engine.js [HTTP/1.1 200 OK 274ms]
[22:44:21.266] GET http://localhost:8080/WebProject.war/dwr/util.js [HTTP/1.1 200 OK 272ms]

[22:44:21.268] GET http://localhost:8080/WebProject.war/dwr/interface/testdwrbean.js [HTTP/1.1 501 Error. Details logged to the console 275ms]
[22:44:21.261] 未知属性 'zoom'。  声明被丢弃。 @ http://localhost:8080/WebProject.war/layout.css:21
[22:44:21.551] POST http://localhost:8080/WebProject.war/dwr/call/plaincall/__System.pageLoaded.dwr [HTTP/1.1 200 OK 19ms]
[22:44:21.557] GET http://localhost:8080/WebProject.war/dwr/interface/testdwrbean.js [HTTP/1.1 501 Error. Details logged to the console 2ms]


小技巧可以这样检测:

http://localhost:8080/WebProject.war/dwr/engine.js 以及另外的2个js


/*
 * Copyright 2005 Joe Walker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Declare an object to which we can add real functions.
 */
if (typeof dwr == 'undefined') dwr = {};
if (!dwr.util) dwr.util = {};

/** @private The flag we use to decide if we should escape html */
dwr.util._escapeHtml = true;

/**
 * Set the global escapeHtml flag
 */
dwr.util.setEscapeHtml = function(escapeHtml) {
  dwr.util._escapeHtml = escapeHtml;
};

/** @private Work out from an options list and global settings if we should be esccaping */
dwr.util._shouldEscapeHtml = function(options) {
  if (options && options.escapeHtml != null) {
    return options.escapeHtml;
  }
  return dwr.util._escapeHtml;
};

/**
 * Return a string with &, < and > replaced with their entities
 * @see TODO
 */
dwr.util.escapeHtml = function(original) {
  return original.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#039;');
};

/**
 * Replace common XML entities with characters (see dwr.util.escapeHtml())
 * @see TODO
 */
dwr.util.unescapeHtml = function(original) {
  return original.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&quot;/g,'"').replace(/&#039;/g,"'").replace(/&amp;/g,'&');
};

/**
 * Replace characters dangerous for XSS reasons with visually similar characters
 * @see TODO
 */
dwr.util.replaceXmlCharacters = function(original) {
  original = original.replace("&", "+");
  original = original.replace("<", "\u2039");
  original = original.replace(">", "\u203A");
  original = original.replace("\'", "\u2018");
  original = original.replace("\"", "\u201C");
  return original;
};

/**
 * Return true iff the input string contains any XSS dangerous characters
 * @see TODO
 */
dwr.util.containsXssRiskyCharacters = function(original) {
  return (original.indexOf('&') != -1
    || original.indexOf('<') != -1
    || original.indexOf('>') != -1
    || original.indexOf('\'') != -1
    || original.indexOf('\"') != -1);
};

/**
 * Enables you to react to return being pressed in an input
 * @see http://getahead.org/dwr/browser/util/selectrange
 */
dwr.util.onReturn = function(event, action) {
  if (!event) event = window.event;
  if (event && event.keyCode && event.keyCode == 13) action();
};

/**
 * Select a specific range in a text box. Useful for 'google suggest' type functions.
 * @see http://getahead.org/dwr/browser/util/selectrange
 */
dwr.util.selectRange = function(ele, start, end) {
  ele = dwr.util._getElementById(ele, "selectRange()");
  if (ele == null) return;
  if (ele.setSelectionRange) {
    ele.setSelectionRange(start, end);
  }
  else if (ele.createTextRange) {
    var range = ele.createTextRange();
    range.moveStart("character", start);
    range.moveEnd("character", end - ele.value.length);
    range.select();
  }
  ele.focus();
};

/**
 * Find the element in the current HTML document with the given id or ids
 * @see http://getahead.org/dwr/browser/util/$
 */
dwr.util.byId = function() {
  var elems = [];
  for (var i = 0; i < arguments.length; i++) {
    var idOrElem = arguments[i];
    var elem;
    if (typeof idOrElem == 'string') {
      var elem = document.getElementById(idOrElem);
      // Workaround for IE and Opera that may return element based on name
      if (document.all && elem && dwr.util._getId(elem) != idOrElem) {
        elem = null;
        var maybeElems = document.all[idOrElem];
        if (maybeElems.tagName) maybeElems = [maybeElems];
        for (var j = 0; j < maybeElems.length; j++) {
          if (dwr.util._getId(maybeElems[j]) == idOrElem) {
            elem = maybeElems[j];
            break;
          }
        }
      }
    }
    else {
      elem = idOrElem;
    }
    if (arguments.length == 1) {
      return elem;
    }
    elems.push(elem);
  }
  return elems;
}

dwr.util._getId = function(elem) {
  // Get the element's real id. In some situations IE may (wrongly)
  // return another element from the getAttribute() call so we watch out for 
  // this case and use the attributes.id value instead. Note that the use of 
  // the attributes collection may cause problems in IE7 together with 
  // cloneNode so we avoid hitting it when possible.  
  var elemId = elem.getAttribute("id");
  if (dwr.util._isObject(elemId)) {
    elemId = elem.attributes.id.value;
  }
  return elemId;
};

/**
 * Alias $ to dwr.util.byId
 * @see http://getahead.org/dwr/browser/util/$
 */
if (typeof $ == 'undefined') {
  $ = dwr.util.byId;
}

/**
 * This function pretty-prints simple data or whole object graphs, f ex as an aid in debugging.
 * @see http://getahead.org/dwr/browser/util/todescriptivestring
 */
dwr.util.toDescriptiveString = function(data, showLevels, options) {
  if (showLevels === undefined) showLevels = 1;
  var opt = {};
  if (dwr.util._isObject(options)) opt = options;
  var defaultoptions = {
    escapeHtml:false,
    baseIndent: "",
    childIndent: "\u00A0\u00A0",
    lineTerminator: "\n",
    oneLineMaxItems: 5,
    shortStringMaxLength: 13,
    propertyNameMaxLength: 30 
  };
  for (var p in defaultoptions) {
    if (!(p in opt)) {
      opt[p] = defaultoptions[p];
    }
  }

  var skipDomProperties = {
    document:true, ownerDocument:true,
    all:true,
    parentElement:true, parentNode:true, offsetParent:true,
    children:true, firstChild:true, lastChild:true,
    previousSibling:true, nextSibling:true,
    innerHTML:true, outerHTML:true,
    innerText:true, outerText:true, textContent:true,
    attributes:true,
    style:true, currentStyle:true, runtimeStyle:true,
    parentTextEdit:true
  };
  
  function recursive(data, showLevels, indentDepth, options) {
    var reply = "";
    try {
      // string
      if (dwr.util._isString(data)) {
        var str = data;
        if (showLevels == 0 && str.length > options.shortStringMaxLength)
          str = str.substring(0, options.shortStringMaxLength-3) + "...";
        if (options.escapeHtml) {
          // Do the escape separately for every line as escapeHtml() on some 
          // browsers (IE) will strip line breaks and we want to preserve them
          var lines = str.split("\n");
          for (var i = 0; i < lines.length; i++) lines[i] = dwr.util.escapeHtml(lines[i]);
          str = lines.join("\n");
        }
        if (showLevels == 0) { // Short format
          str = str.replace(/\n|\r|\t/g, function(ch) {
            switch (ch) {
              case "\n": return "\\n";
              case "\r": return "";
              case "\t": return "\\t";
            }
          });
        }
        else { // Long format
          str = str.replace(/\n|\r|\t/g, function(ch) {
            switch (ch) {
              case "\n": return options.lineTerminator + indent(indentDepth+1, options);
              case "\r": return "";
              case "\t": return "\\t";
            }
          });
        }
        reply = '"' + str + '"';
      }
      
      // function
      else if (dwr.util._isFunction(data)) {
        reply = "function";
      }
    
      // Array
      else if (dwr.util._isArrayLike(data)) {
        if (showLevels == 0) { // Short format (don't show items)
          if (data.length > 0)
            reply = "[...]";
          else
            reply = "[]";
        }
        else { // Long format (show items)
          var strarr = [];
          strarr.push("[");
          var count = 0;
          for (var i = 0; i < data.length; i++) {
            if (!(i in data) && data != "[object NodeList]") continue;
            var itemvalue = data[i];
            if (count > 0) strarr.push(", ");
            if (showLevels == 1) { // One-line format
              if (count == options.oneLineMaxItems) {
                strarr.push("...");
                break;
              }
            }
            else { // Multi-line format
              strarr.push(options.lineTerminator + indent(indentDepth+1, options));
            }
            if (i != count) {
              strarr.push(i);
              strarr.push(":");
            }
            strarr.push(recursive(itemvalue, showLevels-1, indentDepth+1, options));
            count++;
          }
          if (showLevels > 1) strarr.push(options.lineTerminator + indent(indentDepth, options));
          strarr.push("]");
          reply = strarr.join("");
        }
      }
      
      // Objects except Date
      else if (dwr.util._isObject(data) && !dwr.util._isDate(data)) {
        if (showLevels == 0) { // Short format (don't show properties)
          reply = dwr.util._detailedTypeOf(data);
        }
        else { // Long format (show properties)
          var strarr = [];
          if (dwr.util._detailedTypeOf(data) != "Object") {
            strarr.push(dwr.util._detailedTypeOf(data));
            if (typeof data.valueOf() != "object") {
              strarr.push(":");
              strarr.push(recursive(data.valueOf(), 1, indentDepth, options));
            }
            strarr.push(" ");
          }
          strarr.push("{");
          var isDomObject = dwr.util._isHTMLElement(data); 
          var count = 0;
          for (var prop in data) {
            var propvalue = data[prop];
            if (isDomObject) {
              if (propvalue == null) continue;
              if (typeof propvalue == "function") continue;
              if (skipDomProperties[prop]) continue;
              if (prop.toUpperCase() == prop) continue;
            }
            if (count > 0) strarr.push(", ");
            if (showLevels == 1) { // One-line format
              if (count == options.oneLineMaxItems) {
                strarr.push("...");
                break;
              }
            }
            else { // Multi-line format
              strarr.push(options.lineTerminator + indent(indentDepth+1, options));
            }
            strarr.push(prop.length > options.propertyNameMaxLength ? prop.substring(0, options.propertyNameMaxLength-3) + "..." : prop);
            strarr.push(":");
            strarr.push(recursive(propvalue, showLevels-1, indentDepth+1, options));
            count++;
          }
          if (showLevels > 1 && count > 0) strarr.push(options.lineTerminator + indent(indentDepth, options));
          strarr.push("}");
          reply = strarr.join("");
        }
      }
  
      // undefined, null, number, boolean, Date
      else {
        reply = "" + data;
      }
  
      return reply;
    }
    catch(err) {
      return (err.message ? err.message : ""+err);
    }
  }

  function indent(count, options) {
    var strarr = [];
    strarr.push(options.baseIndent);
    for (var i=0; i<count; i++) {
      strarr.push(options.childIndent);
    }
    return strarr.join("");
  };
  
  return recursive(data, showLevels, 0, opt);
};

/**
 * Setup a GMail style loading message.
 * @see http://getahead.org/dwr/browser/util/useloadingmessage
 */
dwr.util.useLoadingMessage = function(message) {
  var loadingMessage;
  if (message) loadingMessage = message;
  else loadingMessage = "Loading";
  dwr.engine.setPreHook(function() {
    var disabledZone = dwr.util.byId('disabledZone');
    if (!disabledZone) {
      disabledZone = document.createElement('div');
      disabledZone.setAttribute('id', 'disabledZone');
      disabledZone.style.position = "absolute";
      disabledZone.style.zIndex = "1000";
      disabledZone.style.left = "0px";
      disabledZone.style.top = "0px";
      disabledZone.style.width = "100%";
      disabledZone.style.height = "100%";
      // IE need a background color to block click. Use an invisible background.
      if (window.ActiveXObject) {
        disabledZone.style.background = "white";
        disabledZone.style.filter = "alpha(opacity=0)";
      }
      document.body.appendChild(disabledZone);
      var messageZone = document.createElement('div');
      messageZone.setAttribute('id', 'messageZone');
      messageZone.style.position = "absolute";
      messageZone.style.top = "0px";
      messageZone.style.right = "0px";
      messageZone.style.background = "red";
      messageZone.style.color = "white";
      messageZone.style.fontFamily = "Arial,Helvetica,sans-serif";
      messageZone.style.padding = "4px";
      document.body.appendChild(messageZone);
      var text = document.createTextNode(loadingMessage);
      messageZone.appendChild(text);
      dwr.util._disabledZoneUseCount = 1;
    }
    else {
      dwr.util.byId('messageZone').innerHTML = loadingMessage;
      disabledZone.style.visibility = 'visible';
      dwr.util._disabledZoneUseCount++;
      dwr.util.byId('messageZone').style.visibility = 'visible';
    }
  });
  dwr.engine.setPostHook(function() {
    dwr.util._disabledZoneUseCount--;
    if (dwr.util._disabledZoneUseCount == 0) {
      dwr.util.byId('disabledZone').style.visibility = 'hidden';
      dwr.util.byId('messageZone').style.visibility = 'hidden';
    }
  });
};

/**
 * Set a global highlight handler
 */
dwr.util.setHighlightHandler = function(handler) {
  dwr.util._highlightHandler = handler;
};

/**
 * An example highlight handler
 */
dwr.util.yellowFadeHighlightHandler = function(ele) {
  dwr.util._yellowFadeProcess(ele, 0);
};
dwr.util._yellowFadeSteps = [ "d0", "b0", "a0", "90", "98", "a0", "a8", "b0", "b8", "c0", "c8", "d0", "d8", "e0", "e8", "f0", "f8" ];
dwr.util._yellowFadeProcess = function(ele, colorIndex) {
  ele = dwr.util.byId(ele);
  if (colorIndex < dwr.util._yellowFadeSteps.length) {
    ele.style.backgroundColor = "#ffff" + dwr.util._yellowFadeSteps[colorIndex];
    setTimeout("dwr.util._yellowFadeProcess('" + dwr.util._getId(ele) + "'," + (colorIndex + 1) + ")", 200);
  }
  else {
    ele.style.backgroundColor = "transparent";
  }
};

/**
 * An example highlight handler
 */
dwr.util.borderFadeHighlightHandler = function(ele) {
  ele.style.borderWidth = "2px";
  ele.style.borderStyle = "solid";
  dwr.util._borderFadeProcess(ele, 0);
};
dwr.util._borderFadeSteps = [ "d0", "b0", "a0", "90", "98", "a0", "a8", "b0", "b8", "c0", "c8", "d0", "d8", "e0", "e8", "f0", "f8" ];
dwr.util._borderFadeProcess = function(ele, colorIndex) {
  ele = dwr.util.byId(ele);
  if (colorIndex < dwr.util._borderFadeSteps.length) {
    ele.style.borderColor = "#ff" + dwr.util._borderFadeSteps[colorIndex] + dwr.util._borderFadeSteps[colorIndex];
    setTimeout("dwr.util._borderFadeProcess('" + dwr.util._getId(ele) + "'," + (colorIndex + 1) + ")", 200);
  }
  else {
    ele.style.backgroundColor = "transparent";
  }
};

/**
 * A focus highlight handler
 */
dwr.util.focusHighlightHandler = function(ele) {
  try {
    ele.focus();
  }
  catch (ex) { /* ignore */ }
};

/** @private the current global highlight style */
dwr.util._highlightHandler = null;

/**
 * Highlight that an element has changed
 */
dwr.util.highlight = function(ele, options) {
  if (options && options.highlightHandler) {
    options.highlightHandler(dwr.util.byId(ele));
  }
  else if (dwr.util._highlightHandler != null) {
    dwr.util._highlightHandler(dwr.util.byId(ele));
  }
};

/**
 * Set the value an HTML element to the specified value.
 * @see http://getahead.org/dwr/browser/util/setvalue
 */
dwr.util.setValue = function(ele, val, options) {
  if (val == null) val = "";
  if (options == null) options = {};

  var orig = ele;
  ele = dwr.util.byId(ele); // Returns null if argument is a name, even on IE
  var nodes = null;
  if (ele == null) {
    // Now it is time to look by name
    nodes = document.getElementsByName(orig);
    if (nodes.length >= 1) ele = nodes.item(0);
  }

  if (ele == null) {
    dwr.util._debug("setValue() can't find an element with id/name: " + orig + ".");
    return;
  }

  // All paths now lead to some update so we highlight a change
  dwr.util.highlight(ele, options);

  if (dwr.util._isHTMLElement(ele, "select")) {
    if (ele.type == "select-multiple" && dwr.util._isArray(val)) dwr.util._selectListItems(ele, val);
    else dwr.util._selectListItem(ele, val);
    return;
  }

  if (dwr.util._isHTMLElement(ele, "input")) {
    if (ele.type == "radio" || ele.type == "checkbox") {
      if (nodes && nodes.length >= 1) {
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes.item(i);
          if (node.type != ele.type) continue;
          if (dwr.util._isArray(val)) {
            node.checked = false;
            for (var j = 0; j < val.length; j++)
              if (val[j] == node.value) node.checked = true;
          }
          else {
            node.checked = (node.value == val);
          }
        }
      }
      else {
        ele.checked = (val == true);
      }
    }
    else ele.value = val;

    return;
  }

  if (dwr.util._isHTMLElement(ele, "textarea")) {
    ele.value = val;
    return;
  }

  if (dwr.util._isHTMLElement(ele, "img")) {
    ele.src = val;
    return;
  }

  // If the value to be set is a DOM object then we try importing the node
  // rather than serializing it out
  if (val.nodeType) {
    if (val.nodeType == 9 /*Node.DOCUMENT_NODE*/) val = val.documentElement;
    val = dwr.util._importNode(ele.ownerDocument, val, true);
    ele.appendChild(val);
    return;
  }

  // Fall back to innerHTML and friends
  if (dwr.util._shouldEscapeHtml(options)) {
    if ("textContent" in ele) ele.textContent = val.toString();
    else if ("innerText" in ele) ele.innerText = val.toString();
    else ele.innerHTML = dwr.util.escapeHtml(val.toString());
  }
  else {
    ele.innerHTML = val;
  }
};

/**
 * @private Find multiple items in a select list and select them. Used by setValue()
 * @param ele The select list item
 * @param val The array of values to select
 */
dwr.util._selectListItems = function(ele, val) {
  // We deal with select list elements by selecting the matching option
  // Begin by searching through the values
  var found  = 0;
  var i;
  var j;
  for (i = 0; i < ele.options.length; i++) {
    ele.options[i].selected = false;
    for (j = 0; j < val.length; j++) {
      if (ele.options[i].value == val[j]) {
        ele.options[i].selected = true;
        found++;
      }
    }
  }
  // If that fails then try searching through the visible text
  if (found == val.length) return;

  for (i = 0; i < ele.options.length; i++) {
    for (j = 0; j < val.length; j++) {
      if (ele.options[i].text == val[j]) {
        ele.options[i].selected = true;
      }
    }
  }
};

/**
 * @private Find an item in a select list and select it. Used by setValue()
 * @param ele The select list item
 * @param val The value to select
 */
dwr.util._selectListItem = function(ele, val) {
  // We deal with select list elements by selecting the matching option
  // Begin by searching through the values
  var found = false;
  var i;
  for (i = 0; i < ele.options.length; i++) {
    if (ele.options[i].value == val) {
      ele.options[i].selected = true;
      found = true;
    }
    else {
      ele.options[i].selected = false;
    }
  }

  // If that fails then try searching through the visible text
  if (found) return;

  for (i = 0; i < ele.options.length; i++) {
    ele.options[i].selected = (ele.options[i].text == val);
  }
};

/**
 * Read the current value for a given HTML element.
 * @see http://getahead.org/dwr/browser/util/getvalue
 */
dwr.util.getValue = function(ele, options) {
  if (options == null) options = {};
  var orig = ele;
  ele = dwr.util.byId(ele); // Returns null if argument is a name, even on IE
  var nodes = null;
  if (ele == null) {
    // Now it is time to look by name
    nodes = document.getElementsByName(orig);
    if (nodes.length >= 1) ele = nodes.item(0);
  }
  if (ele == null) {
    dwr.util._debug("getValue() can't find an element with id/name: " + orig + ".");
    return "";
  }

  if (dwr.util._isHTMLElement(ele, "select")) {
    // Using "type" property instead of "multiple" as "type" is an official 
    // client-side property since JS 1.1
    if (ele.type == "select-multiple") {
      var reply = new Array();
      for (var i = 0; i < ele.options.length; i++) {
        var item = ele.options[i];
        if (item.selected) {
          var valueAttr = item.getAttributeNode("value");
          if (valueAttr && valueAttr.specified) {
            reply.push(item.value);
          }
          else {
            reply.push(item.text);
          }
        }
      }
      return reply;
    }
    else {
      var sel = ele.selectedIndex;
      if (sel != -1) {
        var item = ele.options[sel];
        var valueAttr = item.getAttributeNode("value");
        if (valueAttr && valueAttr.specified) {
          return item.value;
        }
        return item.text;
      }
      else {
        return "";
      }
    }
  }

  if (dwr.util._isHTMLElement(ele, "input")) {
    if (ele.type == "radio") {
      if (nodes && nodes.length >= 1) {
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes.item(i);
          if (node.type == ele.type) {
            if (node.checked) return node.value;
          }
        }
      }
      return ele.checked;
    }
    if (ele.type == "checkbox") {
      if (nodes && nodes.length >= 1) {
        var reply = [];
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes.item(i);
          if (node.type == ele.type) {
            if (node.checked) reply.push(node.value);
          }
        }
        return reply;
      }
      return ele.checked;
    }
    if (ele.type == "file") {
      return ele;
    }
    return ele.value;
  }

  if (dwr.util._isHTMLElement(ele, "textarea")) {
    return ele.value;
  }

  if (dwr.util._shouldEscapeHtml(options)) {
    if ("textContent" in ele) return ele.textContent;
    else if ("innerText" in ele) return ele.innerText;
  }
  return ele.innerHTML;
};

/**
 * getText() is like getValue() except that it reads the text (and not the value) from select elements
 * @see http://getahead.org/dwr/browser/util/gettext
 */
dwr.util.getText = function(ele) {
  ele = dwr.util._getElementById(ele, "getText()");
  if (ele == null) return null;
  if (!dwr.util._isHTMLElement(ele, "select")) {
    dwr.util._debug("getText() can only be used with select elements. Attempt to use: " + dwr.util._detailedTypeOf(ele) + " from  id: " + orig + ".");
    return "";
  }

  // This is a bit of a scam because it assumes single select
  // but I'm not sure how we should treat multi-select.
  var sel = ele.selectedIndex;
  if (sel != -1) {
    return ele.options[sel].text;
  }
  else {
    return "";
  }
};

/**
 * Given a map, or a recursive structure consisting of arrays and maps, call 
 * setValue() for all leaf entries and use intermediate levels to form nested
 * element ids.
 * @see http://getahead.org/dwr/browser/util/setvalues
 */
dwr.util.setValues = function(data, options) {
  var prefix = "";
  var depth = 100;
  if (options && "prefix" in options) prefix = options.prefix;
  if (options && "idPrefix" in options) prefix = options.idPrefix;
  if (options && "depth" in options) depth = options.depth;
  dwr.util._setValuesRecursive(data, prefix, depth, options);
};

/**
 * @private Recursive helper for setValues()
 */
dwr.util._setValuesRecursive = function(data, idpath, depth, options) {
  if (depth == 0) return;
  // Array containing objects -> add "[n]" to prefix and make recursive call
  // for each item object
  if (dwr.util._isArray(data) && data.length > 0 && dwr.util._isObject(data[0])) {
    for (var i = 0; i < data.length; i++) {
      dwr.util._setValuesRecursive(data[i], idpath+"["+i+"]", depth-1, options);
    }
  }
  // Object (not array) -> handle nested object properties
  else if (dwr.util._isObject(data) && !dwr.util._isArray(data)) {
    for (var prop in data) {
      var subidpath = idpath ? idpath+"."+prop : prop;
      // Object (not array), or array containing objects -> call ourselves recursively
      if (dwr.util._isObject(data[prop]) && !dwr.util._isArray(data[prop]) && !dwr.util._isDate(data[prop]) 
          || dwr.util._isArray(data[prop]) && data[prop].length > 0 && dwr.util._isObject(data[prop][0])) {
        dwr.util._setValuesRecursive(data[prop], subidpath, depth-1, options);
      }
      // Functions -> skip
      else if (typeof data[prop] == "function") {
        // NOP
      }
      // Only simple values left (or array of simple values, or empty array)
      // -> call setValue()
      else {
        // Are there any elements with that id or name
        if (dwr.util.byId(subidpath) != null || document.getElementsByName(subidpath).length >= 1) {
          dwr.util.setValue(subidpath, data[prop], options);
        }
      }
    }
  }
};

/**
 * Given a map, or a recursive structure consisting of arrays and maps, call 
 * getValue() for all leaf entries and use intermediate levels to form nested
 * element ids.
 * Given a string or element that refers to a form, create an object from the 
 * elements of the form.
 * @see http://getahead.org/dwr/browser/util/getvalues
 */
dwr.util.getValues = function(data, options) {
  if (typeof data == "string" || dwr.util._isHTMLElement(data)) {
    return dwr.util.getFormValues(data);
  }
  else {
    var prefix = "";
    var depth = 100;
    if (options != null && "prefix" in options) prefix = options.prefix;
    if (options != null && "idPrefix" in options) prefix = options.idPrefix;
    if (options != null && "depth" in options) depth = options.depth;
    dwr.util._getValuesRecursive(data, prefix, depth, options);
    return data;
  }
};

/**
 * Given a string or element that refers to a form, create an object from the 
 * elements of the form.
 * @see http://getahead.org/dwr/browser/util/getvalues
 */
dwr.util.getFormValues = function(eleOrNameOrId) {
  var ele = null;
  if (typeof eleOrNameOrId == "string") {
    ele = document.forms[eleOrNameOrId]; // arg is name
    if (ele == null) ele = dwr.util.byId(eleOrNameOrId); // arg is id
  }
  else if (dwr.util._isHTMLElement(eleOrNameOrId)) {
    ele = eleOrNameOrId; // arg is element
  }
  if (ele != null) {
    if (ele.elements == null) {
      alert("getFormValues() requires an object or reference to a form element.");
      return null;
    }
    var reply = {};
    var name;
    var value;
    for (var i = 0; i < ele.elements.length; i++) {
      if (ele[i].type in {button:0,submit:0,reset:0,image:0,file:0}) continue;
      if (ele[i].name) {
        name = ele[i].name;
        value = dwr.util.getValue(name);
      }
      else {
        if (ele[i].id) name = ele[i].id;
        else name = "element" + i;
        value = dwr.util.getValue(ele[i]);
      }
      reply[name] = value;
    }
    return reply;
  }
};

/**
 * @private Recursive helper for getValues().
 */
dwr.util._getValuesRecursive = function(data, idpath, depth, options) {
  if (depth == 0) return;
  // Array containing objects -> add "[n]" to idpath and make recursive call
  // for each item object
  if (dwr.util._isArray(data) && data.length > 0 && dwr.util._isObject(data[0])) {
    for (var i = 0; i < data.length; i++) {
      dwr.util._getValuesRecursive(data[i], idpath+"["+i+"]", depth-1, options);
    }
  }
  // Object (not array) -> handle nested object properties
  else if (dwr.util._isObject(data) && !dwr.util._isArray(data)) {
    for (var prop in data) {
      var subidpath = idpath ? idpath+"."+prop : prop;
      // Object, or array containing objects -> call ourselves recursively
      if (dwr.util._isObject(data[prop]) && !dwr.util._isArray(data[prop])
          || dwr.util._isArray(data[prop]) && data[prop].length > 0 && dwr.util._isObject(data[prop][0])) {
        dwr.util._getValuesRecursive(data[prop], subidpath, depth-1, options);
      }
      // Functions -> skip
      else if (typeof data[prop] == "function") {
        // NOP
      }
      // Only simple values left (or array of simple values, or empty array)
      // -> call getValue()
      else {
        // Are there any elements with that id or name
        if (dwr.util.byId(subidpath) != null || document.getElementsByName(subidpath).length >= 1) {
          data[prop] = dwr.util.getValue(subidpath);
        }
      }
    }
  }
};

/**
 * Add options to a list from an array or map.
 * @see http://getahead.org/dwr/browser/lists
 */
dwr.util.addOptions = function(ele, data/*, options*/) {
  ele = dwr.util._getElementById(ele, "addOptions()");
  if (ele == null) return;
  var useOptions = dwr.util._isHTMLElement(ele, "select");
  var useLi = dwr.util._isHTMLElement(ele, ["ul", "ol"]);
  if (!useOptions && !useLi) {
    dwr.util._debug("addOptions() can only be used with select/ul/ol elements. Attempt to use: " + dwr.util._detailedTypeOf(ele));
    return;
  }
  if (data == null) return;
  
  var argcount = arguments.length;
  var options = {};
  var lastarg = arguments[argcount - 1]; 
  if (argcount > 2 && dwr.util._isObject(lastarg)) {
    options = lastarg;
    argcount--;
  }
  var arg3 = null; if (argcount >= 3) arg3 = arguments[2];
  var arg4 = null; if (argcount >= 4) arg4 = arguments[3];
  if (!options.optionCreator && useOptions) options.optionCreator = dwr.util._defaultOptionCreator;
  if (!options.optionCreator && useLi) options.optionCreator = dwr.util._defaultListItemCreator;
  options.document = ele.ownerDocument;

  var text, value, li;
  if (dwr.util._isArray(data)) {
    // Loop through the data that we do have
    for (var i = 0; i < data.length; i++) {
      options.data = data[i];
      options.text = null;
      options.value = null;
      if (useOptions) {
        if (arg3 != null) {
          if (arg4 != null) {
            options.text = dwr.util._getValueFrom(data[i], arg4);
            options.value = dwr.util._getValueFrom(data[i], arg3);
          }
          else options.text = options.value = dwr.util._getValueFrom(data[i], arg3);
        }
        else options.text = options.value = dwr.util._getValueFrom(data[i]);

        if (options.text != null || options.value) {
          var opt = options.optionCreator(options);
          opt.text = options.text;
          opt.value = options.value;
          ele.options[ele.options.length] = opt;
        }
      }
      else {
        options.value = dwr.util._getValueFrom(data[i], arg3);
        if (options.value != null) {
          li = options.optionCreator(options);
          if (dwr.util._shouldEscapeHtml(options)) {
            options.value = dwr.util.escapeHtml(options.value);
          }
          li.innerHTML = options.value;
          ele.appendChild(li);
        }
      }
    }
  }
  else if (arg4 != null) {
    if (!useOptions) {
      alert("dwr.util.addOptions can only create select lists from objects.");
      return;
    }
    for (var prop in data) {
      options.data = data[prop];
      options.value = dwr.util._getValueFrom(data[prop], arg3);
      options.text = dwr.util._getValueFrom(data[prop], arg4);

      if (options.text != null || options.value) {
        var opt = options.optionCreator(options);
        opt.text = options.text;
        opt.value = options.value;
        ele.options[ele.options.length] = opt;
      }
    }
  }
  else {
    if (!useOptions) {
      dwr.util._debug("dwr.util.addOptions can only create select lists from objects.");
      return;
    }
    for (var prop in data) {
      if (typeof data[prop] == "function") continue;
      options.data = data[prop];
      if (arg3 == null) {
        options.value = prop;
        options.text = data[prop];
      }
      else {
        options.value = data[prop];
        options.text = prop;
      }
      if (options.text != null || options.value) {
        var opt = options.optionCreator(options);
        opt.text = options.text;
        opt.value = options.value;
        ele.options[ele.options.length] = opt;
      }
    }
  }

  // All error routes through this function result in a return, so highlight now
  dwr.util.highlight(ele, options); 
};

/**
 * @private Get the data from an array function for dwr.util.addOptions
 */
dwr.util._getValueFrom = function(data, method) {
  if (method == null) return data;
  else if (typeof method == 'function') return method(data);
  else return data[method];
};

/**
 * @private Default option creation function
 */
dwr.util._defaultOptionCreator = function(options) {
  return options.document.createElement("option");
};

/**
 * @private Default list item creation function
 */
dwr.util._defaultListItemCreator = function(options) {
  return options.document.createElement("li");
};

/**
 * Remove all the options from a select list (specified by id)
 * @see http://getahead.org/dwr/browser/lists
 */
dwr.util.removeAllOptions = function(ele) {
  ele = dwr.util._getElementById(ele, "removeAllOptions()");
  if (ele == null) return;
  var useOptions = dwr.util._isHTMLElement(ele, "select");
  var useLi = dwr.util._isHTMLElement(ele, ["ul", "ol"]);
  if (!useOptions && !useLi) {
    dwr.util._debug("removeAllOptions() can only be used with select, ol and ul elements. Attempt to use: " + dwr.util._detailedTypeOf(ele));
    return;
  }
  if (useOptions) {
    ele.options.length = 0;
  }
  else {
    while (ele.childNodes.length > 0) {
      ele.removeChild(ele.firstChild);
    }
  }
};

/**
 * Create rows inside a the table, tbody, thead or tfoot element (given by id).
 * @see http://getahead.org/dwr/browser/tables
 */
dwr.util.addRows = function(ele, data, cellFuncs, options) {
  ele = dwr.util._getElementById(ele, "addRows()");
  if (ele == null) return;
  if (!dwr.util._isHTMLElement(ele, ["table", "tbody", "thead", "tfoot"])) {
    dwr.util._debug("addRows() can only be used with table, tbody, thead and tfoot elements. Attempt to use: " + dwr.util._detailedTypeOf(ele));
    return;
  }
  if (!options) options = {};
  if (!options.rowCreator) options.rowCreator = dwr.util._defaultRowCreator;
  if (!options.cellCreator) options.cellCreator = dwr.util._defaultCellCreator;
  options.document = ele.ownerDocument;
  var tr, rowNum;
  if (dwr.util._isArray(data)) {
    for (rowNum = 0; rowNum < data.length; rowNum++) {
      options.rowData = data[rowNum];
      options.rowIndex = rowNum;
      options.rowNum = rowNum;
      options.data = null;
      options.cellNum = -1;
      tr = dwr.util._addRowInner(cellFuncs, options);
      if (tr != null) ele.appendChild(tr);
    }
  }
  else if (typeof data == "object") {
    rowNum = 0;
    for (var rowIndex in data) {
      options.rowData = data[rowIndex];
      options.rowIndex = rowIndex;
      options.rowNum = rowNum;
      options.data = null;
      options.cellNum = -1;
      tr = dwr.util._addRowInner(cellFuncs, options);
      if (tr != null) ele.appendChild(tr);
      rowNum++;
    }
  }

  dwr.util.highlight(ele, options);
};

/**
 * @private The contents we put in empty table cells to workaround IE's border bug.
 */
dwr.util._emptyTableCellReplacement = "<div style='width:0;height:0;overflow:hidden;'></div>";

/**
 * @private Internal function to draw a single row of a table.
 */
dwr.util._addRowInner = function(cellFuncs, options) {
  var tr = options.rowCreator(options);
  if (tr == null) return null;
  for (var cellNum = 0; cellNum < cellFuncs.length; cellNum++) {
    var func = cellFuncs[cellNum];
    if (typeof func == 'function') options.data = func(options.rowData, options);
    else options.data = func || "";
    options.cellNum = cellNum;
    var td = options.cellCreator(options);
    if (td != null) {
      if ("data" in options) {
        if (dwr.util._isHTMLElement(options.data)) td.appendChild(options.data);
        else {
          if (dwr.util._shouldEscapeHtml(options) && typeof(options.data) == "string") {
            td.innerHTML = dwr.util.escapeHtml(options.data);
          }
          else {
            td.innerHTML = options.data;
          }
        }
      }
      else {
        td.innerHTML = dwr.util._emptyTableCellReplacement;
      }
      tr.appendChild(td);
    }
  }
  return tr;
};

/**
 * @private Default row creation function
 */
dwr.util._defaultRowCreator = function(options) {
  return options.document.createElement("tr");
};

/**
 * @private Default cell creation function
 */
dwr.util._defaultCellCreator = function(options) {
  return options.document.createElement("td");
};

/**
 * Remove all the children of a given node.
 * @see http://getahead.org/dwr/browser/tables
 */
dwr.util.removeAllRows = function(ele, options) {
  ele = dwr.util._getElementById(ele, "removeAllRows()");
  if (ele == null) return;
  if (!options) options = {};
  if (!options.filter) options.filter = function() { return true; };
  if (!dwr.util._isHTMLElement(ele, ["table", "tbody", "thead", "tfoot"])) {
    dwr.util._debug("removeAllRows() can only be used with table, tbody, thead and tfoot elements. Attempt to use: " + dwr.util._detailedTypeOf(ele));
    return;
  }
  var child = ele.firstChild;
  var next;
  while (child != null) {
    next = child.nextSibling;
    if (options.filter(child)) {
      ele.removeChild(child);
    }
    child = next;
  }
};

/**
 * dwr.util.byId(ele).className = "X", that we can call from Java easily.
 */
dwr.util.setClassName = function(ele, className) {
  ele = dwr.util._getElementById(ele, "setClassName()");
  if (ele == null) return;
  ele.className = className;
};

/**
 * dwr.util.byId(ele).className += "X", that we can call from Java easily.
 */
dwr.util.addClassName = function(ele, className) {
  ele = dwr.util._getElementById(ele, "addClassName()");
  if (ele == null) return;
  ele.className += " " + className;
};

/**
 * dwr.util.byId(ele).className -= "X", that we can call from Java easily
 * From code originally by Gavin Kistner
 */
dwr.util.removeClassName = function(ele, className) {
  ele = dwr.util._getElementById(ele, "removeClassName()");
  if (ele == null) return;
  var regex = new RegExp("(^|\\s)" + className + "(\\s|$)", 'g');
  ele.className = ele.className.replace(regex, '');
};

/**
 * dwr.util.byId(ele).className |= "X", that we can call from Java easily.
 */
dwr.util.toggleClassName = function(ele, className) {
  ele = dwr.util._getElementById(ele, "toggleClassName()");
  if (ele == null) return;
  var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
  if (regex.test(ele.className)) {
    ele.className = ele.className.replace(regex, '');
  }
  else {
    ele.className += " " + className;
  }
};

/**
 * Clone a node and insert it into the document just above the 'template' node
 * @see http://getahead.org/dwr/???
 */
dwr.util.cloneNode = function(ele, options) {
  ele = dwr.util._getElementById(ele, "cloneNode()");
  if (ele == null) return null;
  if (options == null) options = {};
  var clone = ele.cloneNode(true);
  if ("idPrefix" in options || "idSuffix" in options) {
    dwr.util._updateIds(clone, options);
  }
  else {
    dwr.util._removeIds(clone);
  }
  ele.parentNode.insertBefore(clone, ele);
  return clone;
};

/**
 * @private Update all of the ids in an element tree
 */
dwr.util._updateIds = function(ele, options) {
  if (options == null) options = {};
  if (dwr.util._getId(ele)) {
    ele.setAttribute("id", 
      ("idPrefix" in options ? options.idPrefix : "") 
      + dwr.util._getId(ele) 
      + ("idSuffix" in options ? options.idSuffix : ""));
  }
  var children = ele.childNodes;
  for (var i = 0; i < children.length; i++) {
    var child = children.item(i);
    if (child.nodeType == 1 /*Node.ELEMENT_NODE*/) {
      dwr.util._updateIds(child, options);
    }
  }
};

/**
 * @private Remove all the Ids from an element
 */
dwr.util._removeIds = function(ele) {
  if (dwr.util._getId(ele)) ele.removeAttribute("id");
  var children = ele.childNodes;
  for (var i = 0; i < children.length; i++) {
    var child = children.item(i);
    if (child.nodeType == 1 /*Node.ELEMENT_NODE*/) {
      dwr.util._removeIds(child);
    }
  }
};

/**
 * Clone a template node and its embedded template child nodes according to
 * cardinalities (of arrays) in supplied data.  
 */
dwr.util.cloneNodeForValues = function(templateEle, data, options) {
  templateEle = dwr.util._getElementById(templateEle, "cloneNodeForValues()");
  if (templateEle == null) return null;
  if (options == null) options = {};
  var idpath;
  if (options.idPrefix != null)
    idpath = options.idPrefix;
  else
    idpath = dwr.util._getId(templateEle) || ""; 
  return dwr.util._cloneNodeForValuesRecursive(templateEle, data, idpath, options);
};

/**
 * @private Recursive helper for cloneNodeForValues(). 
 */
dwr.util._cloneNodeForValuesRecursive = function(templateEle, data, idpath, options) {
  // Incoming array -> make an id for each item and call clone of the template 
  // for each of them
  if (dwr.util._isArray(data)) {
    var clones = [];
    for (var i = 0; i < data.length; i++) {
      var item = data[i];
      var clone = dwr.util._cloneNodeForValuesRecursive(templateEle, item, idpath + "[" + i + "]", options);
      clones.push(clone);
    }
    return clones;
  }
  else
  // Incoming object (not array) -> clone the template, add id prefixes, add 
  // clone to DOM, and then recurse into any array properties if they contain 
  // objects and there is a suitable template
  if (dwr.util._isObject(data) && !dwr.util._isArray(data)) {
    var clone = templateEle.cloneNode(true);
    if (options.updateCloneStyle && clone.style) {
      for (var propname in options.updateCloneStyle) {
        clone.style[propname] = options.updateCloneStyle[propname];
      }
    }
    dwr.util._replaceIds(clone, dwr.util._getId(templateEle), idpath);
    templateEle.parentNode.insertBefore(clone, templateEle);
    dwr.util._cloneSubArrays(data, idpath, options);
    return clone;
  }

  // It is an error to end up here so we return nothing
  return null;
};

/**
 * @private Substitute a leading idpath fragment with another idpath for all 
 * element ids tree, and remove ids that don't match the idpath. 
 */
dwr.util._replaceIds = function(ele, oldidpath, newidpath) {
  var currId = dwr.util._getId(ele);
  if (currId) {
    var newId = null;
    if (currId == oldidpath) {
      newId = newidpath;
    }
    else if (currId.length > oldidpath.length) {
      if (currId.substr(0, oldidpath.length) == oldidpath) {
        var trailingChar = currId.charAt(oldidpath.length);
        if (trailingChar == "." || trailingChar == "[") {
          newId = newidpath + currId.substr(oldidpath.length);
        }
      }
    }
    if (newId) {
      ele.setAttribute("id", newId);
    }
    else {
      ele.removeAttribute("id");
    }
  }
  var children = ele.childNodes;
  for (var i = 0; i < children.length; i++) {
    var child = children.item(i);
    if (child.nodeType == 1 /*Node.ELEMENT_NODE*/) {
      dwr.util._replaceIds(child, oldidpath, newidpath);
    }
  }
};

/**
 * @private Finds arrays in supplied data and uses any corresponding template 
 * node to make a clone for each item in the array. 
 */
dwr.util._cloneSubArrays = function(data, idpath, options) {
  for (prop in data) {
    var value = data[prop];
    // Look for potential recursive cloning in all array properties
    if (dwr.util._isArray(value)) {
      // Only arrays with objects are interesting for cloning
      if (value.length > 0 && dwr.util._isObject(value[0])) {
        var subTemplateId = idpath + "." + prop;
        var subTemplateEle = dwr.util.byId(subTemplateId);
        if (subTemplateEle != null) {
          dwr.util._cloneNodeForValuesRecursive(subTemplateEle, value, subTemplateId, options);
        }
      }
    }
    // Continue looking for arrays in object properties
    else if (dwr.util._isObject(value)) {
      dwr.util._cloneSubArrays(value, idpath + "." + prop, options);
    }
  }
};

/**
 * @private Helper to turn a string into an element with an error message
 */
dwr.util._getElementById = function(ele, source) {
  var orig = ele;
  ele = dwr.util.byId(ele);
  if (ele == null) {
    dwr.util._debug(source + " can't find an element with id: " + orig + ".");
  }
  return ele;
};

/**
 * @private Is the given node an HTML element (optionally of a given type)?
 * @param ele The element to test
 * @param nodeName eg "input", "textarea" - check for node name (optional)
 *         if nodeName is an array then check all for a match.
 */
dwr.util._isHTMLElement = function(ele, nodeName) {
  if (ele == null || typeof ele != "object" || ele.nodeName == null) {
    return false;
  }
  if (nodeName != null) {
    var test = ele.nodeName.toLowerCase();
    if (typeof nodeName == "string") {
      return test == nodeName.toLowerCase();
    }
    if (dwr.util._isArray(nodeName)) {
      var match = false;
      for (var i = 0; i < nodeName.length && !match; i++) {
        if (test == nodeName[i].toLowerCase()) {
          match =  true;
        }
      }
      return match;
    }
    dwr.util._debug("dwr.util._isHTMLElement was passed test node name that is neither a string or array of strings");
    return false;
  }
  return true;
};

/**
 * @private Like typeOf except that more information for an object is returned other than "object"
 */
dwr.util._detailedTypeOf = function(x) {
  var reply = typeof x;
  if (reply == "object") {
    reply = Object.prototype.toString.apply(x); // Returns "[object class]"
    reply = reply.substring(8, reply.length-1);  // Just get the class bit
  }
  return reply;
};

/**
 * @private Object detector. Excluding null from objects.
 */
dwr.util._isObject = function(data) {
  return (data && typeof data == "object");
};

/**
 * @private Array detector. Note: instanceof doesn't work with multiple frames.
 */
dwr.util._isArray = function(data) {
  return (data && Object.prototype.toString.call(data)=="[object Array]");
};

/**
 * @private Array like detector. Note: instanceof doesn't work with multiple frames.
 */
dwr.util._isArrayLike = function(data) {
  return data 
    && (typeof data.length == "number") // must have .length
    && ((data.propertyIsEnumerable && data.propertyIsEnumerable("length")==false) || !data.constructor || data!="[object Object]") // .length must be native prop
    && !dwr.util._isString(data) // don't be fooled by string
    && !dwr.util._isFunction(data) // don't be fooled by function
    && !data.tagName; // don't be fooled by elements
};

/**
 * @private String detector.
 */
dwr.util._isString = function(data) {
  return (data && (typeof data == "string" || Object.prototype.toString.call(data) == "[object String]"));
};

/**
 * @private Function detector.
 */
dwr.util._isFunction = function(data) {
  return (data && (typeof data == "function" || Object.prototype.toString.call(data) == "[object Function]") 
    && data != "[object NodeList]"); // need to workaround NodeList on Safari
};

/**
 * @private Date detector. Note: instanceof doesn't work with multiple frames.
 */
dwr.util._isDate = function(data) {
  return (data && Object.prototype.toString.call(data)=="[object Date]");
};

/**
 * @private Used by setValue. Gets around the missing functionallity in IE.
 */
dwr.util._importNode = function(doc, importedNode, deep) {
  var newNode;

  if (importedNode.nodeType == 1 /*Node.ELEMENT_NODE*/) {
    newNode = doc.createElement(importedNode.nodeName);

    for (var i = 0; i < importedNode.attributes.length; i++) {
      var attr = importedNode.attributes[i];
      if (attr.nodeValue != null && attr.nodeValue != '') {
        newNode.setAttribute(attr.name, attr.nodeValue);
      }
    }

    if (importedNode.style != null) {
      newNode.style.cssText = importedNode.style.cssText;
    }
  }
  else if (importedNode.nodeType == 3 /*Node.TEXT_NODE*/) {
    newNode = doc.createTextNode(importedNode.nodeValue);
  }

  if (deep && importedNode.hasChildNodes()) {
    for (i = 0; i < importedNode.childNodes.length; i++) {
      newNode.appendChild(dwr.util._importNode(doc, importedNode.childNodes[i], true));
    }
  }

  return newNode;
};

/** @private Used internally when some message needs to get to the programmer */
dwr.util._debug = function(message, stacktrace) {
  var written = false;
  try {
    if (window.console) {
      if (stacktrace && window.console.trace) window.console.trace();
      window.console.log(message);
      written = true;
    }
    else if (window.opera && window.opera.postError) {
      window.opera.postError(message);
      written = true;
    }
  }
  catch (ex) { /* ignore */ }

  if (!written) {
    var debug = document.getElementById("dwr-debug");
    if (debug) {
      var contents = message + "<br/>" + debug.innerHTML;
      if (contents.length > 2048) contents = contents.substring(0, 2048);
      debug.innerHTML = contents;
    }
  }
};


/*
 * Copyright 2005 Joe Walker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * The DWR object is also defined by dwr.util etc.
 */
if (typeof dwr == 'undefined') dwr = {};

(function() {
  if (!dwr.engine) dwr.engine = {};

  /**
    * Set an alternative error handler from the default alert box.
    * @param {Function} handler The function to call when an error happens
    * @see http://getahead.org/dwr/browser/engine/errors
    */
  dwr.engine.setErrorHandler = function(handler) {
    dwr.engine._errorHandler = handler;
  };

  /**
    * Set an alternative warning handler from the default alert box.
    * @param {Function} handler The function to call when a warning happens
    * @see http://getahead.org/dwr/browser/engine/errors
    */
  dwr.engine.setWarningHandler = function(handler) {
    dwr.engine._warningHandler = handler;
  };

  /**
    * Setter for the text/html handler - what happens if a DWR request gets an HTML
    * reply rather than the expected Javascript. Often due to login timeout
    * @param {Function} handler The function to call on an unexpected text/html content type
    */
  dwr.engine.setTextHtmlHandler = function(handler) {
    dwr.engine._textHtmlHandler = handler;
  };

  dwr.engine.setPollStatusHandler = function(handler) {
    dwr.engine._pollStatusHandler = handler;
  };

  /**
    * Set a default timeout value for all calls. 0 (the default) turns timeouts off.
    * @param {Function} handler The function to call when we get bored of waiting for a call
    * @see getahead.org/dwr/browser/engine/errors
    */
  dwr.engine.setTimeout = function(timeout) {
    dwr.engine._timeout = timeout;
  };

  /**
    * The Pre-Hook is called before any DWR remoting is done.
    * @param {Function} handler The function to call before any remote calls
    * @see getahead.org/dwr/browser/engine/hooks
    */
  dwr.engine.setPreHook = function(handler) {
    dwr.engine._preHook = handler;
  };

  /**
   * The Post-Hook is called after any DWR remoting is done.
   * @param {Function} handler The function to call after any remote calls
   * @see getahead.org/dwr/browser/engine/hooks
   */
  dwr.engine.setPostHook = function(handler) {
    dwr.engine._postHook = handler;
  };

  /**
   * Custom headers for all DWR calls
   * @param {Object} headers Object containing name/value pairs for extra headers
   * @see getahead.org/dwr/????
   */
  dwr.engine.setHeaders = function(headers) {
    dwr.engine._headers = headers;
  };

  /**
   * Custom attributes transferred to server for all DWR calls
   * @param {Object} attributes Object containing name/value pairs for attributes
   * @see getahead.org/dwr/????
   */
  dwr.engine.setAttributes = function(attributes) {
    dwr.engine._attributes = attributes;
  };

  /**
   * Ensure that remote calls happen in the order in which they were sent? (Default: false)
   * @param {boolean} ordered true to enable ordered processing
   * @see getahead.org/dwr/browser/engine/ordering
   */
  dwr.engine.setOrdered = function(ordered) {
    dwr.engine._ordered = ordered;
  };

  /**
   * Do we ask the XHR object to be asynchronous? (Default: true)
   * Warning: it is <strong>highly</strong> advised to use the default of async
   * processing, especially when dealing with Internet based requests.
   * @param {boolean} async false to enable sync processing for XHR queries
   * @see getahead.org/dwr/browser/engine/options
   */
  dwr.engine.setAsync = function(async) {
    dwr.engine._async = async;
  };

  /**
   * Does the client actively check the server for updates? (Default: false)
   * @param {boolean} async true to enable low latency reverse ajax
   * @see getahead.org/dwr/browser/engine/options
   */
  dwr.engine.setActiveReverseAjax = function(activeReverseAjax) {
    if (activeReverseAjax) {
      // Bail if we are already started
      if (dwr.engine._activeReverseAjax) return;
      // We always want a retry policy when reverse AJAX is enabled.
      dwr.engine._retryIntervals = dwr.engine._defaultRetryIntervals; 
      dwr.engine._activeReverseAjax = true;
      dwr.engine._poll();
    }
    else {
      // Can we cancel an existing request?
      if (dwr.engine._activeReverseAjax && dwr.engine._pollBatch) {
        dwr.engine.transport.abort(dwr.engine._pollBatch);
      }
      dwr.engine._activeReverseAjax = false;
    }
    // TODO: in iframe mode, if we start, stop, start then the second start may
    // well kick off a second iframe while the first is still about to return
    // we should cope with this but we don't
  };

  /**
   * Turn server notification of page unload on and off
   * @param {boolean} notify true or false depending on if we want to turn unload on or off
   * @see getahead.org/dwr/browser/engine/options
   */
  dwr.engine.setNotifyServerOnPageUnload = function(notify, asyncUnload) {
    dwr.engine._asyncUnload = (asyncUnload !== undefined) ? asyncUnload : false;
    dwr.engine._isNotifyServerOnPageUnload = notify;
  };

  /*
   * The maximum number of retries, when reached we will no longer attempt to check for server status.
   * @param - maxRetries
   */
  dwr.engine.setMaxRetries = function(maxRetries) {
    dwr.engine._maxRetries = maxRetries;
  };

  /*
   * The intervals between successive retries in seconds
   * @param - array of integers representing the retry interval in seconds.
   */
  dwr.engine.setRetryIntervals = function(intervalsArray) {
    dwr.engine._retryIntervals = intervalsArray;
  };

  /**
   * The default message handler.
   * @param {String} message The text of the error message
   * @param {Object} ex An error object containing at least a name and message
   * @see getahead.org/dwr/browser/engine/errors
   */
  dwr.engine.defaultErrorHandler = function(message, ex) {
    dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true);
    if (message == null || message == "") alert("A server error has occurred.");
    // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
    else if (message.indexOf("0x80040111") != -1) dwr.engine._debug(message);
    else alert(message);
  };

  /**
   * The default warning handler.
   * @param {String} message The text of the error message
   * @param {Object} ex An error object containing at least a name and message
   * @see getahead.org/dwr/browser/engine/errors
   */
  dwr.engine.defaultWarningHandler = function(message, ex) {
    dwr.engine._debug(message);
  };

  /**
   * The default poll status handler.
   * @param {boolean} newStatus - true = online, false = offline
   * @param {object} ex - The exception if one exists (offline).
   * @see getahead.org/dwr/browser/engine/errors
   */
  dwr.engine.defaultPollStatusHandler = function(newStatus, ex) {
    dwr.engine._debug("pollStatusHandler - online: " + newStatus);    
  };

  /**
   * For reduced latency you can group several remote calls together using a batch.
   * @see getahead.org/dwr/browser/engine/batch
   */
  dwr.engine.beginBatch = function() {
    if (dwr.engine._batch) {
      dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" });
      return;
    }
    dwr.engine._batch = dwr.engine.batch.create();
  };

  /**
   * Finished grouping a set of remote calls together. Go and execute them all.
   * @param {Object} options A options object to customize processing
   * @see getahead.org/dwr/browser/engine/batch
   */
  dwr.engine.endBatch = function(options) {
    var batch = dwr.engine._batch;
    if (batch == null) {
      dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" });
      return;
    }
    dwr.engine._batch = null;
    if (batch.map.callCount == 0) {
      return;
    }

    // The hooks need to be merged carefully to preserve ordering
    if (options) {
      dwr.engine.batch.merge(batch, options);
    }

    // In ordered mode, we don't send unless the list of sent items is empty
    // (queuing is not possible for sync calls so let them slip through)
    if (batch.async && (dwr.engine._ordered || dwr.engine._internalOrdered) && dwr.engine._batchesLength != 0) {
      dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch;
    }
    else {
      return dwr.engine.transport.send(batch);
    }
  };

  /**
   * For use with file downloads. When a DWR function returns a binary download
   * you can prompt the user to save it using this function
   * @param {Object} data The binary data passed from DWR
   */
  dwr.engine.openInDownload = function(data) {
    var div = document.createElement("div");
    document.body.appendChild(div);
    div.innerHTML = "<iframe width='0' height='0' scrolling='no' frameborder='0' src='" + data + "'></iframe>";
  };

  /**
   * What is the current version DWR number
   * DWR version numbers are of the form "Version 1.2.3.3128[.beta]", where:
   * 1 is the major release number. Changes in major version number indicate
   * significant enhancements in functionality
   * 2 is the minor release number. Changes in minor version number indicate
   * less significant changes in functionality
   * 3 is the revision release number. Changes here typically indicate bug
   * fixes only
   * 3128 is the build number. This number increments for each build
   * .beta is a release title that is generally only used for non production
   * releases to indicate the purpose/quality of the release
   * The label is these strings concatenated
   */
  dwr.version = {
    /**
     * Changes in major version number indicate significant enhancements
     */
    major:parseInt("3"),

    /**
     * Changes in minor version number indicate smaller enhancements
     */
    minor:parseInt("0"),

    /**
     * Changes with the revision number typically indicate bug-fixes only
     */
    revision:parseInt("0"),

    /**
     * The build number increments for each build
     */
    build:parseInt("312"),

    /**
     * Only used for non production releases to indicate the purpose/quality of
     * the release. Example titles include 'milestone1' or 'beta3'.
     */
    title:"RC2-final",

    /**
     * The strings above concatenated
     */
    label:"3.0.0-RC2-final-312"
  };

  //==============================================================================
  // Only private stuff below here
  //==============================================================================

  /** Is GET enabled for the benefit of Safari? */
  dwr.engine._allowGetForSafariButMakeForgeryEasier = "false";

  /** The script prefix to strip in the case of scriptTagProtection. */
  dwr.engine._scriptTagProtection = "throw 'allowScriptTagRemoting is false.';";

  /** The default path to the DWR servlet 
   *  pathToDwrServlet is aids cross-domain. If pathToDwrServlet
   *  is defined before engine.js is included pathToDwrServlet will
   *  be used.
   */
  if (typeof pathToDwrServlet != "undefined") {
    dwr.engine._pathToDwrServlet = pathToDwrServlet;
  }
  else {
    dwr.engine._pathToDwrServlet = "/WebProject.war/dwr";
  }

  /** The webapp's context path (used for setting cookie path) */
  dwr.engine._contextPath = "/WebProject.war";

  /** Do we use XHR for reverse ajax because we are not streaming? */
  dwr.engine._pollWithXhr = "false";

  dwr.engine._pollOnline = true;  

  /** These URLs can be configured from the server */
  dwr.engine._ModePlainCall = "/call/plaincall/";
  dwr.engine._ModePlainPoll = "/call/plainpoll/";
  dwr.engine._ModeHtmlCall = "/call/htmlcall/";
  dwr.engine._ModeHtmlPoll = "/call/htmlpoll/";

  /** Do we make the calls async? Default to 'true' */
  dwr.engine._async = Boolean("true");

  /** The local page id */
  dwr.engine._pageId = null;

  /** The browser instance id */
  dwr.engine._dwrSessionId = null;

  /** The global page id (browser instance + local page id) */
  dwr.engine._scriptSessionId = ""; // we must use "" and not null

  /** A function to be called before requests are marshalled. Can be null. */
  dwr.engine._preHook = null;

  /** A function to be called after replies are received. Can be null. */
  dwr.engine._postHook = null;

  /** A map of the batches that we have sent and are awaiting a reply on. */
  dwr.engine._batches = {};

  /** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */
  dwr.engine._batchesLength = 0;

  /** In ordered mode, the array of batches waiting to be sent */
  dwr.engine._batchQueue = [];

  /** User setting for saying that calls should execute one by one in the order in which they were sent */
  dwr.engine._ordered = false;

  /** Internal state forcing calls to execute one by one in the order in which they were sent */
  dwr.engine._internalOrdered = false;

  /** The current batch (if we are in batch mode) */
  dwr.engine._batch = null;

  /** The global timeout */
  dwr.engine._timeout = 0;

  /** Are we doing comet or polling? */
  dwr.engine._activeReverseAjax = false;

  /** The batch that we are using to poll */
  dwr.engine._pollBatch = null;

  /** How many milliseconds between internal comet polls */
  dwr.engine._pollCometInterval = 200;

  /** Default SSL secure URL used for iframe src */
  dwr.engine.SSL_SECURE_URL = "about:blank";

  /** How many times have we re-tried a call? */
  dwr.engine._retries = 0;
  dwr.engine._maxRetries = -1; // Unlimited retries

  /** The intervals between successive retries in seconds */
  dwr.engine._retryIntervals = [];

  /** Used as the default for reverse ajax/polling 
   *  Retry immediately twice with one second intervals between, then go offline.
   *  Retry every 10 seconds when offline.
   */
  dwr.engine._defaultRetryIntervals = [ 1, 1, 10 ];

  /** Do we do a document.reload if we get a text/html reply? */
  dwr.engine._textHtmlHandler = null;

  /** If you wish to send custom headers with every request */
  dwr.engine._headers = null;

  /** If you wish to send extra custom request attributes with each request */
  dwr.engine._attributes = null;

  /** Batch ids allow us to know which batch the server is answering */
  dwr.engine._nextBatchId = 0;

  /** The instance id helps us distinguish multiple engine.js loaded in one page */
  dwr.engine._instanceId = -1;

  /** A list of the properties that need merging from calls to a batch */
  dwr.engine._propnames = [ "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ];

  /** Do we stream, or can be hacked to do so? */
  dwr.engine._partialResponseNo = 0;
  dwr.engine._partialResponseYes = 1;
  dwr.engine._partialResponseFlush = 2;

  /** Are we doing page unloading? */
  dwr.engine._isNotifyServerOnPageUnload = false;
  
  /** Should the unload call be asynchronous?  If true it may not be called by the browser. */
  dwr.engine._asyncUnload = false;
  
  /**
   * A map of all mapped classes whose class declarations have been loaded
   * (dwrClassName -> constructor function)
   */
  dwr.engine._mappedClasses = {};

  /** A function to call if something fails. */
  dwr.engine._errorHandler = dwr.engine.defaultErrorHandler;

  /** For debugging when something unexplained happens. */
  dwr.engine._warningHandler = dwr.engine.defaultWarningHandler;

  dwr.engine._pollStatusHandler = dwr.engine.defaultPollStatusHandler;

  /** Undocumented interceptors - do not use */
  dwr.engine._postSeperator = "\n";
  dwr.engine._defaultInterceptor = function(data) { return data; };
  dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor;
  dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor;
  dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor;

  /** Object attributes to ignore when serializing */
  dwr.engine._excludeObjectAttributes = {
    "$dwrClassName": true,
    "$dwrByRef": true,
    "$_dwrConversionRef": true
  };

  /** Is this page in the process of unloading? */
  dwr.engine._unloading = false;

  /** @private Abort any XHRs in progress at page unload (solves zombie socket problems in IE). */
  dwr.engine._unloader = function() {
    dwr.engine._unloading = true;

    // Empty queue of waiting ordered requests
    dwr.engine._batchQueue.length = 0;

    // Abort any ongoing XHRs and clear their batches
    var batch;
    for (var batchId in dwr.engine._batches) {
      batch = dwr.engine._batches[batchId];
      // Only process objects that look like batches (avoid prototype additions!)
      if (batch && batch.map) {
        if (batch.req) {
          batch.req.abort();
        }
      }
    }

    // If we have used reverse ajax then we try to tell the server we are gone
    if (dwr.engine._isNotifyServerOnPageUnload) {
      dwr.engine._debug("calling unloader for: " + dwr.engine._scriptSessionId);
      batch = {
        map:{
          callCount:1,
          'c0-scriptName':'__System',
          'c0-methodName':'pageUnloaded',
          'c0-id':0
        },
        paramCount:0, isPoll:false, async:dwr.engine._asyncUnload,
        headers:{}, preHooks:[], postHooks:[],
        timeout:dwr.engine._timeout,
        errorHandler:null, warningHandler:null, textHtmlHandler:null,
        path:dwr.engine._pathToDwrServlet,
        handlers:[{ exceptionHandler:null, callback:null }]
      };
      dwr.engine.transport.send(batch);
      dwr.engine._isNotifyServerOnPageUnload = false;
    }
  };

  function ignoreIfUnloading(batch, f) {
    if (dwr.engine._unloading) return;
    if (batch && batch.async == false) {
      // Sync calls are reported synchronously
      f();
    }
    else {
      // We delay error reporting for async calls to see if maybe unloading just started
      setTimeout(function() {
        if (dwr.engine._unloading) return;
        f();
      }, 100);
    }
  }

  /**
   * Send a request. Called by the JavaScript interface stub
   * @private
   * @param path part of URL after the host and before the exec bit without leading or trailing /s
   * @param scriptName The class to execute
   * @param methodName The method on said class to execute
   * @param func The callback function to which any returned data should be passed
   *       if this is null, any returned data will be ignored
   * @param args The parameters to passed to the above method
   */
  dwr.engine._execute = function(path, scriptName, methodName, args) {
    var singleShot = false;
    if (dwr.engine._batch == null) {
      dwr.engine.beginBatch();
      singleShot = true;
    }

    var batch = dwr.engine._batch;
    // All the paths MUST be to the same servlet
    if (batch.path == null) {
      batch.path = path;
    }
    else {
      if (batch.path != path) {
        dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." });
        return;
      }
    }

    dwr.engine.batch.addCall(batch, scriptName, methodName, args);

    // Now we have finished remembering the call, we increment the call count
    batch.map.callCount++;
    if (singleShot) {
      return dwr.engine.endBatch();
    }
  };

  /**
   * Poll the server to see if there is any data waiting
   * @private
   */
  dwr.engine._poll = function() {
    if (!dwr.engine._activeReverseAjax) {
      return;
    }
    dwr.engine._pollBatch = dwr.engine.batch.createPoll();
    dwr.engine.transport.send(dwr.engine._pollBatch);
  }; 

  /** @private This is a hack to make the context be this window */
  dwr.engine._eval = function(script) {
    if (script == null) {
      return null;
    }
    if (script == "") {
      dwr.engine._debug("Warning: blank script", true);
      return null;
    }
    // dwr.engine._debug("Exec: [" + script + "]", true);
    return eval(script);
  };

  /** @private call all the post hooks for a batch */
  dwr.engine._callPostHooks = function(batch) {
    if (batch.postHooks) {
      for (var i = 0; i < batch.postHooks.length; i++) {
        batch.postHooks[i]();
      }
      batch.postHooks = null;
    }
  };

  /**
   * Generic error handling routing to save having null checks everywhere
   * @private
   * @param {Object} batch
   * @param {Object} ex
   */
  dwr.engine._handleError = function(batch, ex) {
    // Perform error cleanup synchronously
    var errorHandlers = [];
    if (dwr.engine._retries <= 1) {    
      if (batch) {
        for (var i = 0; i < batch.map.callCount; i++) {
          var handlers = batch.handlers[i];
          if (!handlers.completed) {
            if (typeof handlers.errorHandler == "function") errorHandlers.push(handlers.errorHandler);
            handlers.completed = true;
          }
        }
      }
      if (batch) dwr.engine.batch.remove(batch);
    }
    // Perform error reporting asynchronously (possibly)
    ignoreIfUnloading(batch, function() {
      if (dwr.engine._retries <= 1) {    
        dwr.engine._prepareException(ex);
        var errorHandler;
        while(errorHandlers.length > 0) {
          errorHandler = errorHandlers.shift();
          errorHandler(ex.message, ex);
        }
        if (batch && typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
        else if (dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex);
      }
    });
  };

  /**
   * Handle retries for polling as well as online/offline status.
   * @private
   * @param {Object} batch
   * @param {Object} ex
   */
  dwr.engine._handlePollRetry = function(batch, ex) {
    var retryInterval;
    if (batch && batch.isPoll) {
      if (dwr.engine._retries < dwr.engine._retryIntervals.length) {
        // We are still online, try the next interval.
        retryInterval = dwr.engine._retryIntervals[dwr.engine._retries] * 1000;
      } else {
        // The last interval in retryIntervals is the number that will be used to poll when offline.
        retryInterval = dwr.engine._retryIntervals[dwr.engine._retryIntervals.length - 1] * 1000;
      }      
      if (dwr.engine._maxRetries == -1 || dwr.engine._retries <= dwr.engine._maxRetries) {
        // Call supplied pollStatusHandler and go offline.        
        if (dwr.engine._retries == dwr.engine._retryIntervals.length - 1) {
          dwr.engine._debug("poll retry - going offline: " + retryInterval/1000 + " seconds");
          dwr.engine._handlePollStatusChange(false, ex);       
        }
        dwr.engine._retries++;
        dwr.engine.batch.remove(batch);
        dwr.engine._debug("poll retry - interval: " + retryInterval/1000 + " seconds");
        setTimeout(dwr.engine._poll, retryInterval);
      } else {
        dwr.engine._debug("max retries reached, stop polling for server status.");
      }
    }
  };

  /**
   * Handles polling status changes - online or offline.  
   * @param {boolean} newStatus - true = online, false = offline
   * @param {object} ex - The exception if one exists (offline).
   * @see getahead.org/dwr/browser/engine/errors
   */
  dwr.engine._handlePollStatusChange = function(newStatus, ex) {
    if (!newStatus) {
      dwr.engine._pollOnline = false;
    }   
    if (typeof dwr.engine._pollStatusHandler) dwr.engine._pollStatusHandler(newStatus, ex);
    if (newStatus) {
      dwr.engine._pollOnline = true;
      dwr.engine._retries = 0; 
    }   
  };

  /**
   * Generic error handling routing to save having null checks everywhere.
   * @private
   * @param {Object} batch
   * @param {Object} ex
   */
  dwr.engine._handleWarning = function(batch, ex) {
    ignoreIfUnloading(batch, function() {
      // If this is a poll, we should retry! 
      dwr.engine._prepareException(ex); 
      if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex);
      else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex);
      if (batch) dwr.engine.batch.remove(batch);
    });
  };

  /**
   * Prepares an exception for an error/warning handler.  
   * @private
   * @param {Object} ex
   */
  dwr.engine._prepareException = function(ex) {
    if (typeof ex == "string") ex = { name:"unknown", message:ex };
    if (ex.message == null) ex.message = "";
    if (ex.name == null) ex.name = "unknown";
  };

  /**
   * Create a new object that delegates to obj
   * @param obj 
   */
  dwr.engine._delegate = (function() { 
    function F(){} 
    return (function(obj){ 
      F.prototype = obj; 
      return new F(); 
    }); 
  })();
  
  /**
   * Create a new object that delegates to obj
   * @param obj 
   */
  dwr.engine._createFromMap = function(map) {
    var obj = new this(); // this should be set to a constructor function!
    for(prop in map) if (map.hasOwnProperty(prop)) obj[prop] = map[prop];
    return obj;
  };
  
  /**
   * A reference to the global context (window when in a browser)
   */
  dwr.engine._global = (function(){return this;}).call(null);
  
  /**
   * Navigates properties from the global scope and down to fetch a property
   * value.
   * @param prop hierarchical property name
   * @return property value, or undefined if doesn't exist
   */
  dwr.engine._getObject = function(prop) {
    var parts = prop.split(".");
    var value;
    var scope = dwr.engine._global;
    while(parts.length > 0) {
      var currprop = parts.shift();
      value = scope[currprop];
      if (parts.length > 0 && value == null) return undefined;
      scope = value;
    }
    return value;
  };

  /**
   * Navigates properties from the global scope and down to set a value.
   * @param prop hierarchical property name
   * @param obj property value to set
   */
  dwr.engine._setObject = function(prop, obj) {
    var parts = prop.split(".");
    var level;
    var scope = dwr.engine._global;
    while(parts.length > 0) {
      var currprop = parts.shift();
      if (parts.length == 0) {
        scope[currprop] = obj;
      }
      else {
        level = scope[currprop];
        if (level == null) {
          scope[currprop] = level = {};
        }
        scope = level;
      }
    }
  };
  
  /**
   * Used internally when some message needs to get to the programmer
   * @private
   * @param {String} message
   * @param {Object} stacktrace
   */
  dwr.engine._debug = function(message, stacktrace) {
    var written = false;
    try {
      if (window.console) {
        if (stacktrace && window.console.trace) window.console.trace();
        window.console.log(message);
        written = true;
      }
      else if (window.opera && window.opera.postError) {
        window.opera.postError(message);
        written = true;
      }
      else if (window.Jaxer && Jaxer.isOnServer) {
        Jaxer.Log.info(message);
        written = true;
      }
    }
    catch (ex) { /* ignore */ }

    if (!written) {
      var debug = document.getElementById("dwr-debug");
      if (debug) {
        var contents = message + "<br/>" + debug.innerHTML;
        if (contents.length > 2048) contents = contents.substring(0, 2048);
        debug.innerHTML = contents;
      }
    }
  };

  /**
   * Functions called by the server
   */
  dwr.engine.remote = {
    /**
     * Execute a callback
     * @private
     * @param {int} batchId The ID of the batch that we are replying to
     * @param {int} callId The call ID that the script relates to
     * @param {String} reply The script to execute
     */
    handleCallback:function(batchId, callId, reply) {
      var batch = dwr.engine._batches[batchId];
      if (batch == null) {
        dwr.engine._debug("Warning: batch == null in remoteHandleCallback for batchId=" + batchId, true);
        return;
      }

      // We store the reply in the batch so that in sync mode we can return the data
      batch.reply = reply;

      // Error handlers inside here indicate an error that is nothing to do
      // with DWR so we handle them differently.
      try {
        var handlers = batch.handlers[callId];
        if (!handlers) {
          dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true);
        }
        else {
          batch.handlers[callId].completed = true;
          if (typeof handlers.callback == "function") {
            handlers.callback.apply(handlers.callbackScope, [ reply, handlers.callbackArg ]);
          }
        }
      }
      catch (ex) {
        dwr.engine._handleError(batch, ex);
      }
    },

    /**
     * Called by the server when a JavascriptFunction is executed
     * @param id The ID of the serialized function
     * @param args The arguments to pass to the function
     */
    handleFunctionCall:function(id, args) {
      var func = dwr.engine.serialize.remoteFunctions[id];
      func.apply(window, args);
    },

    /**
     * Called by the server when a JavascriptFunction is executed
     * @param id The ID of the serialized function
     * @param args The arguments to pass to the function
     */
    handleObjectCall:function(id, methodName, args) {
      var obj = dwr.engine.serialize.remoteFunctions[id];
      obj[methodName].apply(obj, args);
    },

    /**
     * Called by the server when a JavascriptFunction is executed
     * @param propertyName The ID of the serialized function
     * @param data The arguments to pass to the function
     */
    handleSetCall:function(id, propertyName, data) {
      var obj = dwr.engine.serialize.remoteFunctions[id];
      obj[propertyName] = data;
    },

    /**
     * Called by the server when a JavascriptFunction is closed
     * @param id The ID of the serialized function
     */
    handleFunctionClose:function(id) {
      delete dwr.engine.serialize.remoteFunctions[id];
    },

    /**
     * Called by the server: Handle an exception for a call
     * @private
     * @param {int} batchId The ID of the batch that we are replying to
     * @param {int} callId The call ID that the script relates to
     * @param {String} reply The script to execute
     */
    handleException:function(batchId, callId, ex) {
      var batch = dwr.engine._batches[batchId];
      if (batch == null) {
        dwr.engine._debug("Warning: null batch in remoteHandleException", true);
        return;
      }

      var handlers = batch.handlers[callId];
      batch.handlers[callId].completed = true;
      if (handlers == null) {
        dwr.engine._debug("Warning: null handlers in remoteHandleException", true);
        return;
      }

      if (ex.message == undefined) {
        ex.message = ""; 
      }

      if (typeof handlers.exceptionHandler == "function") {
        handlers.exceptionHandler.call(handlers.exceptionScope, ex.message, ex, handlers.exceptionArg);
      }
      else if (typeof batch.errorHandler == "function") {
        batch.errorHandler(ex.message, ex);
      }
    },

    /**
     * Called by the server: The whole batch is broken
     * @private
     * @param {Object} ex The data about what broke
     * @param {int} batchId The ID of the batch that we are replying to
     */
    handleBatchException:function(ex, batchId) {
      var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
      if (searchBatch) {
        dwr.engine._receivedBatch = dwr.engine._batches[batchId];
      }
      if (ex.message == undefined) ex.message = "";
      dwr.engine._handleError(dwr.engine._receivedBatch, ex);
      if (searchBatch) {
        dwr.engine._receivedBatch = null;
        dwr.engine.batch.remove(dwr.engine._batches[batchId]);
      }
    },

    /**
     * Called by the server when we need to set a new window name
     */
    handleNewWindowName:function(windowName) {
      dwr.engine._debug("Setting new window name: " + windowName);
      if (window.name != null && window.name != "") {
        dwr.engine._debug("- Warning: This will override existing name of: " + window.name);
      }
      window.name = windowName;
    },

    /**
     * Execute some script in a different window
     * @param {Object} windowName The name of the window in which to eval the script
     * @param {Object} script The script to eval elsewhere
     */
    handleForeign:function(windowName, script) {
      var foreign = window.open(null, windowName);
      if (foreign != null) {
        if (foreign.dwr != null) {
          foreign.dwr.engine._eval(script);
        }
        else {
          dwr.engine._debug("Found window, but DWR did not exist in it");
        }
      }
      else {
        dwr.engine._debug("Could not find window");
      }
    },

    /**
     * Called by the server: Reverse ajax should not be used
     * @private
     * @param {Object} ex
     * @param {int} batchId
     */
    pollCometDisabled:function(ex, batchId){
      dwr.engine.setActiveReverseAjax(false);
      var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
      if (searchBatch) {
        dwr.engine._receivedBatch = dwr.engine._batches[batchId];
      }
      if (ex.message == undefined) {
        ex.message = "";
      }
      dwr.engine._handleError(dwr.engine._receivedBatch, ex);
      if (searchBatch) {
        dwr.engine._receivedBatch = null;
        dwr.engine.batch.remove(dwr.engine._batches[batchId]);
      }
    },

    /**
     * Called by the server: Create a new object of a mapped class
     * @private
     * @param {string} dwrClassName the name of the mapped class
     * @param {Object} memberMap the object's data members
     */
    newObject:function(dwrClassName, memberMap){
      var classfunc = dwr.engine._mappedClasses[dwrClassName]; 
      if (classfunc && classfunc.createFromMap) {
        return classfunc.createFromMap(memberMap);
      }
      else {
        memberMap.$dwrClassName = dwrClassName;
        return memberMap;
      }
    }
  };

  /**
   * Functions to serialize a data set into a list of parameters
   */
  dwr.engine.serialize = {
    /**
     * ActiveX objects to use when we want to convert an xml string into a DOM object
     */
    domDocument:[
      "Msxml2.DOMDocument.6.0",
      "Msxml2.DOMDocument.5.0",
      "Msxml2.DOMDocument.4.0",
      "Msxml2.DOMDocument.3.0",
      "MSXML2.DOMDocument",
      "MSXML.DOMDocument",
      "Microsoft.XMLDOM"
    ],

    /**
     * A holder for functions that we have serialized for remote calling.
     */
    remoteFunctions:{},

    /**
     * The ID of the next function that we serialize
     */
    funcId:0,

    /**
     * Convert a text representation of XML into a DOM element
     * @param {String} xml An xml string
     */
    toDomElement:function(xml) {
      return dwr.engine.serialize.toDomDocument(xml).documentElement;
    },

    /**
     * Convert a text representation of XML into a DOM document
     * @param {String} xml An xml string
     */
    toDomDocument:function(xml) {
      var dom;
      if (window.DOMParser) {
        var parser = new DOMParser();
        dom = parser.parseFromString(xml, "text/xml");
        if (!dom.documentElement || dom.documentElement.tagName == "parsererror") {
          var message = dom.documentElement.firstChild.data;
          message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data;
          throw message;
        }
        return dom;
      }
      else if (window.ActiveXObject) {
        dom = dwr.engine.util.newActiveXObject(dwr.engine.serialize.domDocument);
        dom.loadXML(xml); // What happens on parse fail with IE?
        return dom;
      }
      else {
        var div = document.createElement("div");
        div.innerHTML = xml;
        return div;
      }
    },

    /**
     * Marshall a data item
     * @private
     * @param batch A map of variables to how they have been marshalled
     * @param directrefmap A map of already marshalled variables to prevent recurrsion (these have direct links from their data object)
     * @param otherrefmap A map of already marshalled variables to prevent recursion (these have no links from data objects)
     * @param data The data to be marshalled
     * @param name The name of the data being marshalled
     */
    convert:function(batch, directrefmap, otherrefmap, data, name, depth) {
      if (data == null) {
        batch.map[name] = "null:null";
        return;
      }

      switch (typeof data) {
      case "boolean":
        batch.map[name] = "boolean:" + data;
        break;
      case "number":
        batch.map[name] = "number:" + data;
        break;
      case "string":
        batch.map[name] = "string:" + encodeURIComponent(data);
        break;
      case "object":
        var ref = dwr.engine.serialize.lookup(directrefmap, otherrefmap, data, name);
        var objstr = Object.prototype.toString.call(data);
        if (data.$dwrByRef) batch.map[name] = dwr.engine.serialize.convertByReference(batch, directrefmap, otherrefmap, data, name, depth + 1);
        else if (ref != null) batch.map[name] = ref;
        else if (objstr == "[object String]") batch.map[name] = "string:" + encodeURIComponent(data);
        else if (objstr == "[object Boolean]") batch.map[name] = "boolean:" + data;
        else if (objstr == "[object Number]") batch.map[name] = "number:" + data;
        else if (objstr == "[object Date]") batch.map[name] = "date:" + data.getTime();
        else if (objstr == "[object Array]") batch.map[name] = dwr.engine.serialize.convertArray(batch, directrefmap, otherrefmap, data, name, depth + 1);
        else if (data && data.tagName && data.tagName.toLowerCase() == "input" && data.type && data.type.toLowerCase() == "file") {
          batch.fileUpload = true;
          batch.map[name] = data;
        }
        else {
          // This check for an HTML is not complete, but is there a better way?
          // Maybe we should add: data.hasChildNodes typeof "function" == true
          if (data.nodeName && data.nodeType) {
            batch.map[name] = dwr.engine.serialize.convertXml(batch, directrefmap, otherrefmap, data, name, depth + 1);
          }
          else {
            batch.map[name] = dwr.engine.serialize.convertObject(batch, directrefmap, otherrefmap, data, name, depth + 1);
          }
        }
        break;
      case "function":
        // Ignore functions unless they are directly passed in
        if (depth == 0) {
          batch.map[name] = dwr.engine.serialize.convertByReference(batch, directrefmap, otherrefmap, data, name, depth + 1);
        }
        break;
      default:
        dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." });
        batch.map[name] = "default:" + data;
        break;
      }
    },

    /**
     * Marshall an object by reference
     * @private
     * @see dwr.engine.serialize.convert() for parameter details
     */
    convertByReference:function(batch, directrefmap, otherrefmap, data, name, depth) {
      var funcId = "f" + dwr.engine.serialize.funcId;
      dwr.engine.serialize.remoteFunctions[funcId] = data;
      dwr.engine.serialize.funcId++;
      return "byref:" + funcId;
    },

    /**
     * Marshall an array
     * @private
     * @see dwr.engine.serialize.convert() for parameter details
     */
    convertArray:function(batch, directrefmap, otherrefmap, data, name, depth) {
      var childName, i;
      if (dwr.engine.isIE <= 7) {
        // Use array joining on IE1-7 (fastest)
        var buf = ["array:["];
        for (i = 0; i < data.length; i++) {
          if (i != 0) buf.push(",");
          batch.paramCount++;
          childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
          dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[i], childName, depth + 1);
          buf.push("reference:");
          buf.push(childName);
        }
        buf.push("]");
        reply = buf.join("");
      }
      else {
        // Use string concat on other browsers (fastest)
        var reply = "array:[";
        for (i = 0; i < data.length; i++) {
          if (i != 0) reply += ",";
          batch.paramCount++;
          childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
          dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[i], childName, depth + 1);
          reply += "reference:";
          reply += childName;
        }
        reply += "]";
      }

      return reply;
    },

    /**
     * Marshall an object by value
     * @private
     * @see dwr.engine.serialize.convert() for parameter details
     */
    convertObject:function(batch, directrefmap, otherrefmap, data, name, depth) {
      // treat objects as an associative arrays
      var reply = "Object_" + dwr.engine.serialize.getObjectClassName(data).replace(/:/g, "?") + ":{";
      var elementset = (data.constructor && data.constructor.$dwrClassMembers ? data.constructor.$dwrClassMembers : data);
      var element;
      for (element in elementset) {
        if (typeof data[element] != "function" && !dwr.engine._excludeObjectAttributes[element]) {
          batch.paramCount++;
          var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
          dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[element], childName, depth + 1);
          reply += encodeURIComponent(element) + ":reference:" + childName + ", ";
        }
      }

      if (reply.substring(reply.length - 2) == ", ") {
        reply = reply.substring(0, reply.length - 2);
      }
      reply += "}";

      return reply;
    },

    /**
     * Marshall an object
     * @private
     * @see dwr.engine.serialize.convert() for parameter details
     */
    convertXml:function(batch, directrefmap, otherrefmap, data, name, depth) {
      var output;
      if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data);
      else if (data.toXml) output = data.toXml;
      else output = data.innerHTML;

      return "xml:" + encodeURIComponent(output);
    },

    /**
     * Have we already converted this object?
     * @private
     * @see dwr.engine.serialize.convert() for parameter details
     */
    lookup:function(directrefmap, otherrefmap, data, name) {
      // Look for reference to previous conversion
      var ref;
      if ("$_dwrConversionRef" in data) {
        ref = data.$_dwrConversionRef;
        if (ref && directrefmap[ref] != data) ref = null;
      }
      if (ref == null) {
        for(r in otherrefmap) {
          if (otherrefmap[r] == data) {
            ref = r;
            break;
          }
        }
      }
      if (ref != null) return "reference:" + ref;
      // This was a new conversion - save the reference!
      try {
        data.$_dwrConversionRef = name;
        directrefmap[name] = data;
      }
      catch(err) {
        otherrefmap[name] = data;
      }
      return null;
    },

    /**
     * Clean up our conversion markers from user data
     * @private
     */
    cleanup:function(directrefmap) {
      for(name in directrefmap) {
        var data = directrefmap[name];
        try {
          delete data.$_dwrConversionRef;
        }
        catch(err) {
          data.$_dwrConversionRef = undefined;
        }
      }
    },
    
    /**
     * Returns the classname of supplied argument obj. Similar to typeof, but
     * which returns the name of the constructor that created the object rather
     * than 'object'
     * @private
     * @param {Object} obj The object to detect the type of
     * @return The name of the object
     */
    getObjectClassName:function(obj) {
      // Different handling depending on if, and what type of, class-mapping is used
      if (obj.$dwrClassName)
        return obj.$dwrClassName; // Light class-mapping uses the classname from a property on the instance
      else if (obj.constructor && obj.constructor.$dwrClassName)
        return obj.constructor.$dwrClassName; // Full class-mapping uses the classname from a property on the constructor function
      else
        return "Object";
    }
  };

  /**
   * Functions to handle the various remoting transport
   */
  dwr.engine.transport = {
    /**
     * Manage the DWR session and then send.
     * @private
     * @param {Object} batch
     */
    send:function(batch) {
      dwr.engine.transport.updateDwrSessionFromCookie();
      if (!dwr.engine._dwrSessionId) {
        dwr.engine._internalOrdered = true;
        var idbatch = {
          map:{
            callCount:1,
            'c0-scriptName':'__System',
            'c0-methodName':'generateId',
            'c0-id':0
          },
          paramCount:0, isPoll:false, async:batch.async,
          headers:{}, preHooks:[], 
          postHooks:[function() {
            dwr.engine._internalOrdered = false;
          }],
          timeout:dwr.engine._timeout,
          errorHandler:batch.errorHandler, warningHandler:batch.warningHandler, textHtmlHandler:batch.textHtmlHandler,
          path:batch.path,
          handlers:[{
            exceptionHandler:null, 
            callback:function(id) {
              dwr.engine.transport.updateDwrSessionFromCookie();
              if (!dwr.engine._dwrSessionId) {
                dwr.engine.transport.setDwrSession(id);
              }
              dwr.engine.transport.send2(batch);
            }
          }]
        };
        dwr.engine.transport.send2(idbatch);
      }
      else {
        dwr.engine.transport.send2(batch);
      }
    },

    /**
     * Actually send the block of data in the batch object.
     * @private
     * @param {Object} batch
     */
    send2:function(batch) {
      dwr.engine.batch.prepareToSend(batch);

      // Work out if we are going cross domain
      var isCrossDomain = false;
      if (batch.path == null) {
        batch.path = dwr.engine._pathToDwrServlet;
      }
      if (batch.path.indexOf("http://") == 0 || batch.path.indexOf("https://") == 0) {
        var dwrShortPath = batch.path.split("/", 3).join("/"); 
        var hrefShortPath = window.location.href.split("/", 3).join("/");
        isCrossDomain = (dwrShortPath != hrefShortPath);
      }

      if (batch.fileUpload) {
        if (isCrossDomain) {
          throw new Error("Cross domain file uploads are not possible with this release of DWR");
        }
        batch.transport = dwr.engine.transport.iframe;
      }
      else if (isCrossDomain && !dwr.engine.isJaxerServer) {
        batch.transport = dwr.engine.transport.scriptTag;
      }
      // else if (batch.isPoll && dwr.engine.isIE) {
      //   batch.transport = dwr.engine.transport.htmlfile;
      // }
      else {
        batch.transport = dwr.engine.transport.xhr;
      }

      return batch.transport.send(batch);
    },

    /**
     * Called to signal that the batch response has been delivered
     */
    complete:function(batch) {
        dwr.engine.batch.validate(batch);
        dwr.engine.transport.remove(batch);
    },
    
    /**
     * Called as a result of a request timeout
     * @private
     * @param {Object} batch The batch that is aborting
     */
    abort:function(batch) {
      var transport = batch.transport;
      dwr.engine.transport.remove(batch);
      if (transport.abort) {
        transport.abort(batch);
      }
      dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" });
    },

    /**
     * Remove all remoting artifacts
     * @param {Object} batch The batch that has completed
     */
    remove:function(batch) {
      if (batch.transport) {
        dwr.engine._callPostHooks(batch);
        batch.transport.remove(batch);
        batch.transport = null;
      }
      dwr.engine.batch.remove(batch);
    },

    setDwrSession:function(dwrsess) {
      dwr.engine._dwrSessionId = dwrsess;
      document.cookie = "DWRSESSIONID=" + dwrsess + "; path=" + (dwr.engine._contextPath != "" ? dwr.engine._contextPath : "/");
      dwr.engine._scriptSessionId = dwrsess + "/" + dwr.engine._pageId;
    },

    updateDwrSessionFromCookie:function() {
      if (!dwr.engine._dwrSessionId) {
        var match = document.cookie.match(/(?:^|; )DWRSESSIONID=([^;]+)/);
        if (match) {
          dwr.engine.transport.setDwrSession(match[1]);
        }
      }
    },

    /**
     * Remoting through XHR
     */
    xhr:{
      /**
       * The default HTTP method to use
       */
      httpMethod:"POST",

      /**
       * The ActiveX objects to use when we want to do an XMLHttpRequest call.
       * TODO: We arrived at this by trial and error. Other toolkits use
       * different strings, maybe there is an officially correct version?
       */
      XMLHTTP:["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"],

      /**
       * Setup a batch for transfer through XHR
       * @param {Object} batch The batch to alter for XHR transmit
       */
      send:function(batch) {
        if (batch.isPoll) {
          batch.map.partialResponse = dwr.engine._partialResponseYes;
        }

        // Do proxies or IE force us to use early closing mode?
        if (batch.isPoll && dwr.engine._pollWithXhr == "true") {
          batch.map.partialResponse = dwr.engine._partialResponseNo;
        }
        if (batch.isPoll && dwr.engine.isIE) {
          batch.map.partialResponse = dwr.engine._partialResponseNo;
        }

        if (window.XMLHttpRequest) {
          batch.req = new XMLHttpRequest();
        }
        else if (window.ActiveXObject) {
          batch.req = dwr.engine.util.newActiveXObject(dwr.engine.transport.xhr.XMLHTTP);
        }

        // Proceed using XMLHttpRequest
        if (batch.async == true) {
          batch.req.onreadystatechange = function() {
            if (typeof dwr != 'undefined') {
              dwr.engine.transport.xhr.stateChange(batch);
            }
          };
        }

        httpMethod = dwr.engine.transport.xhr.httpMethod;

        // Workaround for Safari 1.x POST bug
        var indexSafari = navigator.userAgent.indexOf("Safari/");
        if (indexSafari >= 0) {
          var version = navigator.userAgent.substring(indexSafari + 7);
          if (parseInt(version, 10) < 400) {
            if (dwr.engine._allowGetForSafariButMakeForgeryEasier == "true") {
              httpMethod = "GET";
            }
            else {
              dwr.engine._handleWarning(batch, {
                name: "dwr.engine.oldSafari",
                message: "Safari GET support disabled. See getahead.org/dwr/server/servlet and allowGetForSafariButMakeForgeryEasier."
              });
            }
          }
        }

        batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
        var request = dwr.engine.batch.constructRequest(batch, httpMethod);

        try {
          batch.req.open(httpMethod, request.url, batch.async);
          try {
            for (var prop in batch.headers) {
              var value = batch.headers[prop];
              if (typeof value == "string") {
                batch.req.setRequestHeader(prop, value);
              }
            }
            if (!batch.headers["Content-Type"]) {
              batch.req.setRequestHeader("Content-Type", "text/plain");
            }
          }
          catch (ex) {
            dwr.engine._handleWarning(batch, ex);
          }

          batch.req.send(request.body);
          if (batch.async == false) {
            dwr.engine.transport.xhr.stateChange(batch);
          }
        }
        catch (ex) {
          dwr.engine._handleError(batch, ex);
        }

        if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
          dwr.engine.transport.xhr.checkCometPoll();
        }

        // This is only of any use in sync mode to return the reply data
        return batch.reply;
      },

      /**
       * Called by XMLHttpRequest to indicate that something has happened
       * @private
       * @param {Object} batch The current remote operation
       */
      stateChange:function(batch) {
        var toEval;

        if (batch.completed) {
          dwr.engine._debug("Error: _stateChange() with batch.completed");
          return;
        }

        // Try to get the response HTTP status if applicable
        var req = batch.req;
        var status = 0;
        try {
          if (req.readyState >= 2) {
            status = req.status; // causes Mozilla to except on page moves
          }
        }
        catch(ignore) {}

        // If we couldn't get the status we bail out, unless the request is
        // complete, which means error (handled further below)
        if (status == 0 && req.readyState < 4) {
          return;
        }

        // If the status is 200, we are now online. 
        // Future improvement per Mike W. - A solution where we only use the callbacks/handlers of the poll call to trigger 
        // the retry handling would be ideal.  We would need something like a new internal callback that reports 
        // progress back to the caller, and the design should be compatible with getting it to work with iframes as well.   
        if (status == 200 && !dwr.engine._pollOnline) {
          dwr.engine._handlePollStatusChange(true);    
        }  

        // The rest of this function only deals with request completion
        if (req.readyState != 4) {
          return;
        }

        if (dwr.engine._unloading && !dwr.engine.isJaxerServer) {
          dwr.engine._debug("Ignoring reply from server as page is unloading.");
          return;
        }

        try {
          var reply = req.responseText;
          reply = dwr.engine._replyRewriteHandler(reply);

          if (status != 200) {
            dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:req.statusText });
          }
          else if (reply == null || reply == "") {
            dwr.engine._handleError(batch, { name:"dwr.engine.missingData", message:"No data received from server" });
          }
          else {                     
            var contentType = req.getResponseHeader("Content-Type");
            if (dwr.engine.isJaxerServer) {
              // HACK! Jaxer does something b0rken with Content-Type
              contentType = "text/javascript";
            }
            if (!contentType.match(/^text\/plain/) && !contentType.match(/^text\/javascript/)) {
              if (contentType.match(/^text\/html/) && typeof batch.textHtmlHandler == "function") {
                batch.textHtmlHandler({ status:status, responseText:reply, contentType:contentType });
              }
              else {
                dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" });
              }
            }
            else {
             // Comet replies might have already partially executed
             if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
                dwr.engine.transport.xhr.processCometResponse(reply, batch);
              }
              else {
                if (reply.search("//#DWR") == -1) {
                  dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" });
                }
                else {
                  toEval = reply;
                }
              }
            }
          }
        }
        catch (ex) {
          dwr.engine._handleWarning(batch, ex);
        }

        // Outside of the try/catch so errors propagate normally:
        dwr.engine._receivedBatch = batch;
        if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, "");
        dwr.engine._eval(toEval);
        dwr.engine._receivedBatch = null;
        dwr.engine.transport.complete(batch);
      },

      /**
       * Check for reverse Ajax activity
       * @private
       */       
      checkCometPoll:function() {
        var req = dwr.engine._pollBatch && dwr.engine._pollBatch.req;
        if (req) {         
          var text = req.responseText;
          if (text != null) {
            dwr.engine.transport.xhr.processCometResponse(text, dwr.engine._pollBatch);
          }          
        }      
        // If the poll resources are still there, come back again
        if (dwr.engine._pollBatch) {
          setTimeout(dwr.engine.transport.xhr.checkCometPoll, dwr.engine._pollCometInterval);
        }
      },

      /**
       * Some more text might have come in, test and execute the new stuff.
       * This method could also be called by the iframe transport
       * @private
       * @param {Object} response from xhr.responseText
       * @param {Object} batch The batch that the XHR object pertains to
       */
      processCometResponse:function(response, batch) {
        if (batch.charsProcessed == response.length) return;
        if (response.length == 0) {
          batch.charsProcessed = 0;
          return;
        }

        var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed);
        if (firstStartTag == -1) {
          // dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'");
          batch.charsProcessed = response.length;
          return;
        }
        // if (firstStartTag > 0) {
        //   dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'");
        // }

        var lastEndTag = response.lastIndexOf("//#DWR-END#");
        if (lastEndTag == -1) {
          // dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed);
          return;
        }

        // Skip the end tag too for next time, remembering CR and LF
        if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) {
         batch.charsProcessed = lastEndTag + 13;
        }
        else {
          batch.charsProcessed = lastEndTag + 11;
        }

        var exec = response.substring(firstStartTag + 13, lastEndTag);

        try {
          dwr.engine._receivedBatch = batch;
          dwr.engine._eval(exec);
          dwr.engine._receivedBatch = null;
        }
        catch (ex) {
          // This is one of these annoying points where we might be executing
          // while the window is being destroyed. If dwr == null, bail out.
          if (dwr != null) {
            dwr.engine._handleError(batch, ex);
          }
        }
      },

      /**
       * Aborts ongoing request (for timeouts etc)
       */
      abort:function(batch) {
        if (batch.req) {
          batch.req.abort();
        }
      },
      
      /**
       * Tidy-up when an XHR call is done
       * @param {Object} batch
       */
      remove:function(batch) {
        // XHR tidyup: avoid IE handles increase
        if (batch.req) {
          delete batch.req;
        }
      }
    },

    /**
     * Functions for remoting through IFrame
     */
    iframe:{
      /**
       * The default HTTP method to use
       */
      httpMethod:"POST",

      /**
       * Setup a batch for transfer through IFrame
       * @param {Object} batch The batch to alter for IFrame transmit
       */
      send:function(batch) {
        if (document.body == null) {
          setTimeout(function(){dwr.engine.transport.iframe.send(batch);}, 100);
          return;
        }
        batch.httpMethod = dwr.engine.transport.iframe.httpMethod;
        if (batch.fileUpload) {
          batch.httpMethod = "POST";
          batch.encType = "multipart/form-data";
        }
        var idname = dwr.engine.transport.iframe.getId(batch);
        batch.div1 = document.createElement("div");
        document.body.appendChild(batch.div1);
        batch.div1.innerHTML = "<iframe src='" + dwr.engine.SSL_SECURE_URL + "' frameborder='0' style='width:0px;height:0px;border:0;display:none;' id='" + idname + "' name='" + idname + "'></iframe>";
        batch.iframe = batch.div1.firstChild;
        batch.document = document;
        batch.iframe.batch = batch;
        dwr.engine.transport.iframe.beginLoader(batch, idname);
      },

      /**
       * Create a unique ID so multiple iframes can fire at the same time
       * @param {Object} batch A source of a unique number for the batch
       * @return {String} a name prefix for created elements
       */
      getId:function(batch) {
        return batch.isPoll ? "dwr-if-poll-" + batch.map.batchId : "dwr-if-" + batch.map.batchId;
      },

      /**
       * Setup a form or construct a src attribute to use the iframe.
       * This is abstracted from send() because the same logic will do for htmlfile
       * @param {Object} batch
       */
      beginLoader:function(batch, idname) {
        if (batch.iframe.contentWindow.document.body == null) {
          setTimeout(function(){dwr.engine.transport.iframe.beginLoader(batch, idname);}, 100);
          return;
        }
        if (batch.isPoll) {
          batch.map.partialResponse = dwr.engine._partialResponseYes;
        }
        batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall;
        var request = dwr.engine.batch.constructRequest(batch, batch.httpMethod);
        if (batch.httpMethod == "GET") {
          batch.iframe.setAttribute("src", request.url);
        }
        else {
          // TODO: On firefox we can now get the values of file fields, maybe we should use this
          // See http://soakedandsoaped.com/articles/read/firefox-3-native-ajax-file-upload
          // setting enctype via the DOM does not work in IE, create the form using innerHTML instead
          batch.div2 = document.createElement("div");
          document.body.appendChild(batch.div2);
          batch.div2.innerHTML = "<form" + (batch.encType ? " encType='" + batch.encType + "' encoding='" + batch.encType + "'" : "") + "></form>";
          batch.form = batch.div2.firstChild;
          batch.form.setAttribute("action", request.url);
          batch.form.setAttribute("target", idname);
          batch.form.setAttribute("style", "display:none");
          batch.form.setAttribute("method", batch.httpMethod);
          for (var prop in batch.map) {
            var value = batch.map[prop];
            if (typeof value != "function") {
              if (value && value.tagName && value.tagName.toLowerCase() == "input" && value.type && value.type.toLowerCase() == "file") {
                // Since we can not set the value of a file object, we must post the actual file object
                // that the user clicked browse on. We will put a clone in it's place.
                var clone = value.cloneNode(true);
                value.removeAttribute("id", prop);
                value.setAttribute("name", prop);
                value.style.display = "none";
                value.parentNode.insertBefore(clone, value);
                value.parentNode.removeChild(value);
                batch.form.appendChild(value);
              } else {
                var formInput = batch.document.createElement("input");
                formInput.setAttribute("type", "hidden");
                formInput.setAttribute("name", prop);
                formInput.setAttribute("value", value);
                batch.form.appendChild(formInput);
              }
            }
          }
          batch.form.submit();
        }
      },

      /**
       * Functions designed to be called by the server
       */
      remote:{
        /**
         * Called by the server: An IFrame reply is about to start
         * @private
         * @param {Object} iframe
         * @param {int} batchId
         */
        beginIFrameResponse:function(iframe, batchId) {
          if (iframe != null) dwr.engine._receivedBatch = iframe.batch;
        },

        /**
         * Called by the server: An IFrame reply is just completing
         * @private
         * @param {int} batchId
         */
        endIFrameResponse:function(batchId) {
          dwr.engine._receivedBatch = dwr.engine._batches[batchId];
          dwr.engine.transport.complete(dwr.engine._receivedBatch);
          dwr.engine._receivedBatch = null;
        }
      },

      remove:function(batch) {
        // Safari 3 and Chrome 1 will show endless loading spinner if removing 
        // iframe during execution of iframe script, so we delay it a bit
        setTimeout(function(){
          if (batch.iframe && batch.iframe.parentNode) {
            batch.iframe.parentNode.removeChild(batch.iframe);
            batch.iframe = null;
          }
          if (batch.div1 && batch.div1.parentNode) {
            batch.div1.parentNode.removeChild(batch.div1);
            batch.div1 = null;
          }
          if (batch.form && batch.form.parentNode) {
            batch.form.parentNode.removeChild(batch.form);
            batch.form = null;
          }
          if (batch.div2 && batch.div2.parentNode) {
            batch.div2.parentNode.removeChild(batch.div2);
            batch.div2 = null;
          }
        }, 100);
      }

      /*
      // If we have an iframe comet solution where we need to read data streamed
      // into an iframe then we need code like this to slurp the data out.
      // Compare this with xhr.checkCometPoll()
      outstandingIFrames:[],

      checkCometPoll:function() {
        for (var i = 0; i < dwr.engine.transport.iframe.outstandingIFrames.length; i++) {
          var text = "";
          var iframe = dwr.engine.transport.iframe.outstandingIFrames[i];
          try {
            text = dwr.engine.transport.iframe.getTextFromCometIFrame(iframe);
          }
          catch (ex) {
            dwr.engine._handleWarning(iframe.batch, ex);
          }
          if (text != "") dwr.engine.transport.xhr.processCometResponse(text, iframe.batch);
        }

        if (dwr.engine.transport.iframe.outstandingIFrames.length > 0) {
          setTimeout(dwr.engine.transport.iframe.checkCometPoll, dwr.engine._pollCometInterval);
        }
      }

      // We probably also need to update dwr.engine.remote.beginIFrameResponse()
      // to call checkCometPoll.

      // Extract the whole (executed and all) text from the current iframe
      getTextFromCometIFrame:function(frameEle) {
        var body = frameEle.contentWindow.document.body;
        if (body == null) return "";
        var text = body.innerHTML;
        // We need to prevent IE from stripping line feeds
        if (text.indexOf("<PRE>") == 0 || text.indexOf("<pre>") == 0) {
          text = text.substring(5, text.length - 7);
        }
        return text;
      };

      // And an addition to iframe.remove():
      {
        if (batch.iframe) {
          // If this is a poll frame then stop comet polling
          for (var i = 0; i < dwr.engine.transport.iframe.outstandingIFrames.length; i++) {
            if (dwr.engine.transport.iframe.outstandingIFrames[i] == batch.iframe) {
              dwr.engine.transport.iframe.outstandingIFrames.splice(i, 1);
            }
          }
        }
      }
      */
    },

    /**
     * Functions for remoting through Script Tags
     */
    scriptTag:{
      /**
       * Setup a batch for transfer through a script tag
       * @param {Object} batch The batch to alter for script tag transmit
       */
      send:function(batch) {
        if (batch.isPoll) {
          batch.map.partialResponse = dwr.engine._partialResponseNo;
        }
        batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
        var request = dwr.engine.batch.constructRequest(batch, "GET");
        // The best option is DOM manipulation
        batch.script = document.createElement("script");
        batch.script.id = "dwr-st-" + batch.map.batchId;
        batch.script.src = request.url;
        batch.script.type = "text/javascript";
        batch.script.async = true;
        dwr.engine.util.addEventListener(batch.script, "load", function(ev) {
          if (typeof dwr != "undefined") dwr.engine.transport.scriptTag.complete(batch);
        });
        dwr.engine.util.addEventListener(batch.script, "error", function(ev) {
          if (typeof dwr != "undefined") dwr.engine.transport.scriptTag.complete(batch);
        });
        dwr.engine.util.addEventListener(batch.script, "readystatechange", function(ev) {
          if (typeof dwr != "undefined") {
            if (batch.script.readyState == "complete" || batch.script.readyState == "loaded") {
              dwr.engine.transport.scriptTag.complete(batch);
            }
          }
        });
        document.getElementsByTagName("head")[0].appendChild(batch.script);
      },

      /**
       * Notified when the script tag has been loaded and executed
       */
      complete:function(batch) {
        dwr.engine.transport.complete(batch);
      },
      
      /**
       * Cleanup script tag
       */
      remove:function(batch) {
        // Bail out if we were already called
        if (!batch.script) return;
        
        // Cleanup script tag
        batch.script.parentNode.removeChild(batch.script);
        batch.script = null;
      }
    },

    /**
     * Remoting through IE's htmlfile ActiveX control
     */
    htmlfile:{
      /**
       * Setup a batch for transfer through htmlfile
       * @param {Object} batch The batch to alter for htmlfile transmit
       */
      send:function(batch) {
        var idname = dwr.engine.transport.iframe.getId(batch);
        batch.htmlfile = new window.ActiveXObject("htmlfile");
        batch.htmlfile.open();
        batch.htmlfile.write("<" + "html>");
        batch.htmlfile.write("<div><iframe className='wibble' src='javascript:void(0)' id='" + idname + "' name='" + idname + "' οnlοad='dwr.engine.transport.iframe.loadingComplete(" + batch.map.batchId + ");'></iframe></div>");
        batch.htmlfile.write("</" + "html>");
        batch.htmlfile.close();
        batch.htmlfile.parentWindow.dwr = dwr;
        batch.document = batch.htmlfile;

        dwr.engine.transport.iframe.beginLoader(batch, idname);
      }
    }
  };

  /**
   * Functions to manipulate batches
   * @private
   */
  dwr.engine.batch = {
    /**
     * Generate a new standard batch
     * @private
     */
    create:function() {
      var batch = {
        async:dwr.engine._async,
        charsProcessed:0,
        handlers:[],
        isPoll:false,
        map:{ callCount:0, windowName:window.name },
        paramCount:0,
        preHooks:[],
        postHooks:[],
        timeout:dwr.engine._timeout,
        errorHandler:dwr.engine._errorHandler,
        warningHandler:dwr.engine._warningHandler,
        textHtmlHandler:dwr.engine._textHtmlHandler
      };

      if (dwr.engine._preHook) {
        batch.preHooks.push(dwr.engine._preHook);
      }
      if (dwr.engine._postHook) {
        batch.postHooks.push(dwr.engine._postHook);
      }

      dwr.engine.batch.populateHeadersAndAttributes(batch);
      return batch;
    },

    /**
     * Generate a new batch for polling
     * @private
     * @see dwr.engine.batch.create()
     */
    createPoll:function() {
      var batch = {
        async:true,
        charsProcessed:0,
        handlers:[{
          callback:function(pause) {  
            dwr.engine._pollBatch = null;
            setTimeout(dwr.engine._poll, pause);
          }
        }],
        isPoll:true,
        map:{ windowName:window.name, callCount:1 },
        paramCount:0,
        path:dwr.engine._pathToDwrServlet,
        preHooks:[],
        postHooks:[],
        timeout:0,
        windowName:window.name
      };     
      dwr.engine.batch.populateHeadersAndAttributes(batch);
      return batch;
    },

    /**
     * Copy the global headers and parameters into this batch object
     * @private
     * @param {Object} batch The destination
     */
    populateHeadersAndAttributes:function(batch) {
      var propname, data;
      batch.headers = {};
      if (dwr.engine._headers) {
        for (propname in dwr.engine._headers) {
          data = dwr.engine._headers[propname];
          if (typeof data != "function") batch.headers[propname] = data;
        }
      }
      batch.attributes = {}; // props to add as request attributes
      if (dwr.engine._attributes) {
        for (propname in dwr.engine._attributes) {
          data = dwr.engine._attributes[propname];
          if (typeof data != "function") batch.attributes[propname] = data;
        }
      }
    },

    /**
     * Augment this batch with a new call
     * @private
     */
    addCall:function(batch, scriptName, methodName, args) {
      // From the other params, work out which is the function (or object with
      // call meta-data) and which is the call parameters
      var callData, stopAt;
      var lastArg = args[args.length - 1];
      if (lastArg == null || typeof lastArg == "function") {
        callData = { callback:lastArg };
        stopAt = args.length - 1;
      }
      else if (typeof lastArg == "object" && (typeof lastArg.callback == "function"
        || typeof lastArg.exceptionHandler == "function" || typeof lastArg.callbackHandler == "function"
        || typeof lastArg.errorHandler == "function" || typeof lastArg.warningHandler == "function" )) {
        callData = lastArg;
        stopAt = args.length - 1;
      }
      else {
        callData = {};
        stopAt = args.length;
      }

      // Merge from the callData into the batch
      dwr.engine.batch.merge(batch, callData);
      batch.handlers[batch.map.callCount] = {
        exceptionHandler:callData.exceptionHandler,
        exceptionArg:callData.exceptionArg || callData.arg || null,
        exceptionScope:callData.exceptionScope || callData.scope || window,
        callback:callData.callbackHandler || callData.callback,
        callbackArg:callData.callbackArg || callData.arg || null,      
        callbackScope:callData.callbackScope || callData.scope || window          
      };

      // Copy to the map the things that need serializing
      var prefix = "c" + batch.map.callCount + "-";
      batch.map[prefix + "scriptName"] = scriptName;
      batch.map[prefix + "methodName"] = methodName;
      batch.map[prefix + "id"] = batch.map.callCount;
      var directrefmap = {}, otherrefmap = {};
      for (var i = 0; i < stopAt; i++) {
        dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, args[i], prefix + "param" + i, 0);
      }
      dwr.engine.serialize.cleanup(directrefmap);
    },

    /**
     * Take further options and merge them into a batch
     * @private
     * @param {Object} batch The batch that we are altering
     * @param {Object} overrides The object containing properties to copy into batch
     */
    merge:function(batch, overrides) {
      var propname, data;
      for (var i = 0; i < dwr.engine._propnames.length; i++) {
        propname = dwr.engine._propnames[i];
        if (overrides[propname] != null) batch[propname] = overrides[propname];
      }
      if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook);
      if (overrides.postHook != null) batch.postHooks.push(overrides.postHook);
      if (overrides.headers) {
        for (propname in overrides.headers) {
          data = overrides.headers[propname];
          if (typeof data != "function") batch.headers[propname] = data;
        }
      }
      var attrs = null;
      if (overrides.attributes) attrs = overrides.attributes;
      if (attrs) {  
        for (propname in attrs) {
          data = attrs[propname];
          if (typeof data != "function") batch.attributes[propname] = data;
        }
      }      
    },

    /**
     * Executed just before a transport sends the batch
     * @private
     * @param {Object} batch The batch to prepare for sending
     */
    prepareToSend:function(batch) {
      batch.map.batchId = dwr.engine._nextBatchId;
      dwr.engine._nextBatchId++;
      dwr.engine._batches[batch.map.batchId] = batch;
      dwr.engine._batchesLength++;
      batch.completed = false;
      batch.map.instanceId = dwr.engine._instanceId;

      // security details are filled in late so previous batches have completed
      batch.map.page = encodeURIComponent(window.location.pathname + window.location.search);
      batch.map.scriptSessionId = dwr.engine._scriptSessionId;
      batch.map.windowName = window.name;

      for (var i = 0; i < batch.preHooks.length; i++) {
        batch.preHooks[i]();
      }
      batch.preHooks = null;
      // Set a timeout
      if (batch.timeout && batch.timeout != 0) {
        batch.timeoutId = setTimeout(function() { dwr.engine.transport.abort(batch); }, batch.timeout);
      }
    },

    /**
     * Work out what the URL should look like
     * @private
     * @param {Object} batch the data that we are sending
     * @param {Object} httpMethod Are we using GET/POST etc?
     */
    constructRequest:function(batch, httpMethod) {
      // A quick string to help people that use web log analysers
      var urlBuffer = [];
      urlBuffer.push(batch.path);
      urlBuffer.push(batch.mode);
      if (batch.isPoll) {
        urlBuffer.push("ReverseAjax.dwr");
      }
      else if (batch.map.callCount == 1) {
        urlBuffer.push(batch.map["c0-scriptName"]);
        urlBuffer.push(".");
        urlBuffer.push(batch.map["c0-methodName"]);
        urlBuffer.push(".dwr");
      }
      else {
        urlBuffer.push("Multiple.");
        urlBuffer.push(batch.map.callCount);
        urlBuffer.push(".dwr");
      }
      // Play nice with url re-writing
      var sessionMatchExpr = new RegExp(
        "^"               // start of string
        + "[^;\\?#]+"     // protocol, host, and path (up to first of ; ? or #)
        + "(;[^\\?#]+)"); // group 1: ; and sessionid (up to first of ? or #)
      var sessionMatch = location.href.match(sessionMatchExpr); 
      if (sessionMatch != null) { 
        urlBuffer.push(sessionMatch[1]); 
      }

      // Add attributes to the output map
      if (batch.attributes) {
        for (var attrname in batch.attributes) {
          var data = batch.attributes[attrname];
          if (typeof data != "function") batch.map["a-" + attrname] = "" + data;
        }
      }

      var request = {};
      var prop;
      if (httpMethod == "GET") {
        // Some browsers (Opera/Safari2) seem to fail to convert the callCount value
        // to a string in the loop below so we do it manually here.
        batch.map.callCount = "" + batch.map.callCount;
        urlBuffer.push("?");
        for (prop in batch.map) {
          if (typeof batch.map[prop] != "function") {
            urlBuffer.push(encodeURIComponent(prop));
            urlBuffer.push("=");
            urlBuffer.push(encodeURIComponent(batch.map[prop]));
            urlBuffer.push("&");
          }
        }
        urlBuffer.pop(); // remove the trailing &
        request.body = null;
      }
      else {
        // PERFORMANCE: for iframe mode this is thrown away.
        request.body = "";
        if (dwr.engine.isIE <= 7) {
          // Use array joining on IE1-7 (fastest)
          var buf = [];
          for (prop in batch.map) {
            if (typeof batch.map[prop] != "function") {
              buf.push(prop + "=" + batch.map[prop] + dwr.engine._postSeperator);
            }
          }
          request.body = buf.join("");
        }
        else {
          // Use string concat on other browsers (fastest)
          for (prop in batch.map) {
            if (typeof batch.map[prop] != "function") {
              request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator;
            }
          }
        }
        request.body = dwr.engine._contentRewriteHandler(request.body);
      }
      request.url = dwr.engine._urlRewriteHandler(urlBuffer.join(""));
      return request;
    },

    /**
     * @private This function is invoked when a batch reply is received.
     * It checks that there is a response for every call in the batch. Otherwise,
     * an error will be signaled (a call without a response indicates that the
     * server failed to send complete batch response).
     */
    validate:function(batch) {
      // If some call left unreplied, report an error.
      if (!batch.completed) {
        for (var i = 0; i < batch.map.callCount; i++) {
          if (batch.handlers[i].completed !== true) {
            dwr.engine._handleError(batch, { name:"dwr.engine.incompleteReply", message:"Incomplete reply from server" });
            break;
          }
        }
      }
    },

    /**
     * A call has finished by whatever means and we need to shut it all down.
     * @private
     * @param {Object} batch The batch that we are altering
     */
    remove:function(batch) {
      if (!batch) {
        dwr.engine._debug("Warning: null batch in dwr.engine.batch.remove()", true);
        return;
      }

      if (batch.completed) {
        return;
      }
      batch.completed = true;

      // Transport tidyup
      dwr.engine.transport.remove(batch);

      // Timeout tidyup
      if (batch.timeoutId != null) {
        clearTimeout(batch.timeoutId);
        delete batch.timeoutId;
      }

      // TODO: co-locate all the functions that work on a set of batches
      if (batch.map && (batch.map.batchId || batch.map.batchId == 0)) {
        delete dwr.engine._batches[batch.map.batchId];
        dwr.engine._batchesLength--;
      }

      // If there is anything on the queue waiting to go out, then send it.
      // We don't need to check for ordered mode, here because when ordered mode
      // gets turned off, we still process *waiting* batches in an ordered way.
      if (dwr.engine._batchQueue.length != 0) {
        var sendbatch = dwr.engine._batchQueue.shift();
        dwr.engine.transport.send(sendbatch);
      }
    }
  };

  /**
   * Various utility functions
   * @private
   */
  dwr.engine.util = {
    /**
     * Create one of a number of ActiveX strings
     * @param {Object} axarray An array of strings to attempt to create ActiveX objects from
     */
    newActiveXObject:function(axarray) {
      var returnValue;
      for (var i = 0; i < axarray.length; i++) {
        try {
          returnValue = new ActiveXObject(axarray[i]);
          break;
        }
        catch (ex) { /* ignore */ }
      }
      return returnValue;
    },

    /**
     * Transform a number into a token string suitable for ids
     */
    tokenify: function(number) {
      var tokenbuf = [];
      var charmap = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*$";
      var remainder = number;
      while (remainder > 0)
      {
        tokenbuf.push(charmap.charAt(remainder & 0x3F));
        remainder = Math.floor(remainder / 64); // Can't use shift operator due to 32-bit limit in JS
      }
      return tokenbuf.join("");
    },
    
    addEventListener: function(elem, name, func) {
      if (elem.addEventListener)
        elem.addEventListener(name, func, false);
      else
        elem.attachEvent("on" + name, func);
    }
  };

  /**
   * Work out what type of browser we are working on
   */

  var userAgent = navigator.userAgent;
  var versionString = navigator.appVersion;
  var version = parseFloat(versionString);

  dwr.engine.isOpera = (userAgent.indexOf("Opera") >= 0) ? version : 0;
  dwr.engine.isKhtml = (versionString.indexOf("Konqueror") >= 0) || (versionString.indexOf("Safari") >= 0) ? version : 0;
  dwr.engine.isSafari = (versionString.indexOf("Safari") >= 0) ? version : 0;
  dwr.engine.isJaxerServer = (window.Jaxer && Jaxer.isOnServer);

  var geckoPos = userAgent.indexOf("Gecko");
  dwr.engine.isMozilla = ((geckoPos >= 0) && (!dwr.engine.isKhtml)) ? version : 0;

  dwr.engine.isFF = 0;
  dwr.engine.isIE = 0;
  try {
    if (dwr.engine.isMozilla) {
      dwr.engine.isFF = parseFloat(userAgent.split("Firefox/")[1].split(" ")[0]);
    }
    if ((document.all) && (!dwr.engine.isOpera)) {
      dwr.engine.isIE = parseFloat(versionString.split("MSIE ")[1].split(";")[0]);
    }
  }
  catch(ex) { }

  /*
   * Other load-time initializations
   */

  // Make random local page id
  dwr.engine._pageId = dwr.engine.util.tokenify(new Date().getTime()) + "-" + dwr.engine.util.tokenify(Math.random() * 1E16);

  // Reuse any existing dwr session
  dwr.engine.transport.updateDwrSessionFromCookie();

  // Register the unload handler
  if (!dwr.engine.isJaxerServer) {
    dwr.engine.util.addEventListener(window, 'unload', dwr.engine._unloader);
  }

  // Set up a receiver context for this engine instance
  var g = dwr.engine._global;
  if (!g.dwr) {
    g.dwr = {};
  }
  if (!g.dwr._) {
    g.dwr._ = [];
  }
  dwr.engine._instanceId = g.dwr._.length;
  g.dwr._[dwr.engine._instanceId] = {
    handleCallback: dwr.engine.remote.handleCallback,
    handleException: dwr.engine.remote.handleException,
    handleNewWindowName: dwr.engine.remote.handleNewWindowName,
    handleBatchException: dwr.engine.remote.handleBatchException,
    handleFunctionCall: dwr.engine.remote.handleFunctionCall,
    handleObjectCall: dwr.engine.remote.handleObjectCall,
    handleSetCall: dwr.engine.remote.handleSetCall,
    handleFunctionClose: dwr.engine.remote.handleFunctionClose,
    handleObjectCall: dwr.engine.remote.handleObjectCall,
    handleForeign: dwr.engine.remote.handleForeign,
    pollCometDisabled: dwr.engine.remote.pollCometDisabled,
    newObject: dwr.engine.remote.newObject,
    toDomElement: dwr.engine.serialize.toDomElement,
    toDomDocument: dwr.engine.serialize.toDomDocument,
    beginIFrameResponse: dwr.engine.transport.iframe.remote.beginIFrameResponse,
    endIFrameResponse: dwr.engine.transport.iframe.remote.endIFrameResponse,
    _eval: dwr.engine._eval
  };

  /*
   * Run page init code as desired by server
   */

  eval("dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'pageLoaded', [ function() { dwr.engine._ordered = false; }]);");
  
  /**
   * Routines for the DWR pubsub hub
   */
  dwr.hub = {
    /**
     * Publish some data to a given topic
     * @param {Object} topicName The topic to publish to
     * @param {Object} data The data to publish
     */
    publish:function(topicName, data) {
      dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'publish', topicName, data, {});
    },

    /**
     * Subscribe to get notifications of publish events to a given topic
     * @param {String} topicName The topic to subscribe to
     * @param {Function} callback The function to call when a publish happens
     * @param {Object} scope The 'this' object on which the callback executes (optional)
     * @param {Object} subscriberData Data that the subscriber wishes to remember (optional)
     * @return An opaque type for use with unsubscribe
     */
    subscribe:function(topicName, callback, scope, subscriberData) {
      var subscription = "" + dwr.hub._subscriptionId;
      dwr.hub._subscriptionId++;
      dwr.hub._subscriptions[subscription] = {
        callback:callback,
        scope:scope,
        subscriberData:subscriberData
      };
      dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'subscribe', topicName, subscription, {});
      return subscription;
    },

    /**
     * Called by the server: A publish event has happened that we care about
     * @private
     * @param {Object} subscriptionId
     * @param {Object} publishData
     */
    _remotePublish:function(subscriptionId, publishData) {
      var subscriptionData = dwr.hub._subscriptions[subscriptionId];
      if (!subscriptionData) return;
      subscriptionData.callback.call(subscriptionData.scope, publishData, subscriptionData.subscriberData);
    },

    /**
     * Each time we subscribe to something, we use a unique number
     */
    _subscriptionId:0,

    /**
     * We need to remember what we are subscribed to so we can recall the callback
     */
    _subscriptions:{}
  };

/**
 * High level data-sync API for use by Widget libraries like a Dojo-Data-Store.
 * For full documentation see org.directwebremoting.export.Data
 */
dwr.data = {

  /**
   * This is just documentation that defines how the listener parameter must act
   * in order to receive asynchronous updates
   */
  StoreChangeListener:{
    /**
     * Something has removed an item from the store
     * @param {StoreProvider} source The store from which it was moved
     * @param {string} itemId The ID of the item
     */
    itemRemoved:function(source, itemId) { },

    /**
     * Something has added an item to the store
     * @param {StoreProvider} source The store from which it was moved
     * @param {Item} item The thing that has changed
     */
    itemAdded:function(source, item) { },

    /**
     * Something has updated an item in the store
     * @param {StoreProvider} source The store from which it was moved
     * @param {Item} item The thing that has changed
     * @param {string[]} changedAttributes A list of changed attributes. If null then
     * you should assume that everything has changed
     */
    itemChanged:function(source, item, changedAttributes) { }
  },

  /**
   * Create a cache object containing the functions to interact with a server
   * side StoreProvider
   * @param {string} storeId ID of server provided storage
   * @param {dwr.data.StoreChangeListener} listener See server documentation
   * This is likely to be true if dwr.engine.activeReverseAjax == true
   */
  Cache:function(storeId, listener) {
    this.storeId = storeId;
    this.listener = listener;
  }
};

  /**
   * Notes that there is a region of a page that wishes to subscribe to server
   * side data and registers a callback function to receive the data.
   * @param {Object} region filtering and sorting options. Includes:
   * - start: The beginning of the region of specific interest
   * - count: The number of items being viewed
   * - sort: The sort criteria
   * - query: The filter criteria
   * @param {function|object} callbackObj A standard DWR callback object
   * @return 
   */
  dwr.data.Cache.prototype.viewRegion = function(region, callbackObj) {
    if (!region) region = { };
    if (!region.start) region.start = 0;
    if (!region.count) region.count = -1;
    if (!region.sort) region.sort = [];
    else {
      for (var index = 0; index < region.sort.length; index++) {
        if (typeof region.sort[index].descending == "undefined") {
          region.sort[index].descending = false;
        }
      }
    }
    if (!region.query) region.query = {};

    return dwr.engine._execute(dwr.engine._pathToDwrServlet, '__Data', 'viewRegion', [ this.storeId, region, this.listener, callbackObj ]);
  };

  /**
   * As dwr.data.Cache.viewRegion() except that we only want to see a single item.
   * @param {string} itemId ID of object within the given store
   * @param {function|object} callbackObj A standard DWR callback object
   */
  dwr.data.Cache.prototype.viewItem = function(itemId, callbackObj) {
    return dwr.engine._execute(dwr.engine._pathToDwrServlet, '__Data', 'viewItem', [ this.storeId, itemId, this.listener, callbackObj ]);
  };

  /**
   * Undo the action of dwr.data.view()
   * @param {function|object} callbackObj A standard DWR callback object
   */
  dwr.data.Cache.prototype.unsubscribe = function(callbackObj) {
    if (this.listener) {
      return dwr.engine._execute(dwr.engine._pathToDwrServlet, '__Data', 'unsubscribe', [ this.storeId, this.listener, callbackObj ]);
    }
  };

  /**
   * Request an update to server side data
   * @param {Object} items An array of update descriptions
   * @param {function|object} callbackObj A standard DWR callback object
   */
  dwr.data.Cache.prototype.update = function(items, callbackObj) {
    return dwr.engine._execute(dwr.engine._pathToDwrServlet, '__Data', 'update', [ this.storeId, items, callbackObj ]);
  };

})();

 
jsf托管bean不行,但普通java类可以:生成如下:但点击按钮无反应啊!
 
if (typeof dwr == 'undefined' || dwr.engine == undefined) throw new Error('You must include DWR engine before including this file');

(function() {
  if (dwr.engine._getObject("DwrTest") == undefined) {
    var p;
    
    p = {};
    p._path = '/WebProject.war/dwr';

    /**
     * @param {class java.lang.String} p0 a param
     * @param {function|Object} callback callback function or options object
     */
    p.getInfo = function(p0, callback) {
      return dwr.engine._execute(p._path, 'DwrTest', 'getInfo', arguments);
    };

    /**
     * @param {class java.lang.String} p0 a param
     * @param {function|Object} callback callback function or options object
     */
    p.DwrTest = function(p0, callback) {
      return dwr.engine._execute(p._path, 'DwrTest', 'DwrTest', arguments);
    };
    
    dwr.engine._setObject("DwrTest", p);
  }
})();

先测试一把:发现是可以的啊:

Methods For: DwrTest (NewCreator for com.jiang.DwrTest)

To use this class in your javascript you will need the following script includes:

  <script type='text/javascript' src='/WebProject.war/dwr/engine.js'></script>
  <script type='text/javascript' src='/WebProject.war/dwr/interface/DwrTest.js'></script>

In addition there is an optional utility script:

  <script type='text/javascript' src='/WebProject.war/dwr/util.js'></script>

Replies from DWR are shown with a yellow background if they are simple or in an alert box otherwise.
The inputs are evaluated as Javascript so strings must be quoted before execution.

  • getInfo( ); "jiang,hello"
  • DwrTest( );
    (Warning: DwrTest() is excluded: Method access is denied by rules in dwr.xml. See below)

    Other Links

    Fixing Issues

    Warning: No Converter for XXX.

    dwr.xml does not have an allow entry that enables conversion of this type to Javascript. The most common cause of this problem is that XXX is a java bean and bean marshalling has not been enabled. Bean marshalling is disabled by default for security reasons.

    To enable marshalling for a given bean add the following line to the allow section of your dwr.xml file:

    <convert converter="bean" match="XXX"/>
    

    It is also possible to enable marshalling for an entire package or hierachy of packages using the following:

    <convert converter="bean" match="package.name.*"/>
    

    Warning: overloaded methods are not recommended

    Javascript does not support overloaded methods, so the javascript file generated from this class will contain two methods the second of which will replace the first. This is probably not what you wanted.

    It is best to avoid overloaded methods when using DWR.

    Warning: methodName() is excluded:

    The methods may be excluded explicitly with an <exclude> element in dwr.xml or excluded implicitly by not being mentioned in an <include> element. Or the method may be defined in java.lang.Object - methods defined here may not be exported.

    If methods are excluded using <include> or <exclude> then no JavaScript proxy will be generated. To allow testing of methods that should not be accessible, add an init-param to WEB-INF/web.xml with the name/value allowImpossibleTests=true. 


  • 将按钮放到非form中就行了:
  • <h:panelGrid border="0" columns="2" style="padding-left:250px">
    <h:commandButton value="click me test dwr-ajax" rendered="true"
    οnclick="testdwr()" id="dwr" />
    <h:commandButton value="click me test dwr-ajax2" rendered="true"
    οnclick="testdwr()" id="dwr" />
    </h:panelGrid>

 
 
 
 
 

总结:总体配置


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="dwr" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name></display-name>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>

<servlet>
<display-name>DWR Servlet</display-name>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

<filter>
<filter-name>DwrFacesFilter</filter-name>
<filter-class>org.directwebremoting.faces.FacesExtensionFilter</filter-class>
</filter>


<filter-mapping>
<filter-name>DwrFacesFilter</filter-name>
<url-pattern>/dwr/*</url-pattern>
</filter-mapping>

</web-app>


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd">


<dwr>

<init>
<creator id="jsf" class="org.directwebremoting.faces.JsfCreator" />
</init>

<allow>

<create creator="jsf" javascript="TestDWRBean">
<param name="managedBeanName" value="TestDWRBean" />
<param name="class" value="com.jiang.TestDWRBean" />
</create>
</allow>

</dwr>





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值