Added in Bayeux implementation, as per
authorfhanik <fhanik@13f79535-47bb-0310-9956-ffa450edef68>
Tue, 2 Sep 2008 20:00:36 +0000 (20:00 +0000)
committerfhanik <fhanik@13f79535-47bb-0310-9956-ffa450edef68>
Tue, 2 Sep 2008 20:00:36 +0000 (20:00 +0000)
https://issues.apache.org/bugzilla/show_bug.cgi?id=45413
to build,
ant download
ant
ant -f extras.xml bayeux

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@691359 13f79535-47bb-0310-9956-ffa450edef68

56 files changed:
.classpath
COPYRIGHT [new file with mode: 0644]
build.properties.default
build.xml
extras.xml
java/org/apache/cometd/bayeux/Bayeux.java [new file with mode: 0644]
java/org/apache/cometd/bayeux/Channel.java [new file with mode: 0644]
java/org/apache/cometd/bayeux/Client.java [new file with mode: 0644]
java/org/apache/cometd/bayeux/DataFilter.java [new file with mode: 0644]
java/org/apache/cometd/bayeux/Listener.java [new file with mode: 0644]
java/org/apache/cometd/bayeux/Message.java [new file with mode: 0644]
java/org/apache/cometd/bayeux/SecurityPolicy.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/BayeuxException.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/BayeuxRequest.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/BayeuxServlet.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/ChannelImpl.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/ClientImpl.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/HttpError.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/MessageImpl.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/RequestBase.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/RequestFactory.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/TomcatBayeux.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java [new file with mode: 0644]
java/org/apache/tomcat/bayeux/request/PublishRequest.java [new file with mode: 0644]
test/org/apache/cometd/bayeux/samples/EchoChatClient.java [new file with mode: 0644]
webapps/cometd/WEB-INF/filters.json [new file with mode: 0644]
webapps/cometd/WEB-INF/web.xml [new file with mode: 0644]
webapps/cometd/examples/chat/chat.css [new file with mode: 0644]
webapps/cometd/examples/chat/chat.js [new file with mode: 0644]
webapps/cometd/examples/chat/index.html [new file with mode: 0644]
webapps/cometd/examples/dojo/dojo/dojo.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojo/dojo.js.uncompressed.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojo/dojo.xd.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojo/dojo.xd.js.uncompressed.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojox/cometd.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojox/cometd.js.uncompressed.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojox/cometd.xd.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojox/cometd.xd.js.uncompressed.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojox/cometd/session.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojox/cometd/timestamp.js [new file with mode: 0644]
webapps/cometd/examples/dojo/dojox/cometd/timesync.js [new file with mode: 0644]
webapps/cometd/examples/echo/index.html [new file with mode: 0644]
webapps/cometd/examples/simplechat/cometdchat.htm [new file with mode: 0644]
webapps/cometd/examples/tictactoe/resources/0.gif [new file with mode: 0644]
webapps/cometd/examples/tictactoe/resources/1.gif [new file with mode: 0644]
webapps/cometd/examples/tictactoe/resources/10.gif [new file with mode: 0644]
webapps/cometd/examples/tictactoe/resources/blank.gif [new file with mode: 0644]
webapps/cometd/examples/tictactoe/resources/tictactoe.css [new file with mode: 0644]
webapps/cometd/examples/tictactoe/ttt1.htm [new file with mode: 0644]
webapps/cometd/examples/timesync/index.html [new file with mode: 0644]
webapps/cometd/index.html [new file with mode: 0644]
webapps/docs/changelog.xml

index 334c80e..889a4b2 100644 (file)
@@ -1,27 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You 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.
--->
 <classpath>
-        <classpathentry excluding="**/.svn/**|org/apache/naming/factory/webservices/" kind="src" path="java"/>
+       <classpathentry excluding="**/.svn/**|org/apache/naming/factory/webservices/" kind="src" path="java"/>
        <classpathentry kind="src" path="test"/>
        <classpathentry kind="src" path="webapps/examples/WEB-INF/classes"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="var" path="TOMCAT_LIBS_BASE/eclipse/plugins/org.eclipse.jdt.core_3.3.1.v_780_R33x.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
        <classpathentry kind="var" path="ANT_HOME/lib/ant.jar"/>
+       <classpathentry kind="var" path="TOMCAT_LIBS_BASE/json-20070829/json.jar"/>
        <classpathentry kind="output" path=".settings/output"/>
 </classpath>
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..0453866
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,5 @@
+****For the bayeux implementation*********\r
+* Copyright 2007-2008 Guy Molinari\r
+* Copyright 2007-2008 Filip Hanik\r
+* Copyright 2007 Dojo Foundation\r
+* Copyright 2007 Mort Bay Consulting Pty. Ltd.\r
index 096ba49..fd57b98 100644 (file)
@@ -98,3 +98,8 @@ commons-daemon.lib=${commons-daemon.home}
 commons-daemon.jar=${commons-daemon.lib}/commons-daemon.jar
 commons-daemon.loc=${base-commons.loc}/daemon/binaries/commons-daemon-1.0.1.tar.gz
 commons-daemon.jsvc.tar.gz=${commons-daemon.lib}/bin/jsvc.tar.gz
+
+# ----- JSON Libraries, 
+json-lib.home=${base.path}/json-20070829
+json-lib.lib=http://repo1.maven.org/maven2/org/json/json/20070829/json-20070829.jar
+json-lib.jar=json.jar
index 0a100dc..92e3578 100644 (file)
--- a/build.xml
+++ b/build.xml
 <!-- Comment this in to show unchecked warnings:     <compilerarg value="-Xlint:unchecked"/> -->
       <classpath refid="tomcat.classpath" />
       <exclude name="org/apache/naming/factory/webservices/**" />
+      <exclude name="org/apache/tomcat/bayeux/**" />
+      <exclude name="org/apache/cometd/**" />
     </javac>
     <tstamp>
       <format property="TODAY" pattern="MMM d yyyy" locale="en"/>
index 6d6b44a..b7d7bbb 100644 (file)
   <property name="tomcat-juli.jar" value="${tomcat.extras}/tomcat-juli.jar"/>
   <property name="tomcat-juli-adapters.jar" value="${tomcat.extras}/tomcat-juli-adapters.jar"/>
   <property name="catalina-ws.jar" value="${tomcat.extras}/catalina-ws.jar"/>
+
+  <property name="cometd-api.jar" value="${tomcat.extras}/cometd-api.jar"/>
+  <property name="tomcat-bayeux.jar" value="${tomcat.extras}/tomcat-bayeux.jar"/>
+  <property name="cometd.war" value="${tomcat.extras}/cometd.war"/>
+
        
   <!-- Classpath -->
   <path id="tomcat.classpath">
        
   </target>
 
-  <target name="extras" depends="prepare,commons-logging,webservices">
+  <target name="bayeux">
+    <mkdir dir="${tomcat.extras}"/>
+
+    <copy todir="${tomcat.extras}" file="${json-lib.home}/${json-lib.jar}"/>
+    <!-- Classpath -->
+    <path id="tomcat.bayeux.classpath">
+      <pathelement path="${tomcat.classpath}"/>
+      <pathelement path="${json-lib.home}/${json-lib.jar}"/>
+    </path>
+
+    <!-- compile org.apache.tomcat.bayeux -->
+    <!-- compile org.apache.cometd -->
+    <javac srcdir="java" destdir="${tomcat.classes}"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           source="${compile.source}"
+           optimize="${compile.optimize}">
+      <classpath refid="tomcat.bayeux.classpath" />
+      <include name="org/apache/tomcat/bayeux/**" />
+      <include name="org/apache/cometd/**" />
+    </javac>
+    <!-- Cometd API JAR File -->
+    <jar jarfile="${cometd-api.jar}">
+      <fileset dir="${tomcat.classes}">
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+        <include name="org/apache/cometd/**" />
+      </fileset>
+    </jar>
+    <!-- Cometd API JAR File -->
+    <jar jarfile="${tomcat-bayeux.jar}">
+      <fileset dir="${tomcat.classes}">
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+        <include name="org/apache/tomcat/bayeux/**" />
+      </fileset>
+    </jar>
+
+    <zip zipfile="${cometd.war}">
+      <fileset dir="${basedir}/webapps/cometd">
+        <include name="**/**"/>
+      </fileset>
+    </zip>
+    <checksum file="${cometd-api.jar}" forceOverwrite="yes" fileext=".md5" />
+    <checksum file="${tomcat-bayeux.jar}" forceOverwrite="yes" fileext=".md5" />
+    <checksum file="${tomcat.extras}/${json-lib.jar}" forceOverwrite="yes" fileext=".md5" />
+    <echo>You've built the Tomcat Bayeux libraries, simply add the following libraries to your CATALINA_HOME/lib directory:
+          ${cometd-api.jar}
+          ${tomcat-bayeux.jar} 
+          ${tomcat.extras}/${json-lib.jar}
+To run the sample application, copy the following applications into your CATALINA_BASE/webapps directory
+          ${cometd.war}
+    </echo>
+  </target>
+
+  <target name="extras" depends="prepare,commons-logging,webservices,bayeux">
   </target>
 
   <!-- Download and dependency building -->
diff --git a/java/org/apache/cometd/bayeux/Bayeux.java b/java/org/apache/cometd/bayeux/Bayeux.java
new file mode 100644 (file)
index 0000000..5385d32
--- /dev/null
@@ -0,0 +1,242 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.cometd.bayeux;\r
+\r
+import java.util.List;\r
+\r
+/** Bayeux Interface.<br/>\r
+ * This interface represents the server side API for the Bayeux messaging protocol.\r
+ * Bayeux is a simple subscribe/publish/receive methodology, not far from JMS, but much simplified.<br/>\r
+ * It is used both by the actual implementation and by server side clients.<br/>\r
+ * Server side clients use this to create, retrieve and subscribe to channels.\r
+ * Server side clients are represented, just like remote clients, through the Client interface.\r
+ * <br/>\r
+ * The Bayeux implementations is intended to be thread safe and multiple threads may simultaneously call Bayeux methods.\r
+ * <br/>\r
+ * The Bayeux object, is the starting point for any cometd application relying on the Bayeux object.\r
+ * Dependent on the container, the Bayeux object will be stored in the <code>javax.servlet.ServletContext</code> object\r
+ * as an attribute under the name <code>Bayeux.DOJOX_COMETD_BAYEUX</code><br/>\r
+ * To retrieve this object, one would simply call<br/>\r
+ * <code>Bayeux bx = (Bayeux)getServletContext().getAttribute(Bayeux.DOJOX_COMETD_BAYEUX);\r
+ * <br/><br/>\r
+ * The Bayeux protocol is pretty straight forward and includes a bunch of messaging that is not needed to be known to clients,\r
+ * both server side and remote clients.\r
+ * This object gets initialized by a container dependent servlet, and the servlet then handles all Bayeux communication from the client.\r
+ * Remote messsages are delivered to channels, and to server side clients using the <code>Listener</code> interface.<br/>\r
+ * <br/>\r
+ * A <code>Bayeux session</code> is active as long as the webapp hosting the Bayeux object is active.<br/>\r
+ * When the webapplication shuts down, the Bayeux object will unsubscribe all clients and remove all the active channels.\r
+ * \r
+ * @author Greg Wilkins\r
+ * @author Filip Hanik\r
+ */\r
+public interface Bayeux {\r
+    \r
+    /**Meta definitions for channels*/\r
+    public static final String META="/meta";\r
+    /**Meta definitions for channels*/\r
+    public static final String META_SLASH="/meta/";\r
+    /**Meta definitions for channels - connect message*/\r
+    public static final String META_CONNECT="/meta/connect";\r
+    /**Meta definitions for channels - client messsage*/\r
+    public static final String META_CLIENT="/meta/client";\r
+    /**Meta definitions for channels - disconnect messsage*/\r
+    public static final String META_DISCONNECT="/meta/disconnect";\r
+    /**Meta definitions for channels - handshake messsage*/\r
+    public static final String META_HANDSHAKE="/meta/handshake";\r
+    /**Meta definitions for channels - ping messsage*/\r
+    public static final String META_PING="/meta/ping";\r
+    /**Meta definitions for channels - reconnect messsage\r
+     * @deprecated\r
+     */\r
+    public static final String META_RECONNECT="/meta/reconnect";\r
+    /**Meta definitions for channels - status messsage*/\r
+    public static final String META_STATUS="/meta/status";\r
+    /**Meta definitions for channels - subscribe messsage*/\r
+    public static final String META_SUBSCRIBE="/meta/subscribe";\r
+    /**Meta definitions for channels - unsubscribe messsage*/\r
+    public static final String META_UNSUBSCRIBE="/meta/unsubscribe";\r
+    /*Field names inside Bayeux messages*/\r
+    /**Field names inside Bayeux messages - clientId field*/\r
+    public static final String CLIENT_FIELD="clientId";\r
+    /**Field names inside Bayeux messages - data field*/\r
+    public static final String DATA_FIELD="data";\r
+    /**Field names inside Bayeux messages - channel field*/\r
+    public static final String CHANNEL_FIELD="channel";\r
+    /**Field names inside Bayeux messages - id field*/\r
+    public static final String ID_FIELD="id";\r
+    /**Field names inside Bayeux messages - error field*/\r
+    public static final String ERROR_FIELD="error";\r
+    /**Field names inside Bayeux messages - timestamp field*/\r
+    public static final String TIMESTAMP_FIELD="timestamp";\r
+    /**Field names inside Bayeux messages - transport field*/\r
+    public static final String TRANSPORT_FIELD="transport";\r
+    /**Field names inside Bayeux messages - advice field*/\r
+    public static final String ADVICE_FIELD="advice";\r
+    /**Field names inside Bayeux messages - successful field*/\r
+    public static final String SUCCESSFUL_FIELD="successful";\r
+    /**Field names inside Bayeux messages - subscription field*/\r
+    public static final String SUBSCRIPTION_FIELD="subscription";\r
+    /**Field names inside Bayeux messages - ext field*/\r
+    public static final String EXT_FIELD="ext";\r
+    /**Field names inside Bayeux messages - connectionType field*/\r
+    public static final String CONNECTION_TYPE_FIELD="connectionType";\r
+    /**Field names inside Bayeux messages - version field*/\r
+    public static final String VERSION_FIELD="version";\r
+    /**Field names inside Bayeux messages - minimumVersion field*/\r
+    public static final String MIN_VERSION_FIELD="minimumVersion";\r
+    /**Field names inside Bayeux messages - supportedConnectionTypes field*/\r
+    public static final String SUPP_CONNECTION_TYPE_FIELD="supportedConnectionTypes";\r
+    /**Field names inside Bayeux messages - json-comment-filtered field*/\r
+    public static final String JSON_COMMENT_FILTERED_FIELD="json-comment-filtered";\r
+    /**Field names inside Bayeux messages - reconnect field*/\r
+    public static final String RECONNECT_FIELD = "reconnect";\r
+    /**Field names inside Bayeux messages - interval field*/\r
+    public static final String INTERVAL_FIELD = "interval";\r
+    /**Field values inside Bayeux messages - retry response*/\r
+    public static final String RETRY_RESPONSE = "retry";\r
+    /**Field values inside Bayeux messages - handshake response*/\r
+    public static final String HANDSHAKE_RESPONSE = "handshake";\r
+    /**Field values inside Bayeux messages - none response*/\r
+    public static final String NONE_RESPONSE = "none";\r
+    /**Service channel names-starts with*/\r
+    public static final String SERVICE="/service";\r
+    /**Service channel names-trailing slash*/\r
+    public static final String SERVICE_SLASH="/service/";\r
+    /*Transport types*/\r
+    /**Transport types - long polling*/\r
+    public static final String TRANSPORT_LONG_POLL="long-polling";\r
+    /**Transport types - callback polling*/\r
+    public static final String TRANSPORT_CALLBACK_POLL="callback-polling";\r
+    /**Transport types - iframe*/\r
+    public static final String TRANSPORT_IFRAME="iframe";\r
+    /**Transport types - flash*/\r
+    public static final String TRANSPORT_FLASH="flash";\r
+    /** ServletContext attribute name used to obtain the Bayeux object */\r
+    public static final String DOJOX_COMETD_BAYEUX="dojox.cometd.bayeux";\r
+    /*http field names*/\r
+    /**http helpers - text/json content type*/\r
+    public static final String JSON_CONTENT_TYPE="text/json";\r
+    /**http helpers - parameter name for json message*/\r
+    public static final String MESSAGE_PARAMETER="message";\r
+    /**http helpers - name of the jsonp parameter*/\r
+    public static final String JSONP_PARAMETER="jsonp";\r
+    /**http helpers - default name of the jsonp callback function*/\r
+    public static final String JSONP_DEFAULT_NAME="jsonpcallback";\r
+\r
+    /*--Client----------------------------------------------------------- */\r
+    /**\r
+     * Creates a new server side client. This method is to be invoked\r
+     * by server side objects only. You cannot create a remote client by using this method.\r
+     * A client represents an entity that can subscribe to channels and publish and receive messages\r
+     * through these channels\r
+     * @param idprefix String - the prefix string for the id generated, can be null\r
+     * @param listener Listener - a callback object to be called when messages are to be delivered to the new client\r
+     * @return Client - returns an implementation of the client interface.\r
+     */\r
+    public Client newClient(String idprefix, Listener listener);\r
+\r
+    /**\r
+     * retrieve a client based on an ID. Will return null if the client doesn't exist.\r
+     * @param clientid String\r
+     * @return Client-null if the client doesn't exist.returns the client if it does.\r
+     */\r
+    public Client getClient(String clientid);\r
+    \r
+    /**\r
+     * Returns a non modifiable list of all the clients that are currently active\r
+     * in this Bayeux session\r
+     * @return List<Client> - a list containing all clients. The List can not be modified.\r
+     */\r
+    public List<Client> getClients();\r
+    \r
+    /**\r
+     * Returns true if a client with the given id exists.<br/>\r
+     * Same as executing <code>getClient(id)!=null</code>.\r
+     * @param clientId String\r
+     * @return boolean - true if the client exists\r
+     */\r
+    public boolean hasClient(String clientId);\r
+    \r
+    /**\r
+     * Removes the client all together.\r
+     * This will unsubscribe the client to any channels it may be subscribed to\r
+     * and remove it from the list.\r
+     * @param client Client\r
+     * @return Client - returns the client that was removed, or null if no client was removed.\r
+     */\r
+    public Client remove(Client client);\r
+\r
+    \r
+    /*--Channel---------------------------------------------------------- */\r
+    /**\r
+     * Returns the channel for a given channel id.\r
+     * If the channel doesn't exist, and the <code>create</code> parameter is set to true,\r
+     * the channel will be created and added to the list of active channels.<br/>\r
+     * if <code>create</code> is set to false, and the channel doesn't exist, null will be returned.\r
+     * @param channelId String - the id of the channel to be retrieved or created\r
+     * @param create boolean - true if the Bayeux impl should create the channel\r
+     * @return Channel - null if <code>create</code> is set to false and the channel doesn't exist, \r
+     * otherwise it returns a channel object.\r
+     */\r
+    public Channel getChannel(String channelId, boolean create);\r
+    \r
+    /**\r
+     * Returns a list of currently active channels in this Bayeux session.\r
+     * @return List<Channel>\r
+     */\r
+    public List<Channel> getChannels();\r
+\r
+    /**\r
+     * Removes a channel from the Bayeux object.\r
+     * This will also unsubscribe all the clients currently subscribed to the\r
+     * the channel.\r
+     * @param channel Channel - the channel to be removed\r
+     * @return Channel - returns the channel that was removed, or null if no channel was removed.\r
+     */\r
+    public Channel remove(Channel channel);\r
+\r
+    /**\r
+     * returns true if a channel with the given channelId exists.\r
+     * <br/>Same as executing <code>Bayeux.getChannel(channelId,false)!=null</code>\r
+     * @param channelId String\r
+     * @return boolean - true if the channel exists.\r
+     */\r
+    public boolean hasChannel(String channelId);\r
+\r
+    /* --Message---------------------------------------------------------- */\r
+    /**\r
+     * Creates a new message to be sent by a server side client.\r
+     * @return Message - returns a new Message object, that has a unique id.\r
+     */\r
+    public Message newMessage(Client from);\r
+\r
+\r
+    /*--Security policy----------------------------------------------------------- */\r
+    /**\r
+     * Returns the security policy associated with this Bayeux session\r
+     * @return SecurityPolicy\r
+     */\r
+    public SecurityPolicy getSecurityPolicy();\r
+   \r
+    /**\r
+     * Sets the security policy to be used in this Bayeux session\r
+     * @param securityPolicy SecurityPolicy\r
+     */\r
+    public void setSecurityPolicy(SecurityPolicy securityPolicy);\r
+\r
+}
\ No newline at end of file
diff --git a/java/org/apache/cometd/bayeux/Channel.java b/java/org/apache/cometd/bayeux/Channel.java
new file mode 100644 (file)
index 0000000..34af328
--- /dev/null
@@ -0,0 +1,103 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.cometd.bayeux;\r
+\r
+import java.util.List;\r
+\r
+/** \r
+ * A Bayeux Channel represents a channel used to receive messages from and to publish messages to.\r
+ * In order to publish messages to or receive messages from, one must subscribe to the channel.\r
+ * This is easily done by invoking the <code>subscribe</code> method.\r
+ * A channel is created by calling the <code>Bayeux.getChannel(channelId,true)</code> method.\r
+ * A channel can be created either server side by invoking the getChannel, or client side\r
+ * by using the /meta/subscribe message without a wildcard.\r
+ * @author Greg Wilkins\r
+ * @author Filip Hanik\r
+ */\r
+public interface Channel\r
+{\r
+    /**\r
+     * Returns the id for this channel. The id is unique within bayeux session.\r
+     * @return String - will never be null.\r
+     */\r
+    public String getId();\r
+\r
+    /** \r
+     * Publishes a message to all the subscribers of this channel.\r
+     * The <code>from</code> is contained within the message, by calling\r
+     * <code>msg.getClient()</code>\r
+     * @param data - the message to be published, can not be null.\r
+     */\r
+    public void publish(Message msg);\r
+    \r
+    /** \r
+     * Publishes more than one message to all the subscribers of this channel.\r
+     * The <code>from</code> is contained within the message, by calling\r
+     * <code>msg[x].getClient()</code>\r
+     * @param data - the message to be published, can not be null.\r
+     */\r
+    public void publish(Message[] msgs);\r
+\r
+    /** \r
+     * Non persistent channels are removed when the last subscription is\r
+     * removed. Persistent channels survive periods without any subscribers.\r
+     * @return true if the Channel will persist without any subscription.\r
+     */\r
+    public boolean isPersistent();\r
+    \r
+    /**\r
+     * @param persistent true if the Channel will persist without any subscription.\r
+     * @see isPersistent\r
+     */\r
+    public void setPersistent(boolean persistent);\r
+    \r
+    /** \r
+     * Subscribes a client to a channel.\r
+     * @param subscriber - the client to be subscribed. If the client\r
+     * already is subscribed, this call will not create a duplicate subscription.\r
+     */\r
+    public void subscribe(Client subscriber);\r
+\r
+    /** \r
+     * Unsubscribes a client from a channel\r
+     * @param subscriber - the client to be subscribed.\r
+     * @return - returns the client that was unsubscribed, or null if the client wasn't subscribed.\r
+     */\r
+    public Client unsubscribe(Client subscriber);\r
+\r
+    /**\r
+     * returns a non modifiable list of all the subscribers to this \r
+     * channel.\r
+     * @return a list of subscribers\r
+     */\r
+    public List<Client> getSubscribers();\r
+    \r
+    /**\r
+     * Adds a data filter to this channel. All messages received by this channel \r
+     * will run through this filter.\r
+     * @param filter Filter\r
+     */\r
+    public void addFilter(DataFilter filter);\r
+    \r
+    /**\r
+     * Removes a filter from this channel.\r
+     * returns the filter that was removed, or null if the filter wasn't in the channel.\r
+     * @param filter Filter\r
+     * @return Filter - null if no filter was removed otherwise it returns the filter that was removed.\r
+     */\r
+    public DataFilter removeFilter(DataFilter filter);\r
+}
\ No newline at end of file
diff --git a/java/org/apache/cometd/bayeux/Client.java b/java/org/apache/cometd/bayeux/Client.java
new file mode 100644 (file)
index 0000000..3d6a8d8
--- /dev/null
@@ -0,0 +1,91 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.apache.cometd.bayeux;\r
+\r
+\r
+\r
+/** A Bayeux Client.\r
+ * <p>\r
+ * A client may subscribe to channels and publish messages to channels.\r
+ * Client instances should not be directly created by uses, but should \r
+ * be obtained via the {@link Bayeux#getClient(String)} or {@link Bayeux#newClient(String, Listener)}\r
+ * methods.\r
+ * </p>\r
+ * <p>\r
+ * Three types of client may be represented by this interface:<nl>\r
+ * <li>The server representation of a remote client connected via HTTP, \r
+ *     automatically created by the Bayeux server when a connect message comes in</li>\r
+ * <li>A server side client, created by the application using the {@link Bayeux#newClient(String, Listener)} method</li>\r
+ * <li>A java client connected to a remote Bayeux server - not implemented</li>\r
+ * </nl>\r
+ * @author Greg Wilkins\r
+ * @author Filip Hanik\r
+ */\r
+public interface Client\r
+{\r
+    /**\r
+     * Returns a unique id for this client. The id is unique within this Bayeux session.\r
+     * @return String - will not be null\r
+     */\r
+    public String getId();\r
+\r
+    /**\r
+     * Returns true if this client is holding messages to be delivered to the remote client.\r
+     * This method always returns false for local clients, since messages are delivered instantly using the \r
+     * Listener(callback) object\r
+     * @return boolean\r
+     */\r
+    public boolean hasMessages();\r
+\r
+    /** \r
+     * Deliver a message to this client only\r
+     * Deliver a message directly to the client. The message is not \r
+     * filtered or published to a channel.\r
+     * @param message\r
+     */\r
+    public void deliver(Message message);\r
+\r
+    /** \r
+     * Deliver a batch of messages to this client only\r
+     * Deliver a batch messages directly to the client. The messages are not \r
+     * filtered or published to a channel.\r
+     * @param message\r
+     */\r
+    public void deliver(Message[] message);\r
+\r
+    /**\r
+     * @return True if the client is local. False if this client is either a remote HTTP client or\r
+     * a java client to a remote server. \r
+     */\r
+    public boolean isLocal();\r
+    \r
+    /**\r
+     * Starts a batch, no messages will be delivered until endBatch is called.\r
+     * Batches can be nested, and messages will only be delivered after\r
+     * the last endBatch has been called.\r
+     */\r
+    public void startBatch();\r
+    \r
+    /**\r
+     * Ends a batch. since batches can be nested, messages will only be delivered\r
+     * after the endBatch has been called as many times as startBatch has.\r
+     */\r
+    public void endBatch();\r
+    \r
+    \r
+}
\ No newline at end of file
diff --git a/java/org/apache/cometd/bayeux/DataFilter.java b/java/org/apache/cometd/bayeux/DataFilter.java
new file mode 100644 (file)
index 0000000..d632aec
--- /dev/null
@@ -0,0 +1,38 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.cometd.bayeux;\r
+\r
+/** \r
+ * Data Filter<br/>\r
+ * Data filters are used to transform data as it is sent to a Channel.\r
+ * Messages are filtered as the message is published to a channel, invoking the \r
+ * {@link Channel#publish(Message)} method.<br/>\r
+ * This method gets invoked in two different scenarios, the first being when a message is received from\r
+ * a remote client, and the Bayeux implementation invokes the publish method directly.\r
+ * The second scenario is when a local client invokes {@link Channel#publish(Message)} directly in the local JVM.\r
+ * @author Greg Wilkins\r
+ * @author Filip Hanik\r
+ *\r
+ */\r
+public interface DataFilter\r
+{\r
+    /**\r
+     * Runs a message through the filter. Filtering can only modify an existing object, it can not replace it.\r
+     * @param data Message - the message to be filtered, may not be null\r
+     */\r
+    public void filter(Message data);\r
+}\r
diff --git a/java/org/apache/cometd/bayeux/Listener.java b/java/org/apache/cometd/bayeux/Listener.java
new file mode 100644 (file)
index 0000000..cce2018
--- /dev/null
@@ -0,0 +1,45 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.cometd.bayeux;\r
+\r
+/** \r
+ * Cometd Listener interface.<br/>\r
+ * For local clients, in order to receive messages, they pass in a callback object\r
+ * when the local client is created using the {@link Bayeux#newClient(String,Listener)} method.\r
+ * This callback object, implementing the Listener interface, is used to deliver messages to local, in JVM, clients.\r
+ * @author Greg Wilkins\r
+ * @author Filip Hanik\r
+ *\r
+ */\r
+public interface Listener\r
+{\r
+    /**\r
+     * This method is called when the client is removed (explicitly or from a timeout)\r
+     * @param timeout - true if the client was removed from a timeout\r
+     * false if it was removed explicitly.\r
+     */\r
+    public void removed(boolean timeout);\r
+    \r
+    /**\r
+     * Invoked when a message is delivered to the client.\r
+     * The message contains the message itself, as well as what channel this message came through\r
+     * and who the sender is. If someone invoked {@link Client#deliver(Message)} then the channel reference will\r
+     * be null.\r
+     * @param msg \r
+     */\r
+    public void deliver(Message[] msg);\r
+}\r
diff --git a/java/org/apache/cometd/bayeux/Message.java b/java/org/apache/cometd/bayeux/Message.java
new file mode 100644 (file)
index 0000000..26ba640
--- /dev/null
@@ -0,0 +1,68 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.cometd.bayeux;\r
+\r
+import java.util.Map;\r
+\r
+/** \r
+ * A Bayeux Message<br/>\r
+ * A Bayeux message is a Map of String/Object key value pairs representing the data in the message.\r
+ * The message contains information about the channel it was published through and who the sender was\r
+ * \r
+ * @author Greg Wilkins\r
+ * @author Filip Hanik\r
+ */\r
+public interface Message extends Map<String,Object>\r
+{\r
+    /**\r
+     * Returns a reference to the client that sent this message\r
+     * @return Client - may be null\r
+     */\r
+    public Client getClient();\r
+    /**\r
+     * Returns a reference to the channel that this message was published throuhg\r
+     * @return Channel - may be null\r
+     */\r
+    public Channel getChannel();\r
+    /**\r
+     * Returns the unique id of this message\r
+     * @return String\r
+     */\r
+    public String getId();\r
+    \r
+    /**\r
+     * Sets the time to live in milliseconds. If the message hasn't been delivered \r
+     * when the time passed after the creation time is longer than the TTL the message will\r
+     * expire and removed from any delivery queues.\r
+     * @param ttl long\r
+     */\r
+    public void setTTL(long ttl);\r
+    \r
+    /**\r
+     * Returns the time to live (in milliseconds) for this message\r
+     * @return long\r
+     */\r
+    public long getTTL();\r
+    \r
+    /**\r
+     * returns the timestamp in milliseconds(System.currentTimeMillis()) of when this message was created.\r
+     * @return long\r
+     */\r
+    public long getCreationTime();\r
+}\r
+\r
+\r
diff --git a/java/org/apache/cometd/bayeux/SecurityPolicy.java b/java/org/apache/cometd/bayeux/SecurityPolicy.java
new file mode 100644 (file)
index 0000000..930f605
--- /dev/null
@@ -0,0 +1,28 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.cometd.bayeux;\r
+\r
+/**\r
+ * @author Greg Wilkins\r
+ */\r
+public interface SecurityPolicy\r
+{\r
+    boolean canHandshake(Message message);\r
+    boolean canCreate(Client client,String channel,Message message);\r
+    boolean canSubscribe(Client client,String channel,Message messsage);\r
+    boolean canPublish(Client client,String channel,Message messsage);\r
+}\r
diff --git a/java/org/apache/tomcat/bayeux/BayeuxException.java b/java/org/apache/tomcat/bayeux/BayeuxException.java
new file mode 100644 (file)
index 0000000..d201c5e
--- /dev/null
@@ -0,0 +1,39 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+/**\r
+ * \r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ */\r
+public class BayeuxException extends Exception {\r
+    public BayeuxException() {\r
+        super();\r
+    }\r
+\r
+    public BayeuxException(String message) {\r
+        super(message);\r
+    }\r
+\r
+    public BayeuxException(String message, Throwable cause) {\r
+        super(message, cause);\r
+    }\r
+\r
+    public BayeuxException(Throwable cause) {\r
+        super(cause);\r
+    }\r
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/bayeux/BayeuxRequest.java b/java/org/apache/tomcat/bayeux/BayeuxRequest.java
new file mode 100644 (file)
index 0000000..6e93c99
--- /dev/null
@@ -0,0 +1,54 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+\r
+import org.apache.tomcat.bayeux.HttpError;\r
+\r
+/**\r
+ * An interface that defines methods for managing Bayeux request meta \r
+ * messages.\r
+ *\r
+ * @author Guy A. Molinari\r
+ * @author Filip Hanik\r
+ * @version 0.9\r
+ */\r
+public interface BayeuxRequest {\r
+\r
+    public static final String LAST_REQ_ATTR = "org.apache.cometd.bayeux.last_request";\r
+    public static final String CURRENT_REQ_ATTR = "org.apache.cometd.bayeux.current_request";\r
+    public static final String JSON_MSG_ARRAY = "org.apache.cometd.bayeux.json_msg_array";\r
+\r
+    /**\r
+     * Validates a specific request. \r
+     * This method must be called prior to process()\r
+     * as a request can do pre processing in the validate method.\r
+     * <br/>\r
+     * Should the validation fail, an error object is returned \r
+     * containing an error message, and potentially a stack trace\r
+     * if an exception was generated\r
+     * @return HttpError - null if no error was detected, an HttpError object containing information about the error.\r
+     */\r
+    public HttpError validate();\r
+    \r
+    /**\r
+     * processes a remote client Bayeux message\r
+     * @param prevops - the operation requested by the previous request, in case of chained requests.\r
+     * @return int - returns the interest operation for a CometEvent. Currently not used\r
+     * @throws BayeuxException - if an error was detected, and the appropriate error response couldn't be delivered to the client. \r
+     */\r
+    public int process(int prevops) throws BayeuxException;\r
+}\r
diff --git a/java/org/apache/tomcat/bayeux/BayeuxServlet.java b/java/org/apache/tomcat/bayeux/BayeuxServlet.java
new file mode 100644 (file)
index 0000000..c230336
--- /dev/null
@@ -0,0 +1,236 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+\r
+import java.io.IOException;\r
+import javax.servlet.ServletConfig;\r
+import javax.servlet.ServletContext;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.ServletRequest;\r
+import javax.servlet.ServletResponse;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.catalina.CometProcessor;\r
+import org.apache.juli.logging.Log;\r
+import org.apache.juli.logging.LogFactory;\r
+import org.json.JSONArray;\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+\r
+/**\r
+ * \r
+ * @author Filip Hanik\r
+ * @author Guy Molinari\r
+ * @version 1.0\r
+ */\r
+public class BayeuxServlet implements CometProcessor {\r
+\r
+    /**\r
+     * Attribute to hold the TomcatBayeux object in the servlet context\r
+     */\r
+    public static final String TOMCAT_BAYEUX_ATTR = Bayeux.DOJOX_COMETD_BAYEUX;\r
+    \r
+    /**\r
+     * Logger object\r
+     */\r
+    protected static Log log = LogFactory.getLog(BayeuxServlet.class);\r
+\r
+    /**\r
+     * Servlet config - for future use\r
+     */\r
+    protected ServletConfig servletConfig;\r
+    \r
+    /**\r
+     * Reference to the global TomcatBayeux object\r
+     */\r
+    protected TomcatBayeux tb;\r
+    \r
+    /**\r
+     * Upon servlet destruction, the servlet will clean up the \r
+     * TomcatBayeux object and terminate any outstanding events.\r
+     */\r
+    public void destroy() {\r
+        servletConfig = null;\r
+        //to do, close all outstanding comet events\r
+        //tb.destroy();\r
+        tb = null;//TO DO, close everything down\r
+        \r
+    }\r
+    \r
+    /**\r
+     * Returns the preconfigured connection timeout.\r
+     * If no timeout has been configured as a servlet init parameter named <code>timeout</code>\r
+     * then the default of 2min will be used.\r
+     * @return int - the timeout for a connection in milliseconds\r
+     */\r
+    protected int getTimeout() {\r
+        String timeoutS = servletConfig.getInitParameter("timeout");\r
+        int timeout = 120*1000; //2 min\r
+        try {\r
+            timeout = Integer.parseInt(timeoutS);\r
+        }catch (NumberFormatException nfe) {\r
+            //ignore, we have a default value\r
+        }\r
+        return timeout;\r
+    }\r
+    \r
+    protected int getReconnectInterval() {\r
+        String rs = servletConfig.getInitParameter("reconnectInterval");\r
+        int rct = 5000; //5 seconds\r
+        try {\r
+            rct = Integer.parseInt(rs);\r
+        }catch (NumberFormatException nfe) {\r
+            //ignore, we have a default value\r
+        }\r
+        return rct;\r
+    }\r
+\r
+\r
+    public void event(CometEvent cometEvent) throws IOException, ServletException {\r
+        CometEvent.EventType type = cometEvent.getEventType();\r
+        if (log.isDebugEnabled()) {\r
+            log.debug("["+Thread.currentThread().getName()+"] Received Comet Event type="+type+" subtype:"+cometEvent.getEventSubType());\r
+        }\r
+        synchronized (cometEvent) {\r
+            if (type==CometEvent.EventType.BEGIN) {\r
+                //begin event, set the timeout\r
+                cometEvent.setTimeout(getTimeout());\r
+                //checkBayeux(cometEvent); - READ event should always come\r
+            } else if (type==CometEvent.EventType.READ) {\r
+                checkBayeux(cometEvent);\r
+            } else if (type==CometEvent.EventType.ERROR) {\r
+                tb.remove(cometEvent);\r
+                cometEvent.close();\r
+            } else if (type==CometEvent.EventType.END) {\r
+                tb.remove(cometEvent);\r
+                cometEvent.close();\r
+            }//end if\r
+            \r
+        }//synchronized\r
+    }//event\r
+\r
+    /**\r
+     * \r
+     * @param cometEvent CometEvent\r
+     * @return boolean - true if we comet event stays open\r
+     * @throws IOException\r
+     * @throws UnsupportedOperationException\r
+     */\r
+    protected void checkBayeux(CometEvent cometEvent) throws IOException, UnsupportedOperationException {\r
+        //we actually have data.\r
+        //data can be text/json or \r
+        if (Bayeux.JSON_CONTENT_TYPE.equals(cometEvent.getHttpServletRequest().getContentType())) {\r
+            //read and decode the bytes according to content length\r
+            log.warn("["+Thread.currentThread().getName()+"] JSON encoding not supported, will throw an exception and abort the request.");\r
+            int contentlength = cometEvent.getHttpServletRequest().getContentLength();\r
+            throw new UnsupportedOperationException("Decoding "+Bayeux.JSON_CONTENT_TYPE+" not yet implemented.");\r
+        } else { //GET method or application/x-www-form-urlencoded\r
+            String message = cometEvent.getHttpServletRequest().getParameter(Bayeux.MESSAGE_PARAMETER);\r
+            if (log.isTraceEnabled()) {\r
+                log.trace("["+Thread.currentThread().getName()+"] Received JSON message:"+message);\r
+            }\r
+            try {\r
+                int action = handleBayeux(message, cometEvent);\r
+                if (log.isDebugEnabled()) {\r
+                    log.debug("["+Thread.currentThread().getName()+"] Bayeux handling complete, action result="+action);\r
+                }\r
+                if (action<=0) {\r
+                    cometEvent.close();\r
+                }\r
+            }catch (Exception x) {\r
+                x.printStackTrace();\r
+                tb.remove(cometEvent);\r
+                log.error(x);\r
+                cometEvent.close();\r
+            }\r
+        }\r
+    }\r
+    \r
+    protected int handleBayeux(String message, CometEvent event) throws IOException, ServletException {\r
+        int result = 0;\r
+        if (message==null || message.length()==0) return result;\r
+        try {\r
+            BayeuxRequest request = null;\r
+            //a message can be an array of messages\r
+            JSONArray jsArray = new JSONArray(message);\r
+            for (int i = 0; i < jsArray.length(); i++) {\r
+                JSONObject msg = jsArray.getJSONObject(i);\r
+                \r
+                if (log.isDebugEnabled()) {\r
+                    log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message:"+msg);\r
+                }\r
+                request = RequestFactory.getRequest(tb,event,msg);\r
+                if (log.isDebugEnabled()) {\r
+                    log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message using request:"+request);\r
+                }\r
+                result = request.process(result);\r
+                if (log.isDebugEnabled()) {\r
+                    log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message result:"+result);\r
+                }\r
+            }\r
+            if (result>0 && request!=null) {\r
+                event.getHttpServletRequest().setAttribute(BayeuxRequest.LAST_REQ_ATTR, request);\r
+                ClientImpl ci = (ClientImpl)tb.getClient(((RequestBase)request).getClientId());\r
+                ci.addCometEvent(event);\r
+                if (log.isDebugEnabled()) {\r
+                    log.debug("["+Thread.currentThread().getName()+"] Done bayeux message added to request attribute");\r
+                }\r
+            } else if (result == 0 && request!=null) {\r
+                RequestBase.deliver(event,(ClientImpl)tb.getClient(((RequestBase)request).getClientId()));\r
+                if (log.isDebugEnabled()) {\r
+                    log.debug("["+Thread.currentThread().getName()+"] Done bayeux message, delivered to client");\r
+                }\r
+            }\r
+            \r
+        }catch (JSONException x) {\r
+            log.error(x);//to do impl error handling\r
+            result = -1;\r
+        }catch (BayeuxException x) {\r
+            log.error(x); //to do impl error handling\r
+            result = -1;\r
+        }\r
+        return result;\r
+    }\r
+\r
+    public ServletConfig getServletConfig() {\r
+        return servletConfig;\r
+    }\r
+\r
+    public String getServletInfo() {\r
+        return "Tomcat/BayeuxServlet/1.0";\r
+    }\r
+\r
+    public void init(ServletConfig servletConfig) throws ServletException {\r
+        \r
+        this.servletConfig = servletConfig;\r
+        ServletContext ctx = servletConfig.getServletContext();\r
+        if (ctx.getAttribute(TOMCAT_BAYEUX_ATTR)==null)\r
+            ctx.setAttribute(TOMCAT_BAYEUX_ATTR,new TomcatBayeux());\r
+        this.tb = (TomcatBayeux)ctx.getAttribute(TOMCAT_BAYEUX_ATTR);\r
+        tb.setReconnectInterval(getReconnectInterval());\r
+    }\r
+\r
+    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\r
+        if (servletResponse instanceof HttpServletResponse) {\r
+            ( (HttpServletResponse) servletResponse).sendError(500, "Misconfigured Tomcat server, must be configured to support Comet operations.");\r
+        } else {\r
+            throw new ServletException("Misconfigured Tomcat server, must be configured to support Comet operations for the Bayeux protocol.");\r
+        }\r
+    }\r
+}\r
diff --git a/java/org/apache/tomcat/bayeux/ChannelImpl.java b/java/org/apache/tomcat/bayeux/ChannelImpl.java
new file mode 100644 (file)
index 0000000..e1e08a8
--- /dev/null
@@ -0,0 +1,189 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+\r
+import java.util.LinkedList;\r
+\r
+import org.apache.cometd.bayeux.Channel;\r
+import org.apache.cometd.bayeux.Client;\r
+import org.apache.cometd.bayeux.DataFilter;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import org.apache.cometd.bayeux.Message;\r
+import java.util.Iterator;\r
+import org.apache.juli.logging.Log;\r
+import org.apache.juli.logging.LogFactory;\r
+/**\r
+ * \r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ */\r
+public class ChannelImpl implements Channel {\r
+    \r
+    protected static Log log = LogFactory.getLog(ChannelImpl.class);\r
+    \r
+    /**\r
+     * The unique id of this channel\r
+     */\r
+    protected String id = null;\r
+    \r
+    /**\r
+     * A list of the current subscribers\r
+     */\r
+    protected LinkedList<Client> subscribers = new LinkedList<Client>();\r
+    \r
+    /**\r
+     * A list of the current filters\r
+     */\r
+    protected LinkedList<DataFilter> filters = new LinkedList<DataFilter>();\r
+    \r
+    /**\r
+     * Is this channel persistent, default value is true\r
+     */\r
+    protected boolean persistent = true; \r
+    \r
+    /**\r
+     * Creates a new channel\r
+     * @param id String - the id of the channel, can not be null\r
+     */\r
+    protected ChannelImpl(String id) {\r
+        assert id != null;\r
+        this.id = id;\r
+    }\r
+\r
+    /**\r
+     * returns the id of this channel\r
+     * @return String\r
+     */\r
+    public String getId() {\r
+        return id;\r
+    }\r
+    \r
+    /**\r
+     * Returns true if this channel matches the pattern to its id.\r
+     * The channel pattern can be a complete name like <code>/service/mychannel</code>\r
+     * or it can be a wild card pattern like <code>/service/app2/**</code>\r
+     * @param pattern String according to the Bayeux specification section 2.2.1 Channel Globbing, can not be null.\r
+     * @return boolean true if the id of this channel matches the pattern\r
+     */\r
+    public boolean matches(String pattern) {\r
+        if (pattern == null)\r
+            throw new NullPointerException("Channel pattern must not be null.");\r
+        if (getId().equals(pattern))\r
+            return true;\r
+        int wildcardPos = pattern.indexOf("/*");\r
+        if (wildcardPos == -1)\r
+            return false;\r
+        boolean multiSegment = pattern.indexOf("**") != -1;\r
+        String leadSubstring = pattern.substring(0, wildcardPos);\r
+        if (leadSubstring == null)\r
+            return false;\r
+        if (multiSegment) \r
+            return getId().startsWith(leadSubstring);\r
+        else {\r
+            if (getId().length() <= wildcardPos + 2)\r
+                return false;\r
+            return !(getId().substring(wildcardPos + 2).contains("/"));\r
+        }\r
+    }\r
+\r
+\r
+\r
+    /**\r
+     * @return returns a non modifiable list of the subscribers for this channel.\r
+     */\r
+    public List<Client> getSubscribers() {\r
+        return Collections.unmodifiableList(subscribers);\r
+    }\r
+\r
+    /**\r
+     * @return true if the Channel will persist without any subscription.\r
+     */\r
+    public boolean isPersistent() {\r
+        return persistent;\r
+    }\r
+    \r
+    public void publish(Message msg) {\r
+        publish(new Message[] {msg});\r
+    }\r
+\r
+    public void publish(Message[] msgs) {\r
+        if (msgs==null) return;\r
+        MessageImpl[] imsgs = new MessageImpl[msgs.length];\r
+        for (int i=0; msgs!=null && i<msgs.length; i++) {\r
+            Message data = msgs[i];\r
+\r
+            if (!(data instanceof MessageImpl)) \r
+                throw new IllegalArgumentException("Invalid message class, you can only publish messages "+\r
+                                                   "created through the Bayeux.newMessage() method");\r
+            if (log.isDebugEnabled()) {\r
+                log.debug("Publishing message:"+data+" to channel:"+this);\r
+            }\r
+            //clone it so that we can set this channel as a reference\r
+            MessageImpl msg = (MessageImpl)((MessageImpl)data).clone();\r
+            //this is the channel it was delivered through\r
+            msg.setChannel(this);\r
+            //pass through filters\r
+            for (Iterator<DataFilter> it = filters.iterator(); it.hasNext(); ) {\r
+                it.next().filter(msg);\r
+            }\r
+            imsgs[i] = msg;\r
+        }\r
+        //deliver it to the clients\r
+        for (Iterator<Client> it = subscribers.iterator(); it.hasNext(); ) {\r
+            ClientImpl c = (ClientImpl)it.next();\r
+            c.deliverInternal(this,imsgs);\r
+        }\r
+        \r
+    }\r
+\r
+    public void setPersistent(boolean persistent) {\r
+        this.persistent = persistent;\r
+    }\r
+\r
+    public void subscribe(Client subscriber) {\r
+        if (!subscribers.contains((subscriber))) { \r
+            subscribers.addLast(subscriber);\r
+            ((ClientImpl)subscriber).subscribed(this);\r
+        }\r
+    }\r
+\r
+    public Client unsubscribe(Client subscriber) {\r
+        if (subscribers.remove(subscriber)) {\r
+            ((ClientImpl)subscriber).unsubscribed(this);\r
+            return subscriber;\r
+        } else\r
+            return null;\r
+    }\r
+    \r
+    public void addFilter(DataFilter filter) {\r
+        if (!filters.contains(filter)) \r
+            filters.addLast(filter);\r
+    }\r
+\r
+    public DataFilter removeFilter(DataFilter filter) {\r
+        if ( filters.remove(filter) ) return filter;\r
+        else return null;\r
+    }\r
+    \r
+    public String toString() {\r
+        StringBuffer buf = new StringBuffer(super.toString());\r
+        buf.append("; channelId=").append(getId());\r
+        return buf.toString();\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/bayeux/ClientImpl.java b/java/org/apache/tomcat/bayeux/ClientImpl.java
new file mode 100644 (file)
index 0000000..f1ab929
--- /dev/null
@@ -0,0 +1,278 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Queue;\r
+import java.util.concurrent.ConcurrentLinkedQueue;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import org.apache.cometd.bayeux.Client;\r
+import org.apache.cometd.bayeux.Listener;\r
+import org.apache.cometd.bayeux.Message;\r
+import org.apache.juli.logging.Log;\r
+import org.apache.juli.logging.LogFactory;\r
+import java.util.concurrent.atomic.AtomicInteger;\r
+import java.util.HashMap;\r
+import java.util.ArrayList;\r
+\r
+public class ClientImpl implements Client {\r
+    \r
+    public static final int SUPPORT_CALLBACK_POLL = 0x1;\r
+    public static final int SUPPORT_LONG_POLL = 0x2; \r
+\r
+    public static final String COMET_EVENT_ATTR = "org.apache.cometd.bayeux.client";\r
+    \r
+    protected static Log log = LogFactory.getLog(ClientImpl.class);\r
+\r
+    protected static LinkedList<Message> EMPTY_LIST = new LinkedList<Message>();\r
+    /**\r
+     * queued message for remote clients.\r
+     */\r
+    protected LinkedList<Message> messages = null;\r
+    \r
+    /**\r
+     * \r
+     */\r
+    protected Queue<CometEvent> events = new LinkedList<CometEvent>();\r
+    \r
+    /**\r
+     * Unique id representing this client\r
+     */\r
+    protected String id;\r
+    \r
+    /**\r
+     * supported connection types, defaults to long-polling\r
+     */\r
+    protected int supportedConnTypes = SUPPORT_LONG_POLL | SUPPORT_CALLBACK_POLL;\r
+    \r
+    /**\r
+     * The desired connection type\r
+     */\r
+    protected int desirectConnType = SUPPORT_LONG_POLL;\r
+    \r
+    /**\r
+     * Does this client use json-comment-filtered messages\r
+     */\r
+    protected boolean useJsonFiltered = false;\r
+    \r
+    /**\r
+     * Same JVM clients, get local=true\r
+     */\r
+    protected boolean local;\r
+    \r
+    /**\r
+     * The callback object for local clients\r
+     */\r
+    protected Listener listener;\r
+    \r
+    protected AtomicInteger nrofsubscriptions = new AtomicInteger(0);\r
+    \r
+    protected ClientImpl(String id, boolean local) {\r
+        this.id = id;\r
+        this.local = local;\r
+        if (!local) messages = new LinkedList<Message>();\r
+    }\r
+    \r
+    protected ClientImpl(String id, CometEvent event) {\r
+        this(id,false);\r
+        events = new ConcurrentLinkedQueue<CometEvent>();\r
+        addCometEvent(event);\r
+    }\r
+\r
+    public synchronized void deliver(Message message) {\r
+        deliverInternal(null,new MessageImpl[] {(MessageImpl)message});\r
+    }\r
+    \r
+    public synchronized void deliver(Message[] message) {\r
+        deliverInternal(null,message);\r
+    }\r
+\r
+    protected synchronized void deliverInternal(ChannelImpl channel, MessageImpl message) {\r
+        deliverInternal(channel,new MessageImpl[] {message});\r
+    }\r
+\r
+    protected synchronized void deliverInternal(ChannelImpl channel, Message[] msgs) {\r
+        if (isLocal()) {\r
+            //local clients must have a listener\r
+            ArrayList<Message> list = new ArrayList<Message>();\r
+            for (int i=0; msgs!=null && i<msgs.length; i++) {\r
+                if (this!=msgs[i].getClient()) list.add(msgs[i]);\r
+            }\r
+            if (getListener() != null) {\r
+                getListener().deliver(list.toArray(new Message[0]));\r
+            }\r
+        } else {\r
+            for (int i=0; msgs!=null && i<msgs.length; i++) {\r
+                MessageImpl message = (MessageImpl)msgs[i];\r
+                if (this==message.getClient()) { \r
+                    //dont deliver to ourself\r
+                    continue;\r
+                }\r
+                //we are not implementing forever responses, if the client is connected\r
+                //then we will fire off the message\r
+                //first we check to see if we have any existing connections we can piggy back on\r
+                CometEvent event = events.poll();\r
+                boolean delivered = false;\r
+                //TODO TODO - check on thread safety, for writing and for getting last request.\r
+                if (event!=null) {\r
+                    synchronized (event) {\r
+                        RequestBase rq = (RequestBase)event.getHttpServletRequest().getAttribute(RequestBase.LAST_REQ_ATTR);\r
+                        if (rq!=null) {\r
+                            Map map = new HashMap();\r
+                            try {\r
+                                map.put(Bayeux.CHANNEL_FIELD,message.getChannel().getId());\r
+                                map.put(Bayeux.DATA_FIELD,message);\r
+                                JSONObject json = new JSONObject(map);\r
+                                if (log.isDebugEnabled()) {\r
+                                    log.debug("Message instantly delivered to remote client["+this+"] message:"+json);\r
+                                }\r
+                                rq.addToDeliveryQueue(this, json);\r
+                                //deliver the batch\r
+                                if (i==(msgs.length-1)) {\r
+                                    rq.deliver(event, this);\r
+                                    event.close(); //todo, figure out a better way, this means only one message gets delivered\r
+                                    removeCometEvent(event); //and delivered instantly\r
+                                }\r
+                                delivered = true;\r
+                            } catch (Exception x) {\r
+                                log.error(x);\r
+                            }\r
+                        }\r
+                    }\r
+                } \r
+                if (!delivered) {\r
+                    if (log.isDebugEnabled()) {\r
+                        log.debug("Message added to queue for remote client["+this+"] message:"+message);\r
+                    }\r
+                    //queue the message for the next round\r
+                    messages.add(message);\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    public String getId() {\r
+        return this.id;\r
+    }\r
+\r
+    protected Listener getListener() {\r
+        return listener;\r
+    }\r
+\r
+    public boolean hasMessages() {\r
+        if (isLocal()) return false;\r
+        else {\r
+            return messages.size() > 0;\r
+        }\r
+    }\r
+\r
+    public boolean isLocal() {\r
+        return local;\r
+    }\r
+\r
+    public int getSupportedConnTypes() {\r
+        return supportedConnTypes;\r
+    }\r
+\r
+    public int getDesirectConnType() {\r
+        return desirectConnType;\r
+    }\r
+\r
+    public boolean useJsonFiltered() {\r
+        return useJsonFiltered;\r
+    }\r
+\r
+    public void setListener(Listener listener) {\r
+        this.listener = listener;\r
+    }\r
+\r
+    public void setSupportedConnTypes(int supportedConnTypes) {\r
+        this.supportedConnTypes = supportedConnTypes;\r
+    }\r
+\r
+    public void setUseJsonFiltered(boolean useJsonFiltered) {\r
+        this.useJsonFiltered = useJsonFiltered;\r
+    }\r
+\r
+    public void setDesirectConnType(int desirectConnType) {\r
+        this.desirectConnType = desirectConnType;\r
+    }\r
+\r
+    public boolean supportsCallbackPoll() {\r
+        return (supportedConnTypes & SUPPORT_CALLBACK_POLL) == SUPPORT_CALLBACK_POLL;\r
+    }\r
+\r
+    public boolean supportsLongPoll() {\r
+        return (supportedConnTypes & SUPPORT_LONG_POLL) == SUPPORT_LONG_POLL;\r
+    }\r
+\r
+    public synchronized List<Message> takeMessages() {\r
+        if (isLocal()) return null;\r
+        if (messages.size()==0) return EMPTY_LIST;\r
+        List result = new LinkedList(messages);\r
+        messages.clear();\r
+        return result;\r
+    }\r
+    \r
+    public String toString() {\r
+        StringBuffer buf = new StringBuffer(super.toString());\r
+        buf.append(" id=").append(getId());\r
+        return buf.toString();\r
+    }\r
+    \r
+    public boolean isSubscribed() {\r
+        return nrofsubscriptions.get()>0;\r
+    }\r
+    \r
+    protected synchronized boolean addCometEvent(CometEvent event) {\r
+        boolean result = false;\r
+        if (!events.contains(event)) {\r
+            events.add(event);\r
+            result = true;\r
+        }\r
+        event.getHttpServletRequest().setAttribute(COMET_EVENT_ATTR,this);\r
+        return result;\r
+    }\r
+    \r
+    protected synchronized boolean removeCometEvent(CometEvent event) {\r
+        boolean result = events.remove(event);\r
+        event.getHttpServletRequest().removeAttribute(COMET_EVENT_ATTR);\r
+        return result;\r
+    }\r
+    \r
+    \r
+    protected void subscribed(ChannelImpl ch) {\r
+        nrofsubscriptions.addAndGet(1);\r
+    }\r
+    \r
+    protected void unsubscribed(ChannelImpl ch) {\r
+        nrofsubscriptions.addAndGet(-1);\r
+    }\r
+    \r
+    public void startBatch(){\r
+        //noop until improved\r
+    }\r
+    public void endBatch() {\r
+        //noop until improved\r
+    }\r
+        \r
+}\r
diff --git a/java/org/apache/tomcat/bayeux/HttpError.java b/java/org/apache/tomcat/bayeux/HttpError.java
new file mode 100644 (file)
index 0000000..55ff2c7
--- /dev/null
@@ -0,0 +1,43 @@
+package org.apache.tomcat.bayeux;\r
+\r
+public class HttpError {\r
+    private int code;\r
+    private String status;\r
+    private Throwable cause;\r
+    public HttpError(int code, String status, Throwable cause) {\r
+        this.code = code;\r
+        this.status = status;\r
+        this.cause = cause;\r
+    }\r
+\r
+    public void setCode(int code) {\r
+        this.code = code;\r
+    }\r
+\r
+    public void setStatus(String status) {\r
+        this.status = status;\r
+    }\r
+\r
+    public void setCause(Throwable exception) {\r
+        this.cause = exception;\r
+    }\r
+\r
+    public int getCode() {\r
+        return code;\r
+    }\r
+\r
+    public String getStatus() {\r
+        return status;\r
+    }\r
+\r
+    public Throwable getCause() {\r
+        return cause;\r
+    }\r
+\r
+    public String toString() {\r
+        if (cause != null)\r
+            return code + ":" + status + " - [" + cause + "]";\r
+        else\r
+            return code + ":" + status;\r
+    }\r
+}\r
diff --git a/java/org/apache/tomcat/bayeux/MessageImpl.java b/java/org/apache/tomcat/bayeux/MessageImpl.java
new file mode 100644 (file)
index 0000000..53d5342
--- /dev/null
@@ -0,0 +1,80 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+\r
+import java.util.HashMap;\r
+\r
+import org.apache.cometd.bayeux.Channel;\r
+import org.apache.cometd.bayeux.Client;\r
+import org.apache.cometd.bayeux.Message;\r
+\r
+public class MessageImpl extends HashMap<String,Object> implements Message {\r
+    \r
+    protected Channel channel;\r
+    protected Client client;\r
+    protected String id;\r
+    private long TTL = 1000*60*5; //5min is the default TTL for a message\r
+    protected long creationTime = System.currentTimeMillis();\r
+\r
+    public Object clone() {\r
+        MessageImpl copy = new MessageImpl(id);\r
+        copy.putAll(this);\r
+        copy.channel = channel;\r
+        copy.client = client;\r
+        copy.id = id;\r
+        copy.creationTime = creationTime;\r
+        copy.TTL = TTL;\r
+        return copy;\r
+    }\r
+\r
+    protected MessageImpl(String id) {\r
+        assert id != null;\r
+        this.id = id;\r
+    }\r
+\r
+    public Channel getChannel() {\r
+        return channel;\r
+    }\r
+\r
+    public Client getClient() {\r
+        return client;\r
+    }\r
+\r
+    public long getCreationTime() {\r
+        return creationTime;\r
+    }\r
+\r
+    public long getTTL() {\r
+        return TTL;\r
+    }\r
+\r
+    public String getId() {\r
+        return id;\r
+    }\r
+\r
+    protected void setChannel(Channel channel) {\r
+        this.channel = channel;\r
+    }\r
+\r
+    protected void setClient(Client client) {\r
+        this.client = client;\r
+    }\r
+\r
+    public void setTTL(long TTL) {\r
+        this.TTL = TTL;\r
+    }\r
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/bayeux/RequestBase.java b/java/org/apache/tomcat/bayeux/RequestBase.java
new file mode 100644 (file)
index 0000000..19df7fc
--- /dev/null
@@ -0,0 +1,251 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+\r
+import java.io.IOException;\r
+import java.io.PrintWriter;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.LinkedHashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.TimeZone;\r
+import java.util.Date;\r
+import java.text.SimpleDateFormat;\r
+import javax.servlet.ServletException;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.tomcat.bayeux.HttpError;\r
+\r
+import org.apache.juli.logging.Log;\r
+import org.apache.juli.logging.LogFactory;\r
+import org.json.JSONArray;\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import org.apache.cometd.bayeux.Message;\r
+\r
+/**\r
+ * Common functionality and member variables for all Bayeux requests.\r
+ *\r
+ * @author Guy A. Molinari\r
+ * @author Filip Hanik\r
+ * @version 0.9\r
+ *\r
+ */\r
+public abstract class RequestBase implements BayeuxRequest {\r
+    \r
+    protected static final SimpleDateFormat timestampFmt =\r
+        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");\r
+    static {\r
+        timestampFmt.setTimeZone(TimeZone.getTimeZone("GMT"));\r
+    }\r
+    //message properties, combined for all messages\r
+    protected TomcatBayeux tomcatBayeux;\r
+    protected String channel;\r
+    protected String id;\r
+    protected String clientId;\r
+    protected String version = null;\r
+    protected String[] suppConnTypes = null;\r
+    protected int suppConnTypesFlag = 0;\r
+    protected int desiredConnTypeFlag = 0;\r
+    protected String minVersion = null;\r
+    protected String subscription = null;\r
+    protected String data = null;\r
+    protected String conType = null;\r
+    protected LinkedHashMap<String, Object> ext = new LinkedHashMap<String, Object> ();\r
+\r
+    \r
+    protected CometEvent event;\r
+    \r
+    protected HashMap<String, Object> response = null;\r
+    \r
+    protected static Log log = LogFactory.getLog(RequestBase.class);\r
+    \r
+    protected int reconnectInterval;\r
+    \r
+    protected RequestBase(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {\r
+        this.tomcatBayeux = tb;\r
+        this.event = event;\r
+        channel = jsReq.optString(Bayeux.CHANNEL_FIELD);\r
+        id = jsReq.optString(Bayeux.ID_FIELD);\r
+        clientId = jsReq.optString(Bayeux.CLIENT_FIELD);\r
+        version = jsReq.optString(Bayeux.VERSION_FIELD);\r
+        minVersion = jsReq.optString(Bayeux.MIN_VERSION_FIELD);\r
+        conType = jsReq.optString(Bayeux.CONNECTION_TYPE_FIELD);\r
+        subscription = jsReq.optString(Bayeux.SUBSCRIPTION_FIELD);\r
+        data = jsReq.optString(Bayeux.DATA_FIELD);\r
+        reconnectInterval = tb.getReconnectInterval();\r
+        if (jsReq.has(Bayeux.EXT_FIELD)) {\r
+            JSONObject jext = jsReq.getJSONObject(Bayeux.EXT_FIELD);\r
+            for (Iterator<String> i = jext.keys(); i.hasNext(); ) {\r
+                String key = i.next();\r
+                ext.put(key, jext.get(key));\r
+            }//for\r
+        }//end if\r
+        \r
+        if (jsReq.has(Bayeux.SUPP_CONNECTION_TYPE_FIELD)) {\r
+            JSONArray types = jsReq.getJSONArray(Bayeux.SUPP_CONNECTION_TYPE_FIELD);\r
+            suppConnTypes = new String[types.length()];\r
+            for (int i = 0; i < types.length(); i++) {\r
+                suppConnTypes[i] = types.getString(i);\r
+                if (Bayeux.TRANSPORT_CALLBACK_POLL.equals(suppConnTypes[i]))\r
+                    suppConnTypesFlag = suppConnTypesFlag|ClientImpl.SUPPORT_CALLBACK_POLL;\r
+                else if (Bayeux.TRANSPORT_LONG_POLL.equals(suppConnTypes[i]))\r
+                    suppConnTypesFlag = suppConnTypesFlag|ClientImpl.SUPPORT_LONG_POLL;\r
+            }//for\r
+        }//end if\r
+\r
+        if (conType!=null) {\r
+            if (Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType))\r
+                desiredConnTypeFlag = ClientImpl.SUPPORT_CALLBACK_POLL;\r
+            else if (Bayeux.TRANSPORT_LONG_POLL.equals(conType))\r
+                desiredConnTypeFlag = ClientImpl.SUPPORT_LONG_POLL;\r
+        }//end if\r
+        \r
+        //due to the fact that the javascript doesn't send up a required field\r
+        //we have to fake it\r
+        suppConnTypesFlag = ClientImpl.SUPPORT_CALLBACK_POLL | ClientImpl.SUPPORT_LONG_POLL;\r
+\r
+    }\r
+\r
+    public HttpError validate() {\r
+        HttpError result = null;\r
+//        if (clientId == null) {\r
+//            result = new HttpError(401,"No Client ID.", null);\r
+//        }\r
+        return result;\r
+    }\r
+\r
+    public TomcatBayeux getTomcatBayeux() {\r
+        return tomcatBayeux;\r
+    }\r
+\r
+    public String getChannel() {\r
+        return channel;\r
+    }\r
+\r
+    public String getId() {\r
+        return id;\r
+    }\r
+\r
+    public String getClientId() {\r
+        return clientId;\r
+    }\r
+\r
+    public LinkedHashMap getExt() {\r
+        return ext;\r
+    }\r
+\r
+    public CometEvent getEvent() {\r
+        return event;\r
+    }\r
+    \r
+    protected static void deliver(CometEvent event, ClientImpl to) throws IOException, ServletException, BayeuxException {\r
+        JSONArray jarray = getJSONArray(event,true);\r
+        if ( jarray == null ) throw new BayeuxException("No message to send!");\r
+        String jsonstring = jarray.toString();\r
+        if (log.isDebugEnabled()) {\r
+            log.debug("["+Thread.currentThread().getName()+"] Delivering message to[" + to + "] message:" + jsonstring);\r
+        }\r
+\r
+        PrintWriter out = event.getHttpServletResponse().getWriter();\r
+        if (to==null) {\r
+            //do nothing\r
+        }else if ( (to.getDesirectConnType() == 0 && to.supportsLongPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_LONG_POLL) {\r
+            if (to.useJsonFiltered())\r
+                out.print("/*");\r
+        } else if ( (to.getDesirectConnType() == 0 && to.supportsCallbackPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_CALLBACK_POLL) {\r
+            String jsonp = event.getHttpServletRequest().getParameter(Bayeux.JSONP_PARAMETER);\r
+            if (jsonp == null)\r
+                jsonp = Bayeux.JSONP_DEFAULT_NAME;\r
+            out.print(jsonp);\r
+            out.print('(');\r
+        } else {\r
+            throw new BayeuxException("Client doesn't support any appropriate connection type.");\r
+        }\r
+        out.print(jsonstring);\r
+        if ( to == null ) {\r
+            //do nothing\r
+        } else if ( (to.getDesirectConnType() == 0 && to.supportsLongPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_LONG_POLL) {\r
+            if (to.useJsonFiltered())\r
+                out.print("*/");\r
+        } else if ( (to.getDesirectConnType() == 0 && to.supportsCallbackPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_CALLBACK_POLL) {\r
+            out.print(");");\r
+        } \r
+        out.flush();\r
+        event.getHttpServletResponse().flushBuffer();\r
+\r
+        \r
+    }\r
+\r
+    protected static JSONArray getJSONArray(CometEvent event, boolean nullok) {\r
+        synchronized(event) {\r
+            JSONArray jarray = (JSONArray) event.getHttpServletRequest().getAttribute(JSON_MSG_ARRAY);\r
+            if (jarray == null && (!nullok)) {\r
+                jarray = new JSONArray();\r
+                event.getHttpServletRequest().setAttribute(JSON_MSG_ARRAY, jarray);\r
+            }\r
+            return jarray;\r
+        }\r
+    }\r
+\r
+    protected JSONArray getJSONArray() {\r
+        return getJSONArray(event,false);\r
+    }\r
+\r
+    protected void addToDeliveryQueue(ClientImpl to, JSONObject msg) throws IOException, ServletException, BayeuxException {\r
+        synchronized (event) {\r
+            getJSONArray().put(msg);\r
+        }\r
+    }\r
+    \r
+    protected void flushMessages(ClientImpl client) throws BayeuxException {\r
+        List<Message> msgs = client.takeMessages();\r
+        synchronized (event) {\r
+            try {\r
+                for (Iterator<Message> it = msgs.iterator(); it.hasNext(); ){\r
+                    MessageImpl msg = (MessageImpl)it.next();\r
+                    Map map = new HashMap();\r
+                    map.put(Bayeux.CHANNEL_FIELD,msg.getChannel().getId());\r
+                    if (msg.getClient()!=null) map.put(Bayeux.CLIENT_FIELD,msg.getClient().getId());\r
+                    map.put(Bayeux.DATA_FIELD,msg);\r
+                    JSONObject obj = new JSONObject(map);\r
+                    addToDeliveryQueue(client, obj);\r
+                }\r
+            } catch (ServletException x) {\r
+                throw new BayeuxException(x);\r
+            } catch (IOException x) {\r
+                throw new BayeuxException(x);\r
+            }\r
+        }\r
+    }\r
+    \r
+    public int process(int prevops) throws BayeuxException {\r
+        event.getHttpServletRequest().setAttribute(CURRENT_REQ_ATTR,this);\r
+        return prevops;\r
+    }\r
+    \r
+    public int getReconnectInterval() {\r
+        return reconnectInterval;\r
+    }\r
+\r
+    public String getTimeStamp() {\r
+        return timestampFmt.format(new Date(System.currentTimeMillis()));\r
+    }\r
+\r
+}\r
diff --git a/java/org/apache/tomcat/bayeux/RequestFactory.java b/java/org/apache/tomcat/bayeux/RequestFactory.java
new file mode 100644 (file)
index 0000000..f2765e6
--- /dev/null
@@ -0,0 +1,48 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+\r
+import org.json.JSONObject;\r
+import org.apache.tomcat.bayeux.request.MetaHandshakeRequest;\r
+import org.apache.catalina.CometEvent;\r
+import org.json.JSONException;\r
+import org.apache.tomcat.bayeux.request.MetaConnectRequest;\r
+import org.apache.tomcat.bayeux.request.MetaDisconnectRequest;\r
+import org.apache.tomcat.bayeux.request.MetaSubscribeRequest;\r
+import org.apache.tomcat.bayeux.request.MetaUnsubscribeRequest;\r
+import org.apache.tomcat.bayeux.request.PublishRequest;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+\r
+public class RequestFactory {\r
+\r
+    public static BayeuxRequest getRequest(TomcatBayeux tomcatBayeux, CometEvent event, JSONObject msg) throws JSONException {\r
+        String channel = msg.optString(Bayeux.CHANNEL_FIELD);\r
+        if (Bayeux.META_HANDSHAKE.equals(channel)) {\r
+            return new MetaHandshakeRequest(tomcatBayeux,event,msg);\r
+        }else if (Bayeux.META_CONNECT.equals(channel)) {\r
+            return new MetaConnectRequest(tomcatBayeux,event,msg);\r
+        }else if (Bayeux.META_DISCONNECT.equals(channel)) {\r
+            return new MetaDisconnectRequest(tomcatBayeux,event,msg);\r
+        }else if (Bayeux.META_SUBSCRIBE.equals(channel)) {\r
+            return new MetaSubscribeRequest(tomcatBayeux,event,msg);\r
+        }else if (Bayeux.META_UNSUBSCRIBE.equals(channel)) {\r
+            return new MetaUnsubscribeRequest(tomcatBayeux,event,msg);\r
+        } else {\r
+            return new PublishRequest(tomcatBayeux,event,msg);\r
+        }\r
+    }\r
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/bayeux/TomcatBayeux.java b/java/org/apache/tomcat/bayeux/TomcatBayeux.java
new file mode 100644 (file)
index 0000000..efe2264
--- /dev/null
@@ -0,0 +1,176 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux;\r
+\r
+import java.util.HashMap;\r
+import java.util.LinkedHashMap;\r
+import java.util.List;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.catalina.tribes.util.Arrays;\r
+import org.apache.catalina.tribes.util.UUIDGenerator;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import org.apache.cometd.bayeux.Channel;\r
+import org.apache.cometd.bayeux.Client;\r
+import org.apache.cometd.bayeux.Listener;\r
+import org.apache.cometd.bayeux.Message;\r
+import org.apache.cometd.bayeux.SecurityPolicy;\r
+/**\r
+ * \r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ */\r
+public class TomcatBayeux implements Bayeux {\r
+    \r
+\r
+    protected int reconnectInterval = 5000;\r
+    /**\r
+     * a list of all active clients\r
+     */\r
+    protected HashMap<String,Client> clients = new HashMap<String,Client>();\r
+    \r
+    /**\r
+     * a list of all active channels\r
+     */\r
+    protected LinkedHashMap<String, Channel> channels = new LinkedHashMap<String,Channel>();\r
+    \r
+    /**\r
+     * security policy to be used.\r
+     */\r
+    protected SecurityPolicy securityPolicy = null;\r
+    /**\r
+     * default client to use when we need to send an error message but don't have a client valid reference\r
+     */\r
+    protected static ClientImpl errorClient = new ClientImpl("error-no-client",false);\r
+    \r
+    /**\r
+     * returns the default error client\r
+     * @return ClientImpl\r
+     */\r
+    public static ClientImpl getErrorClient() {\r
+        return errorClient;\r
+    }\r
+    \r
+    protected TomcatBayeux() {\r
+    }\r
+    \r
+    /**\r
+     * should be invoked when the servlet is destroyed or when the context shuts down\r
+     */\r
+    public void destroy() {\r
+        throw new UnsupportedOperationException("TomcatBayeux.destroy() not yet implemented");\r
+    }\r
+\r
+    public Channel getChannel(String channelId, boolean create) {\r
+        Channel result = channels.get(channelId);\r
+        if (result==null && create) {\r
+            result = new ChannelImpl(channelId);\r
+            channels.put(channelId,result);\r
+        }\r
+        return result;\r
+    }\r
+    \r
+    public Channel remove(Channel channel) {\r
+        return channels.remove(channel.getId());\r
+    }\r
+    \r
+    public Client remove(Client client) {\r
+        if (client==null) return null;\r
+        for (Channel ch : getChannels()) {\r
+            ch.unsubscribe(client);\r
+        }\r
+        return clients.remove(client.getId());\r
+    }\r
+\r
+    public Client getClient(String clientId) {\r
+        return clients.get(clientId);\r
+    }\r
+    \r
+    public boolean hasClient(String clientId) {\r
+        return clients.containsKey(clientId);\r
+    }\r
+    \r
+    public List<Client> getClients() {\r
+        return java.util.Arrays.asList(clients.entrySet().toArray(new Client[0]));\r
+    }\r
+\r
+    public SecurityPolicy getSecurityPolicy() {\r
+        return securityPolicy;\r
+    }\r
+\r
+    public int getReconnectInterval() { \r
+        return reconnectInterval;\r
+    }\r
+\r
+    public boolean hasChannel(String channel) {\r
+        return channels.containsKey(channel);\r
+    }\r
+\r
+    public Client newClient(String idprefix, Listener listener, boolean local, CometEvent event) {\r
+        String id = createUUID(idprefix);\r
+        ClientImpl client = new ClientImpl(id, local);\r
+        client.setListener(listener);\r
+        clients.put(id, client);\r
+        return client;\r
+    }\r
+\r
+    public Client newClient(String idprefix, Listener listener) {\r
+        assert listener!=null;\r
+        //if this method gets called, someone is using the API inside\r
+        //the JVM, this is a local client\r
+        return newClient(idprefix,listener,true, null);\r
+    }\r
+    \r
+    protected ClientImpl getClientImpl(CometEvent event) {\r
+        return (ClientImpl)event.getHttpServletRequest().getAttribute(ClientImpl.COMET_EVENT_ATTR);\r
+    }\r
+    \r
+    protected void remove(CometEvent event) {\r
+        ClientImpl client = getClientImpl(event);\r
+        if (client!=null) {\r
+            client.removeCometEvent(event);\r
+        }\r
+    }\r
+\r
+    public String createUUID(String idprefix) {\r
+        if (idprefix==null) idprefix="";\r
+        return idprefix + Arrays.toString(UUIDGenerator.randomUUID(false));\r
+    }\r
+    \r
+    public List<Channel> getChannels() {\r
+        return java.util.Arrays.asList(channels.entrySet().toArray(new Channel[0]));\r
+    }\r
+\r
+    protected Message newMessage() {\r
+        String id = createUUID("msg-");\r
+        return new MessageImpl(id);\r
+    }\r
+\r
+    public Message newMessage(Client from) {\r
+        MessageImpl msg = (MessageImpl)newMessage();\r
+        msg.setClient(from);\r
+        return msg;\r
+    }\r
+    public void setSecurityPolicy(SecurityPolicy securityPolicy) {\r
+        this.securityPolicy = securityPolicy;\r
+    }\r
+\r
+    public void setReconnectInterval(int reconnectTimeout) {\r
+        this.reconnectInterval = reconnectTimeout;\r
+    }\r
+\r
+}\r
diff --git a/java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java b/java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java
new file mode 100644 (file)
index 0000000..f052e73
--- /dev/null
@@ -0,0 +1,125 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux.request;\r
+\r
+import java.io.IOException;\r
+import java.util.HashMap;\r
+import javax.servlet.ServletException;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.tomcat.bayeux.HttpError;\r
+import org.apache.tomcat.bayeux.BayeuxException;\r
+import org.apache.tomcat.bayeux.BayeuxRequest;\r
+import org.apache.tomcat.bayeux.ClientImpl;\r
+import org.apache.tomcat.bayeux.TomcatBayeux;\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import org.apache.tomcat.bayeux.*;\r
+\r
+/******************************************************************************\r
+ * Handshake request Bayeux message.\r
+ *\r
+ * @author Guy A. Molinari\r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ *\r
+ */\r
+public class MetaConnectRequest extends RequestBase implements BayeuxRequest {\r
+    protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();\r
+\r
+    static {\r
+        responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_CONNECT);\r
+        responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);\r
+        responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());\r
+    }\r
+\r
+    public MetaConnectRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {\r
+        super(tb, event, jsReq);\r
+        if (clientId!=null && getTomcatBayeux().hasClient(clientId)) {\r
+            event.getHttpServletRequest().setAttribute("client",getTomcatBayeux().getClient(clientId));\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * Check client request for validity.\r
+     *\r
+     * Per section 4.2.1 of the Bayuex spec a connect request must contain:\r
+     *  1) The "/meta/connect" channel identifier.\r
+     *  2) The clientId returned by the server after handshake.\r
+     *  3) The desired connectionType (must be one of the server's supported\r
+     *     types returned by handshake response.\r
+     *  \r
+     * @return HttpError This method returns null if no errors were found\r
+     */\r
+    public HttpError validate() {\r
+        if(clientId==null|| (!getTomcatBayeux().hasClient(clientId)))\r
+            return new HttpError(400,"Client Id not valid.", null);\r
+        if (! (Bayeux.TRANSPORT_LONG_POLL.equals(conType) || Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType)))\r
+            return new HttpError(400,"Unsupported connection type.",null);\r
+        return null;//no error\r
+    }\r
+\r
+    /**\r
+     * Transition to connected state, flushing pending messages if\r
+     * available.  If there are pending subscriptions and no messages to\r
+     * flush then the connection is held until there is a pending publish\r
+     * event to be delivered to this client (Section 4.2.2 of spec).\r
+     */\r
+    public int process(int prevops) throws BayeuxException {\r
+        prevops = super.process(prevops);\r
+        response = (HashMap<String, Object>)responseTemplate.clone();\r
+        ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId);\r
+        boolean success = false;\r
+        HttpError error = validate();\r
+        if (error == null) {\r
+            client.setDesirectConnType(desiredConnTypeFlag);\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE);\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval());\r
+            success = true;\r
+        }else {\r
+            response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);\r
+            response.put(Bayeux.ERROR_FIELD, error.toString());\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.HANDSHAKE_RESPONSE);\r
+            if (client==null) client = TomcatBayeux.getErrorClient();\r
+        }\r
+        response.put(Bayeux.CLIENT_FIELD, client.getId());\r
+        response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp());\r
+        try {\r
+            JSONObject obj = new JSONObject(response);\r
+            addToDeliveryQueue(client, obj);\r
+        } catch (ServletException x) {\r
+            throw new BayeuxException(x);\r
+        } catch (IOException x) {\r
+            throw new BayeuxException(x);\r
+        }\r
+        \r
+        //return immediately if there is no subscriptions\r
+        //so that we can process the next message\r
+        int result = client.isSubscribed()?1:0; \r
+\r
+        if (success && client!=null && client.hasMessages()) {\r
+            //send out messages \r
+            flushMessages(client);\r
+            result = 0; //flush out the messages\r
+        }\r
+\r
+        return result;\r
+    }\r
+}\r
+\r
diff --git a/java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java b/java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java
new file mode 100644 (file)
index 0000000..d191660
--- /dev/null
@@ -0,0 +1,105 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux.request;\r
+\r
+import java.io.IOException;\r
+import java.util.HashMap;\r
+import javax.servlet.ServletException;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.tomcat.bayeux.HttpError;\r
+import org.apache.tomcat.bayeux.BayeuxException;\r
+import org.apache.tomcat.bayeux.BayeuxRequest;\r
+import org.apache.tomcat.bayeux.ClientImpl;\r
+import org.apache.tomcat.bayeux.TomcatBayeux;\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import org.apache.tomcat.bayeux.*;\r
+import org.apache.cometd.bayeux.Channel;\r
+\r
+/******************************************************************************\r
+ * Handshake request Bayeux message.\r
+ *\r
+ * @author Guy A. Molinari\r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ *\r
+ */\r
+public class MetaDisconnectRequest extends RequestBase implements BayeuxRequest {\r
+\r
+    protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();\r
+\r
+    static {\r
+        responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_DISCONNECT);\r
+        responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);\r
+        responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());\r
+    }\r
+\r
+    public MetaDisconnectRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {\r
+        super(tb, event, jsReq);\r
+    }\r
+\r
+\r
+    /**\r
+     * Check client request for validity.\r
+     *\r
+     * Per section 4.4.1 of the Bayuex spec a connect request must contain:\r
+     *  1) The "/meta/disconnect" channel identifier.\r
+     *  2) The clientId.\r
+     *  \r
+     * @return HttpError This method returns null if no errors were found\r
+     */\r
+    public HttpError validate() {\r
+        if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId)))\r
+            return new HttpError(400,"Client Id not valid.", null);\r
+//        if (! (Bayeux.TRANSPORT_LONG_POLL.equals(conType) || Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType)))\r
+//            return new HttpError(400,"Unsupported connection type.",null);\r
+        return null;//no error\r
+    }\r
+\r
+    /**\r
+     * Disconnect a client session.\r
+     */\r
+    public int process(int prevops) throws BayeuxException {\r
+        prevops = super.process(prevops);\r
+        response = (HashMap<String, Object>)responseTemplate.clone();\r
+        ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId);\r
+        HttpError error = validate();\r
+        if (error == null) {\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry");\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval());\r
+        }else {\r
+            getTomcatBayeux().remove(client);\r
+            response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);\r
+            response.put(Bayeux.ERROR_FIELD, error.toString());\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "none");\r
+            if (client==null) client = TomcatBayeux.getErrorClient();\r
+        }\r
+        response.put(Bayeux.CLIENT_FIELD, client.getId());\r
+        try {\r
+            JSONObject obj = new JSONObject(response);\r
+            addToDeliveryQueue(client, obj);\r
+        } catch (ServletException x) {\r
+            throw new BayeuxException(x);\r
+        } catch (IOException x) {\r
+            throw new BayeuxException(x);\r
+        }\r
+        return 0;\r
+    }\r
+}\r
+\r
diff --git a/java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java b/java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java
new file mode 100644 (file)
index 0000000..2835d12
--- /dev/null
@@ -0,0 +1,116 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux.request;\r
+\r
+import java.io.IOException;\r
+import java.util.HashMap;\r
+import javax.servlet.ServletException;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.tomcat.bayeux.HttpError;\r
+import org.apache.tomcat.bayeux.BayeuxException;\r
+import org.apache.tomcat.bayeux.BayeuxRequest;\r
+import org.apache.tomcat.bayeux.ClientImpl;\r
+import org.apache.tomcat.bayeux.TomcatBayeux;\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import org.apache.tomcat.bayeux.*;\r
+\r
+/******************************************************************************\r
+ * Handshake request Bayeux message.\r
+ *\r
+ * @author Guy A. Molinari\r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ *\r
+ */\r
+public class MetaHandshakeRequest extends RequestBase implements BayeuxRequest {\r
+\r
+    protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();\r
+    \r
+    static {\r
+        responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_HANDSHAKE);\r
+        responseTemplate.put(Bayeux.VERSION_FIELD,"1.0");\r
+        responseTemplate.put(Bayeux.SUPP_CONNECTION_TYPE_FIELD,new String[] { Bayeux.TRANSPORT_LONG_POLL, Bayeux.TRANSPORT_CALLBACK_POLL });\r
+        responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);\r
+        responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());\r
+    }\r
+\r
+    public MetaHandshakeRequest(TomcatBayeux tomcatBayeux, CometEvent event, JSONObject jsReq) throws JSONException {\r
+        super(tomcatBayeux, event, jsReq);\r
+    }\r
+    \r
+\r
+    public String getVersion() { return version; }\r
+    public String getMinimumVersion() { return minVersion; }\r
+\r
+\r
+    /**\r
+     * Check client request for validity.\r
+     *\r
+     * Per section 4.1.1 of the Bayuex spec a handshake request must contain:\r
+     *  1) The "/meta/handshake" channel identifier.\r
+     *  2) The version of the protocol supported by the client\r
+     *  3) The client's supported connection types.\r
+     *  \r
+     * @return HttpError This method returns null if no errors were found\r
+     */\r
+    public HttpError validate() {\r
+        boolean error = (version==null || version.length()==0);\r
+        if (!error) error = suppConnTypesFlag==0;\r
+        if (error) return new HttpError(400,"Invalid handshake request, supportedConnectionType field missing.",null);\r
+        else return null;\r
+    }\r
+\r
+    /**\r
+     * Generate and return a client identifier.  Return a list of\r
+     * supported connection types.  Must be a subset of or identical to\r
+     * the list of types supported by the client.  See section 4.1.2 of\r
+     * the Bayuex specification.\r
+     */\r
+    public int process(int prevops) throws BayeuxException {\r
+        prevops = super.process(prevops);\r
+        response = (HashMap<String, Object>)responseTemplate.clone();\r
+        ClientImpl client = null;\r
+        HttpError error = validate();\r
+        if (error == null) {\r
+            client = (ClientImpl) getTomcatBayeux().newClient("http-", null, false,getEvent());\r
+            clientId = client.getId();\r
+            client.setSupportedConnTypes(suppConnTypesFlag);\r
+            client.setUseJsonFiltered(getExt().get(Bayeux.JSON_COMMENT_FILTERED_FIELD) != null);\r
+            response.put(Bayeux.CLIENT_FIELD, client.getId());\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE);\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval());\r
+        }else {\r
+            response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);\r
+            response.put(Bayeux.ERROR_FIELD, error.toString());\r
+            client = TomcatBayeux.getErrorClient();\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.NONE_RESPONSE);\r
+        }\r
+        try {\r
+            JSONObject obj = new JSONObject(response);\r
+            addToDeliveryQueue(client, obj);\r
+        } catch (ServletException x) {\r
+            throw new BayeuxException(x);\r
+        } catch (IOException x) {\r
+            throw new BayeuxException(x);\r
+        }\r
+        return 0;\r
+    }\r
+}\r
+\r
diff --git a/java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java b/java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java
new file mode 100644 (file)
index 0000000..c16667a
--- /dev/null
@@ -0,0 +1,130 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux.request;\r
+\r
+import java.io.IOException;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import javax.servlet.ServletException;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.tomcat.bayeux.HttpError;\r
+import org.apache.tomcat.bayeux.BayeuxException;\r
+import org.apache.tomcat.bayeux.BayeuxRequest;\r
+import org.apache.tomcat.bayeux.ChannelImpl;\r
+import org.apache.tomcat.bayeux.ClientImpl;\r
+import org.apache.tomcat.bayeux.TomcatBayeux;\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Channel;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import org.apache.tomcat.bayeux.*;\r
+\r
+/******************************************************************************\r
+ * Handshake request Bayeux message.\r
+ *\r
+ * @author Guy A. Molinari\r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ */\r
+public class MetaSubscribeRequest extends RequestBase implements BayeuxRequest {\r
+\r
+    protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();\r
+\r
+    static {\r
+        responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_SUBSCRIBE);\r
+        responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);\r
+        responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());\r
+    }\r
+\r
+    public MetaSubscribeRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {\r
+        super(tb, event, jsReq);\r
+    }\r
+\r
+\r
+    /**\r
+     * Check client request for validity.\r
+     *\r
+     * Per section 4.5.1 of the Bayuex spec a connect request must contain:\r
+     *  1) The "/meta/subscribe" channel identifier.\r
+     *  2) The clientId.\r
+     *  3) The subscription.  This is the name of the channel of interest,\r
+     *     or a pattern.\r
+     *  \r
+     * @return HttpError This method returns null if no errors were found\r
+     */\r
+    public HttpError validate() {\r
+        if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId)))\r
+            return new HttpError(400,"Client Id not valid.", null);\r
+        if (subscription==null||subscription.length()==0)\r
+            return new HttpError(400,"Subscription missing.",null);\r
+        return null;//no error\r
+    }\r
+\r
+    /**\r
+     * Register interest for one or more channels.  Per section 2.2.1 of the\r
+     * Bayeux spec, a pattern may be specified.  Assign client to matching\r
+     * channels and inverse client to channel reference.\r
+     */\r
+    public int process(int prevops) throws BayeuxException {\r
+        prevops = super.process(prevops);\r
+        response = (HashMap<String, Object>)this.responseTemplate.clone();\r
+        ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId);\r
+        HttpError error = validate();\r
+        if (error == null) {\r
+            boolean wildcard = subscription.indexOf('*')!=-1;\r
+            boolean subscribed = false;\r
+            if (wildcard) {\r
+                List<Channel> channels = getTomcatBayeux().getChannels();\r
+                Iterator<Channel> it = channels.iterator();\r
+                while (it.hasNext()) {\r
+                    ChannelImpl ch = (ChannelImpl)it.next();\r
+                    if (ch.matches(subscription)) {\r
+                        ch.subscribe(client);\r
+                        subscribed = true;\r
+                    }\r
+                }\r
+            }else {\r
+                ChannelImpl ch = (ChannelImpl)getTomcatBayeux().getChannel(subscription,true);\r
+                ch.subscribe(client);\r
+                subscribed = true;\r
+            }\r
+            response.put(Bayeux.SUCCESSFUL_FIELD, Boolean.valueOf(subscribed));\r
+            response.put(Bayeux.SUBSCRIPTION_FIELD,subscription);\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry");\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval());\r
+        }else {\r
+            response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);\r
+            response.put(Bayeux.ERROR_FIELD, error.toString());\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "handshake");\r
+            if (client==null) client = TomcatBayeux.getErrorClient();\r
+        }\r
+        response.put(Bayeux.CLIENT_FIELD, client.getId());\r
+        response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp());\r
+        try {\r
+            JSONObject obj = new JSONObject(response);\r
+            addToDeliveryQueue(client, obj);\r
+        } catch (ServletException x) {\r
+            throw new BayeuxException(x);\r
+        } catch (IOException x) {\r
+            throw new BayeuxException(x);\r
+        }\r
+        return 0;\r
+    }\r
+}\r
+\r
diff --git a/java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java b/java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java
new file mode 100644 (file)
index 0000000..28969a0
--- /dev/null
@@ -0,0 +1,130 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux.request;\r
+\r
+import java.io.IOException;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import javax.servlet.ServletException;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.tomcat.bayeux.HttpError;\r
+import org.apache.tomcat.bayeux.BayeuxException;\r
+import org.apache.tomcat.bayeux.BayeuxRequest;\r
+import org.apache.tomcat.bayeux.ChannelImpl;\r
+import org.apache.tomcat.bayeux.ClientImpl;\r
+import org.apache.tomcat.bayeux.TomcatBayeux;\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Channel;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import org.apache.tomcat.bayeux.*;\r
+\r
+/******************************************************************************\r
+ * Handshake request Bayeux message.\r
+ *\r
+ * @author Guy A. Molinari\r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ *\r
+ */\r
+public class MetaUnsubscribeRequest extends RequestBase implements BayeuxRequest {\r
+\r
+    protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();\r
+\r
+    static {\r
+        responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_UNSUBSCRIBE);\r
+        responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);\r
+        responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());\r
+    }\r
+\r
+    public MetaUnsubscribeRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {\r
+        super(tb, event, jsReq);\r
+    }\r
+\r
+\r
+    /**\r
+     * Check client request for validity.\r
+     *\r
+     * Per section 4.6.1 of the Bayuex spec a connect request must contain:\r
+     *  1) The "/meta/unsubscribe" channel identifier.\r
+     *  2) The clientId.\r
+     *  3) The subscription.  This is the name of the channel of interest,\r
+     *     or a pattern.\r
+     *  \r
+     * @return HttpError This method returns null if no errors were found\r
+     */\r
+    public HttpError validate() {\r
+        if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId)))\r
+            return new HttpError(400,"Client Id not valid.", null);\r
+        if (subscription==null||subscription.length()==0)\r
+            return new HttpError(400,"Subscription missing.",null);\r
+        return null;//no error\r
+    }\r
+\r
+    /**\r
+     * De-register interest for one or more channels.  Per section 2.2.1 of the\r
+     * Bayeux spec, a pattern may be specified.  Sever relationships.\r
+     */\r
+    public int process(int prevops) throws BayeuxException {\r
+        prevops = super.process(prevops);\r
+        response = (HashMap<String, Object>)responseTemplate.clone();\r
+        ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId);\r
+        HttpError error = validate();\r
+        if (error == null) {\r
+            boolean wildcard = subscription.indexOf('*')!=-1;\r
+            boolean unsubscribed = false;\r
+            if (wildcard) {\r
+                List<Channel> channels = getTomcatBayeux().getChannels();\r
+                Iterator<Channel> it = channels.iterator();\r
+                while (it.hasNext()) {\r
+                    ChannelImpl ch = (ChannelImpl)it.next();\r
+                    if (ch.matches(subscription)) {\r
+                        ch.unsubscribe(client);\r
+                        unsubscribed = true;\r
+                    }\r
+                }\r
+            }else {\r
+                ChannelImpl ch = (ChannelImpl)getTomcatBayeux().getChannel(subscription,true);\r
+                ch.unsubscribe(client);\r
+                unsubscribed = true;\r
+            }\r
+            response.put(Bayeux.SUCCESSFUL_FIELD, Boolean.valueOf(unsubscribed));\r
+            response.put(Bayeux.SUBSCRIPTION_FIELD,subscription);\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry");\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval());\r
+        }else {\r
+            response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);\r
+            response.put(Bayeux.ERROR_FIELD, error.toString());\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "handshake");\r
+            if (client==null) client = TomcatBayeux.getErrorClient();\r
+        }\r
+        response.put(Bayeux.CLIENT_FIELD, client.getId());\r
+        response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp());\r
+        try {\r
+            JSONObject obj = new JSONObject(response);\r
+            addToDeliveryQueue(client, obj);\r
+        } catch (ServletException x) {\r
+            throw new BayeuxException(x);\r
+        } catch (IOException x) {\r
+            throw new BayeuxException(x);\r
+        }\r
+        return 0;\r
+    }\r
+}\r
+\r
diff --git a/java/org/apache/tomcat/bayeux/request/PublishRequest.java b/java/org/apache/tomcat/bayeux/request/PublishRequest.java
new file mode 100644 (file)
index 0000000..8bb0dff
--- /dev/null
@@ -0,0 +1,140 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.apache.tomcat.bayeux.request;\r
+\r
+import java.io.IOException;\r
+import java.util.HashMap;\r
+import javax.servlet.ServletException;\r
+\r
+import org.apache.catalina.CometEvent;\r
+import org.apache.tomcat.bayeux.HttpError;\r
+import org.apache.tomcat.bayeux.BayeuxException;\r
+import org.apache.tomcat.bayeux.BayeuxRequest;\r
+import org.apache.tomcat.bayeux.ChannelImpl;\r
+import org.apache.tomcat.bayeux.ClientImpl;\r
+import org.apache.tomcat.bayeux.MessageImpl;\r
+import org.apache.tomcat.bayeux.TomcatBayeux;\r
+import org.json.JSONException;\r
+import org.json.JSONObject;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import java.util.List;\r
+import org.apache.cometd.bayeux.Message;\r
+import java.util.Iterator;\r
+import org.apache.tomcat.bayeux.*;\r
+\r
+/******************************************************************************\r
+ * Handshake request Bayeux message.\r
+ *\r
+ * @author Guy A. Molinari\r
+ * @author Filip Hanik\r
+ * @version 1.0\r
+ *\r
+ */\r
+public class PublishRequest extends RequestBase implements BayeuxRequest {\r
+\r
+    JSONObject msgData = null;\r
+\r
+    protected static HashMap<String,Object> responseTemplate = new HashMap<String,Object>();\r
+\r
+    static {\r
+        responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE);\r
+        responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap<String, Object>());\r
+    }\r
+\r
+    public PublishRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException {\r
+        super(tb, event, jsReq);\r
+    }\r
+\r
+\r
+    /**\r
+     * Check client request for validity.\r
+     *\r
+     * Per section 5.1.1 of the Bayuex spec a connect request must contain:\r
+     *  1) The channel identifier of the channel for publication.\r
+     *  2) The data to send.\r
+     *  \r
+     * @return HttpError This method returns null if no errors were found\r
+     */\r
+    public HttpError validate() {\r
+        if(channel==null|| (!this.getTomcatBayeux().hasChannel(channel)))\r
+            return new HttpError(400,"Channel Id not valid.", null);\r
+        if(data==null || data.length()==0)\r
+            return new HttpError(400,"Message data missing.", null);\r
+        try {\r
+            this.msgData = new JSONObject(data);\r
+        }catch (JSONException x) {\r
+            return new HttpError(400,"Invalid JSON object in data attribute.",x);\r
+        }\r
+        if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId)))\r
+            return new HttpError(400,"Client Id not valid.", null);\r
+        return null;//no error\r
+    }\r
+\r
+    /**\r
+     *  Send the event message to all registered subscribers.\r
+     */\r
+    public int process(int prevops) throws BayeuxException {\r
+        prevops = super.process(prevops);\r
+        response = (HashMap<String, Object>)responseTemplate.clone();\r
+        ClientImpl client = clientId!=null?(ClientImpl)getTomcatBayeux().getClient(clientId):\r
+                                           (ClientImpl)event.getHttpServletRequest().getAttribute("client");\r
+        boolean success = false;\r
+        HttpError error = validate();\r
+        if (error == null) {\r
+            ChannelImpl chimpl = (ChannelImpl)getTomcatBayeux().getChannel(channel,false);\r
+            MessageImpl mimpl = (MessageImpl)getTomcatBayeux().newMessage(client);\r
+            \r
+            try {\r
+                String[] keys = JSONObject.getNames(msgData);\r
+                for (int i = 0; i < keys.length; i++) {\r
+                    mimpl.put(keys[i], msgData.get(keys[i]));\r
+                }\r
+                success = true;\r
+                ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE);\r
+                ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval());\r
+            }catch (JSONException x) {\r
+                if (log.isErrorEnabled()) log.error("Unable to parse:"+msgData,x);\r
+                throw new BayeuxException(x);\r
+            }\r
+            chimpl.publish(mimpl);\r
+        }\r
+        if(!success) {\r
+            response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE);\r
+            response.put(Bayeux.ERROR_FIELD, error.toString());\r
+            ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.HANDSHAKE_RESPONSE);\r
+            if (client==null) client = TomcatBayeux.getErrorClient();\r
+        }\r
+        response.put(Bayeux.CHANNEL_FIELD,channel);\r
+        response.put(Bayeux.CLIENT_FIELD, client.getId());\r
+        try {\r
+            JSONObject obj = new JSONObject(response);\r
+            addToDeliveryQueue(client, obj);\r
+        } catch (ServletException x) {\r
+            throw new BayeuxException(x);\r
+        } catch (IOException x) {\r
+            throw new BayeuxException(x);\r
+        }\r
+        \r
+        if (success && client!=null && client.hasMessages()) {\r
+            //send out messages \r
+            flushMessages(client);\r
+        }\r
+\r
+        return 0;\r
+    }\r
+}\r
+\r
diff --git a/test/org/apache/cometd/bayeux/samples/EchoChatClient.java b/test/org/apache/cometd/bayeux/samples/EchoChatClient.java
new file mode 100644 (file)
index 0000000..3d7c393
--- /dev/null
@@ -0,0 +1,111 @@
+package org.apache.cometd.bayeux.samples;\r
+\r
+import javax.servlet.ServletContextEvent;\r
+import javax.servlet.ServletContextListener;\r
+import javax.servlet.ServletContextAttributeListener;\r
+import javax.servlet.ServletContextAttributeEvent;\r
+import org.apache.cometd.bayeux.Bayeux;\r
+import java.util.concurrent.atomic.AtomicInteger;\r
+import org.apache.cometd.bayeux.Client;\r
+import org.apache.cometd.bayeux.Listener;\r
+import org.apache.cometd.bayeux.Message;\r
+import org.apache.cometd.bayeux.Channel;\r
+\r
+public class EchoChatClient implements ServletContextListener, ServletContextAttributeListener, Listener {\r
+    \r
+    static AtomicInteger counter = new AtomicInteger(0);\r
+    protected int id;\r
+    protected Bayeux b;\r
+    protected Client c;\r
+    protected boolean alive = true;\r
+    protected TimestampThread tt = new TimestampThread();\r
+    public EchoChatClient() {\r
+        id = counter.incrementAndGet();\r
+        System.out.println("new listener created with id:"+id);\r
+    }\r
+\r
+    /**\r
+     * contextDestroyed\r
+     *\r
+     * @param servletContextEvent ServletContextEvent\r
+     * @todo Implement this javax.servlet.ServletContextListener method\r
+     */\r
+    public void contextDestroyed(ServletContextEvent servletContextEvent) {\r
+        alive = false;\r
+        tt.interrupt();\r
+    }\r
+\r
+    /**\r
+     * contextInitialized\r
+     *\r
+     * @param servletContextEvent ServletContextEvent\r
+     * @todo Implement this javax.servlet.ServletContextListener method\r
+     */\r
+    public void contextInitialized(ServletContextEvent servletContextEvent) {\r
+    }\r
+\r
+    public void attributeAdded(ServletContextAttributeEvent scae) {\r
+        if (scae.getName().equals(Bayeux.DOJOX_COMETD_BAYEUX)) {\r
+            b = (Bayeux)scae.getValue();\r
+            c = b.newClient("echochat-",this);\r
+            Channel ch = b.getChannel("/chat/demo",true);\r
+            ch.subscribe(c);\r
+            tt.start();\r
+        }\r
+    }\r
+\r
+    public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {\r
+    }\r
+\r
+    public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {\r
+    }\r
+\r
+    public void removed(boolean timeout) {\r
+        System.out.println("Client removed.");\r
+    }\r
+\r
+    public void deliver(Message[] msgs) {\r
+        for (int i=0; msgs!=null && i<msgs.length; i++) {\r
+            Message msg = msgs[i];\r
+            System.out.println("[echochatclient ]received message:" + msg);\r
+            Message m = b.newMessage(c);\r
+            m.putAll(msg);\r
+            //echo the same message\r
+            m.put("user", "echochatserver");\r
+            if (m.containsKey("msg")) {\r
+                //simple chat demo\r
+                String chat = (String) m.get("msg");\r
+                m.put("msg", "echochatserver|I received your message-" + chat.substring(chat.indexOf("|") + 1));\r
+            }\r
+            System.out.println("[echochatclient ]sending message:" + m);\r
+            msg.getChannel().publish(m);\r
+        }\r
+    }\r
+\r
+    public class TimestampThread extends Thread {\r
+        public TimestampThread() {\r
+            setDaemon(true);\r
+        }\r
+        \r
+        public void run() {\r
+            while (alive) {\r
+                try {\r
+                    sleep(5000);\r
+                    Channel ch = b.getChannel("/chat/demo",false);\r
+                    if (ch.getSubscribers().size()<=1) {\r
+                        continue;\r
+                    }\r
+                    Message m = b.newMessage(c);\r
+                    m.put("user","echochatserver");\r
+                    m.put("chat","Time is:"+new java.sql.Date(System.currentTimeMillis()).toLocaleString());\r
+                    m.put("join",false);\r
+                    ch.publish(m);\r
+                }catch (InterruptedException ignore) {\r
+                    \r
+                }catch (Exception x) {\r
+                    x.printStackTrace();\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/webapps/cometd/WEB-INF/filters.json b/webapps/cometd/WEB-INF/filters.json
new file mode 100644 (file)
index 0000000..2557c5e
--- /dev/null
@@ -0,0 +1,27 @@
+[
+  { 
+    "channels": "/**",
+    "filter"  : "org.mortbay.cometd.filter.NoMarkupFilter",
+    "init"    : {}
+  },
+
+  { 
+    "channels": "/chat/*",
+    "filter"   : "org.mortbay.cometd.filter.RegexFilter",
+    "init"    : [ 
+                     [ "[fF].ck","dang" ],
+                     [ "teh ","the "]
+                   ]
+  },
+  
+  { 
+    "channels": "/chat/**",
+    "filter"   : "org.mortbay.cometd.filter.RegexFilter",
+    "init"    : [ 
+                     [ "[Mm]icrosoft", "Micro\\$oft" ],
+                     [ ".*tomcat.*", null ] 
+                   ]
+  }
+  
+  
+]
\ No newline at end of file
diff --git a/webapps/cometd/WEB-INF/web.xml b/webapps/cometd/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..085353b
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>\r
+<web-app \r
+   xmlns="http://java.sun.com/xml/ns/javaee" \r
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"\r
+   version="2.5"> \r
+  <display-name>Cometd Test WebApp</display-name>\r
+  \r
+  <servlet>\r
+    <servlet-name>cometd</servlet-name>\r
+    <servlet-class>org.apache.tomcat.bayeux.BayeuxServlet</servlet-class>\r
+    <init-param>\r
+      <param-name>timeout</param-name>\r
+      <param-value>120000000</param-value>\r
+    </init-param>\r
+    <load-on-startup>1</load-on-startup>\r
+  </servlet>\r
+\r
+  <servlet-mapping>\r
+    <servlet-name>cometd</servlet-name>\r
+    <url-pattern>/cometd/*</url-pattern>\r
+  </servlet-mapping>\r
+  \r
+</web-app>\r
+\r
+\r
diff --git a/webapps/cometd/examples/chat/chat.css b/webapps/cometd/examples/chat/chat.css
new file mode 100644 (file)
index 0000000..dfc1c85
--- /dev/null
@@ -0,0 +1,57 @@
+\r
+div \r
+{ \r
+  border: 0px solid black; \r
+}\r
+\r
+div#chatroom\r
+{\r
+  background-color: #e0e0e0;\r
+  border: 1px solid black; \r
+  width: 45em;\r
+}\r
+\r
+div#chat\r
+{\r
+  height: 20ex;\r
+  overflow: auto; \r
+  background-color: #f0f0f0;\r
+  padding: 4px;\r
+  border: 0px solid black; \r
+}\r
+\r
+div#input\r
+{\r
+  clear: both;\r
+  padding: 4px;\r
+  border: 0px solid black; \r
+  border-top: 1px solid black; \r
+}\r
+\r
+input#phrase\r
+{\r
+  width:28em;\r
+  background-color: #e0f0f0;\r
+}\r
+\r
+input#username\r
+{\r
+  width:14em;\r
+  background-color: #e0f0f0;\r
+}\r
+\r
+div.hidden\r
+{\r
+  display: none;\r
+}\r
+\r
+span.from\r
+{\r
+  font-weight: bold;\r
+}\r
+\r
+span.alert\r
+{\r
+  font-style: italic;\r
+}\r
+\r
diff --git a/webapps/cometd/examples/chat/chat.js b/webapps/cometd/examples/chat/chat.js
new file mode 100644 (file)
index 0000000..6258434
--- /dev/null
@@ -0,0 +1,139 @@
+dojo.require("dojox.cometd");\r
+dojo.require("dojox.cometd.timestamp");\r
+\r
+var room = {\r
+    _last: "",\r
+    _username: null,\r
+    _connected: true,\r
+\r
+    join: function(name){\r
+        \r
+        if(name == null || name.length==0 ){\r
+            alert('Please enter a username!');\r
+        }else{\r
+               \r
+            dojox.cometd.init(new String(document.location).replace(/http:\/\/[^\/]*/,'').replace(/\/examples\/.*$/,'')+"/cometd");\r
+            // dojox.cometd.init("http://127.0.0.2:8080/cometd");\r
+            this._connected=true;\r
+           \r
+            this._username=name;\r
+            dojo.byId('join').className='hidden';\r
+            dojo.byId('joined').className='';\r
+            dojo.byId('phrase').focus();\r
+           \r
+            // subscribe and join\r
+           dojox.cometd.startBatch();\r
+            dojox.cometd.subscribe("/chat/demo", room, "_chat");\r
+            dojox.cometd.publish("/chat/demo", { user: room._username, join: true, chat : room._username+" has joined"});\r
+           dojox.cometd.endBatch();\r
+           \r
+            // handle cometd failures while in the room\r
+            room._meta=dojo.subscribe("/cometd/meta",dojo.hitch(this,function(event){\r
+                console.debug(event);   \r
+                if (event.action=="handshake"){\r
+                   room._chat({data:{join:true,user:"SERVER",chat:"reinitialized"}});\r
+                    dojox.cometd.subscribe("/chat/demo", room, "_chat");\r
+                } else if (event.action=="connect") {\r
+                   if (event.successful && !this._connected)\r
+                        room._chat({data:{leave:true,user:"SERVER",chat:"reconnected!"}});\r
+                   if (!event.successful && this._connected)\r
+                        room._chat({data:{leave:true,user:"SERVER",chat:"disconnected!"}});\r
+                   this._connected=event.successful;\r
+               }\r
+            }));\r
+        }\r
+    },\r
+\r
+    leave: function(){\r
+        if (room._username==null)\r
+            return;\r
+           \r
+       if (room._meta)\r
+            dojo.unsubscribe(room._meta);\r
+       room._meta=null;\r
+       \r
+       dojox.cometd.startBatch();\r
+        dojox.cometd.unsubscribe("/chat/demo", room, "_chat");\r
+        dojox.cometd.publish("/chat/demo", { user: room._username, leave: true, chat : room._username+" has left"});\r
+       dojox.cometd.endBatch();\r
+\r
+        // switch the input form\r
+        dojo.byId('join').className='';\r
+        dojo.byId('joined').className='hidden';\r
+        dojo.byId('username').focus();\r
+        room._username=null;\r
+        dojox.cometd.disconnect();\r
+    },\r
+      \r
+    chat: function(text){\r
+        if(!text || !text.length){ return false; }\r
+        dojox.cometd.publish("/chat/demo", { user: room._username, chat: text});\r
+    },\r
+\r
+    _chat: function(message){\r
+        var chat=dojo.byId('chat');\r
+        if(!message.data){\r
+            alert("bad message format "+message);\r
+            return;\r
+        }\r
+        var from=message.data.user;\r
+        var special=message.data.join || message.data.leave;\r
+        var text=message.data.chat;\r
+        if(!text){ return; }\r
+\r
+        if( !special && from == room._last ){\r
+            from="...";\r
+        }else{\r
+            room._last=from;\r
+            from+=":";\r
+        }\r
+\r
+        if(special){\r
+            chat.innerHTML += "<span class=\"alert\"><span class=\"from\">"+from+"&nbsp;</span><span class=\"text\">"+text+"</span></span><br/>";\r
+            room._last="";\r
+        }else{\r
+            chat.innerHTML += "<span class=\"from\">"+from+"&nbsp;</span><span class=\"text\">"+text+"</span><br/>";\r
+        } \r
+        chat.scrollTop = chat.scrollHeight - chat.clientHeight;    \r
+    },\r
+  \r
+  _init: function(){\r
+        dojo.byId('join').className='';\r
+        dojo.byId('joined').className='hidden';\r
+        dojo.byId('username').focus();\r
+       \r
+        var element=dojo.byId('username');\r
+        element.setAttribute("autocomplete","OFF"); \r
+        dojo.connect(element, "onkeyup", function(e){   \r
+            if(e.keyCode == dojo.keys.ENTER){\r
+                room.join(dojo.byId('username').value);\r
+                return false;\r
+            }\r
+            return true;\r
+       });\r
+  \r
+        dojo.connect(dojo.byId('joinB'), "onclick", function(e){\r
+            room.join(dojo.byId('username').value);\r
+            e.preventDefault();\r
+       });\r
+  \r
+        element=dojo.byId('phrase');\r
+        element.setAttribute("autocomplete","OFF");\r
+        dojo.connect(element, "onkeyup", function(e){   \r
+            if(e.keyCode == dojo.keys.ENTER){\r
+                room.chat(dojo.byId('phrase').value);\r
+                dojo.byId('phrase').value='';\r
+                e.preventDefault();\r
+            }\r
+       });\r
+  \r
+       dojo.connect(dojo.byId('sendB'), "onkeyup", function(e){   \r
+           room.chat(dojo.byId('phrase').value);\r
+            dojo.byId('phrase').value='';\r
+       });\r
+       dojo.connect(dojo.byId('leaveB'), "onclick", room, "leave");\r
+    } \r
+};\r
+\r
+dojo.addOnLoad(room, "_init");\r
+dojo.addOnUnload(room,"leave");\r
diff --git a/webapps/cometd/examples/chat/index.html b/webapps/cometd/examples/chat/index.html
new file mode 100644 (file)
index 0000000..3d96299
--- /dev/null
@@ -0,0 +1,26 @@
+<html>\r
+<head>\r
+    <title>Cometd chat</title>\r
+    <script type="text/javascript" src="../dojo/dojo/dojo.js"></script>\r
+    <script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>\r
+    <script type="text/javascript" src="chat.js"></script>\r
+    <link rel="stylesheet" type="text/css" href="chat.css">\r
+</head>\r
+<body>\r
+<h1>Cometd Chat</h1>\r
+\r
+<div id="chatroom">\r
+ <div id="chat"></div>\r
+ <div id="input">\r
+   <div id="join" >\r
+     Username:&nbsp;<input id="username" type="text"/><input id="joinB" class="button" type="submit" name="join" value="Join"/>\r
+   </div>\r
+   <div id="joined" class="hidden">\r
+     Chat:&nbsp;<input id="phrase" type="text"/>\r
+     <input id="sendB" class="button" type="submit" name="join" value="Send"/>\r
+     <input id="leaveB" class="button" type="submit" name="join" value="Leave"/>\r
+   </div>\r
+  </div>\r
+ </div>\r
+\r
+</body>\r
diff --git a/webapps/cometd/examples/dojo/dojo/dojo.js b/webapps/cometd/examples/dojo/dojo/dojo.js
new file mode 100644 (file)
index 0000000..13986ec
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+       Copyright (c) 2004-2008, The Dojo Foundation
+       All Rights Reserved.
+
+       Licensed under the Academic Free License version 2.1 or above OR the
+       modified BSD license. For more information on Dojo licensing, see:
+
+               http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing
+*/
+
+/*
+       This is a compiled version of Dojo, built for deployment and not for
+       development. To get an editable version, please visit:
+
+               http://dojotoolkit.org
+
+       for documentation and information on getting the source.
+*/
+
+(function(){var _1=null;if((_1||(typeof djConfig!="undefined"&&djConfig.scopeMap))&&(typeof window!="undefined")){var _2="",_3="",_4="",_5={},_6={};_1=_1||djConfig.scopeMap;for(var i=0;i<_1.length;i++){var _8=_1[i];_2+="var "+_8[0]+" = {}; "+_8[1]+" = "+_8[0]+";"+_8[1]+"._scopeName = '"+_8[1]+"';";_3+=(i==0?"":",")+_8[0];_4+=(i==0?"":",")+_8[1];_5[_8[0]]=_8[1];_6[_8[1]]=_8[0];}eval(_2+"dojo._scopeArgs = ["+_4+"];");dojo._scopePrefixArgs=_3;dojo._scopePrefix="(function("+_3+"){";dojo._scopeSuffix="})("+_4+")";dojo._scopeMap=_5;dojo._scopeMapRev=_6;}(function(){if(!this["console"]){this.console={log:function(){}};}var cn=["assert","count","debug","dir","dirxml","error","group","groupEnd","info","profile","profileEnd","time","timeEnd","trace","warn","log"];var i=0,tn;while((tn=cn[i++])){if(!console[tn]){(function(){var _c=tn+"";console[_c]=function(){var a=Array.apply({},arguments);a.unshift(_c+":");console.log(a.join(" "));};})();}}if(typeof dojo=="undefined"){this.dojo={_scopeName:"dojo",_scopePrefix:"",_scopePrefixArgs:"",_scopeSuffix:"",_scopeMap:{},_scopeMapRev:{}};}var d=dojo;if(typeof dijit=="undefined"){this.dijit={_scopeName:"dijit"};}if(typeof dojox=="undefined"){this.dojox={_scopeName:"dojox"};}if(!d._scopeArgs){d._scopeArgs=[dojo,dijit,dojox];}d.global=this;d.config={isDebug:false,debugAtAllCosts:false};if(typeof djConfig!="undefined"){for(var _f in djConfig){d.config[_f]=djConfig[_f];}}var _10=["Browser","Rhino","Spidermonkey","Mobile"];var t;while((t=_10.shift())){d["is"+t]=false;}dojo.locale=d.config.locale;var rev="$Rev: 13707 $".match(/\d+/);dojo.version={major:1,minor:1,patch:1,flag:"",revision:rev?+rev[0]:999999,toString:function(){with(d.version){return major+"."+minor+"."+patch+flag+" ("+revision+")";}}};if(typeof OpenAjax!="undefined"){OpenAjax.hub.registerLibrary(dojo._scopeName,"http://dojotoolkit.org",d.version.toString());}dojo._mixin=function(obj,_14){var _15={};for(var x in _14){if(_15[x]===undefined||_15[x]!=_14[x]){obj[x]=_14[x];}}if(d["isIE"]&&_14){var p=_14.toString;if(typeof p=="function"&&p!=obj.toString&&p!=_15.toString&&p!="\nfunction toString() {\n    [native code]\n}\n"){obj.toString=_14.toString;}}return obj;};dojo.mixin=function(obj,_19){for(var i=1,l=arguments.length;i<l;i++){d._mixin(obj,arguments[i]);}return obj;};dojo._getProp=function(_1c,_1d,_1e){var obj=_1e||d.global;for(var i=0,p;obj&&(p=_1c[i]);i++){if(i==0&&this._scopeMap[p]){p=this._scopeMap[p];}obj=(p in obj?obj[p]:(_1d?obj[p]={}:undefined));}return obj;};dojo.setObject=function(_22,_23,_24){var _25=_22.split("."),p=_25.pop(),obj=d._getProp(_25,true,_24);return obj&&p?(obj[p]=_23):undefined;};dojo.getObject=function(_28,_29,_2a){return d._getProp(_28.split("."),_29,_2a);};dojo.exists=function(_2b,obj){return !!d.getObject(_2b,false,obj);};dojo["eval"]=function(_2d){return d.global.eval?d.global.eval(_2d):eval(_2d);};d.deprecated=d.experimental=function(){};})();(function(){var d=dojo;d.mixin(d,{_loadedModules:{},_inFlightCount:0,_hasResource:{},_modulePrefixes:{dojo:{name:"dojo",value:"."},doh:{name:"doh",value:"../util/doh"},tests:{name:"tests",value:"tests"}},_moduleHasPrefix:function(_2f){var mp=this._modulePrefixes;return !!(mp[_2f]&&mp[_2f].value);},_getModulePrefix:function(_31){var mp=this._modulePrefixes;if(this._moduleHasPrefix(_31)){return mp[_31].value;}return _31;},_loadedUrls:[],_postLoad:false,_loaders:[],_unloaders:[],_loadNotifying:false});dojo._loadPath=function(_33,_34,cb){var uri=((_33.charAt(0)=="/"||_33.match(/^\w+:/))?"":this.baseUrl)+_33;try{return !_34?this._loadUri(uri,cb):this._loadUriAndCheck(uri,_34,cb);}catch(e){console.error(e);return false;}};dojo._loadUri=function(uri,cb){if(this._loadedUrls[uri]){return true;}var _39=this._getText(uri,true);if(!_39){return false;}this._loadedUrls[uri]=true;this._loadedUrls.push(uri);if(cb){_39="("+_39+")";}else{_39=this._scopePrefix+_39+this._scopeSuffix;}if(d.isMoz){_39+="\r\n//@ sourceURL="+uri;}var _3a=d["eval"](_39);if(cb){cb(_3a);}return true;};dojo._loadUriAndCheck=function(uri,_3c,cb){var ok=false;try{ok=this._loadUri(uri,cb);}catch(e){console.error("failed loading "+uri+" with error: "+e);}return !!(ok&&this._loadedModules[_3c]);};dojo.loaded=function(){this._loadNotifying=true;this._postLoad=true;var mll=d._loaders;this._loaders=[];for(var x=0;x<mll.length;x++){try{mll[x]();}catch(e){throw e;console.error("dojo.addOnLoad callback failed: "+e,e);}}this._loadNotifying=false;if(d._postLoad&&d._inFlightCount==0&&mll.length){d._callLoaded();}};dojo.unloaded=function(){var mll=this._unloaders;while(mll.length){(mll.pop())();}};var _42=function(arr,obj,fn){if(!fn){arr.push(obj);}else{if(fn){var _46=(typeof fn=="string")?obj[fn]:fn;arr.push(function(){_46.call(obj);});}}};dojo.addOnLoad=function(obj,_48){_42(d._loaders,obj,_48);if(d._postLoad&&d._inFlightCount==0&&!d._loadNotifying){d._callLoaded();}};dojo.addOnUnload=function(obj,_4a){_42(d._unloaders,obj,_4a);};dojo._modulesLoaded=function(){if(d._postLoad){return;}if(d._inFlightCount>0){console.warn("files still in flight!");return;}d._callLoaded();};dojo._callLoaded=function(){if(typeof setTimeout=="object"||(dojo.config.useXDomain&&d.isOpera)){if(dojo.isAIR){setTimeout(function(){dojo.loaded();},0);}else{setTimeout(dojo._scopeName+".loaded();",0);}}else{d.loaded();}};dojo._getModuleSymbols=function(_4b){var _4c=_4b.split(".");for(var i=_4c.length;i>0;i--){var _4e=_4c.slice(0,i).join(".");if((i==1)&&!this._moduleHasPrefix(_4e)){_4c[0]="../"+_4c[0];}else{var _4f=this._getModulePrefix(_4e);if(_4f!=_4e){_4c.splice(0,i,_4f);break;}}}return _4c;};dojo._global_omit_module_check=false;dojo._loadModule=dojo.require=function(_50,_51){_51=this._global_omit_module_check||_51;var _52=this._loadedModules[_50];if(_52){return _52;}var _53=this._getModuleSymbols(_50).join("/")+".js";var _54=(!_51)?_50:null;var ok=this._loadPath(_53,_54);if(!ok&&!_51){throw new Error("Could not load '"+_50+"'; last tried '"+_53+"'");}if(!_51&&!this._isXDomain){_52=this._loadedModules[_50];if(!_52){throw new Error("symbol '"+_50+"' is not defined after loading '"+_53+"'");}}return _52;};dojo.provide=function(_56){_56=_56+"";return (d._loadedModules[_56]=d.getObject(_56,true));};dojo.platformRequire=function(_57){var _58=_57.common||[];var _59=_58.concat(_57[d._name]||_57["default"]||[]);for(var x=0;x<_59.length;x++){var _5b=_59[x];if(_5b.constructor==Array){d._loadModule.apply(d,_5b);}else{d._loadModule(_5b);}}};dojo.requireIf=function(_5c,_5d){if(_5c===true){var _5e=[];for(var i=1;i<arguments.length;i++){_5e.push(arguments[i]);}d.require.apply(d,_5e);}};dojo.requireAfterIf=d.requireIf;dojo.registerModulePath=function(_60,_61){d._modulePrefixes[_60]={name:_60,value:_61};};dojo.requireLocalization=function(_62,_63,_64,_65){d.require("dojo.i18n");d.i18n._requireLocalization.apply(d.hostenv,arguments);};var ore=new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$");var ire=new RegExp("^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$");dojo._Url=function(){var n=null;var _a=arguments;var uri=[_a[0]];for(var i=1;i<_a.length;i++){if(!_a[i]){continue;}var _6c=new d._Url(_a[i]+"");var _6d=new d._Url(uri[0]+"");if(_6c.path==""&&!_6c.scheme&&!_6c.authority&&!_6c.query){if(_6c.fragment!=n){_6d.fragment=_6c.fragment;}_6c=_6d;}else{if(!_6c.scheme){_6c.scheme=_6d.scheme;if(!_6c.authority){_6c.authority=_6d.authority;if(_6c.path.charAt(0)!="/"){var _6e=_6d.path.substring(0,_6d.path.lastIndexOf("/")+1)+_6c.path;var _6f=_6e.split("/");for(var j=0;j<_6f.length;j++){if(_6f[j]=="."){if(j==_6f.length-1){_6f[j]="";}else{_6f.splice(j,1);j--;}}else{if(j>0&&!(j==1&&_6f[0]=="")&&_6f[j]==".."&&_6f[j-1]!=".."){if(j==(_6f.length-1)){_6f.splice(j,1);_6f[j-1]="";}else{_6f.splice(j-1,2);j-=2;}}}}_6c.path=_6f.join("/");}}}}uri=[];if(_6c.scheme){uri.push(_6c.scheme,":");}if(_6c.authority){uri.push("//",_6c.authority);}uri.push(_6c.path);if(_6c.query){uri.push("?",_6c.query);}if(_6c.fragment){uri.push("#",_6c.fragment);}}this.uri=uri.join("");var r=this.uri.match(ore);this.scheme=r[2]||(r[1]?"":n);this.authority=r[4]||(r[3]?"":n);this.path=r[5];this.query=r[7]||(r[6]?"":n);this.fragment=r[9]||(r[8]?"":n);if(this.authority!=n){r=this.authority.match(ire);this.user=r[3]||n;this.password=r[4]||n;this.host=r[5];this.port=r[7]||n;}};dojo._Url.prototype.toString=function(){return this.uri;};dojo.moduleUrl=function(_72,url){var loc=d._getModuleSymbols(_72).join("/");if(!loc){return null;}if(loc.lastIndexOf("/")!=loc.length-1){loc+="/";}var _75=loc.indexOf(":");if(loc.charAt(0)!="/"&&(_75==-1||_75>loc.indexOf("/"))){loc=d.baseUrl+loc;}return new d._Url(loc,url);};})();if(typeof window!="undefined"){dojo.isBrowser=true;dojo._name="browser";(function(){var d=dojo;if(document&&document.getElementsByTagName){var _77=document.getElementsByTagName("script");var _78=/dojo(\.xd)?\.js(\W|$)/i;for(var i=0;i<_77.length;i++){var src=_77[i].getAttribute("src");if(!src){continue;}var m=src.match(_78);if(m){if(!d.config.baseUrl){d.config.baseUrl=src.substring(0,m.index);}var cfg=_77[i].getAttribute("djConfig");if(cfg){var _7d=eval("({ "+cfg+" })");for(var x in _7d){dojo.config[x]=_7d[x];}}break;}}}d.baseUrl=d.config.baseUrl;var n=navigator;var dua=n.userAgent;var dav=n.appVersion;var tv=parseFloat(dav);d.isOpera=(dua.indexOf("Opera")>=0)?tv:0;var idx=Math.max(dav.indexOf("WebKit"),dav.indexOf("Safari"),0);if(idx){d.isSafari=parseFloat(dav.split("Version/")[1])||((parseFloat(dav.substr(idx+7))>=419.3)?3:2)||2;}d.isAIR=(dua.indexOf("AdobeAIR")>=0)?1:0;d.isKhtml=(dav.indexOf("Konqueror")>=0||d.isSafari)?tv:0;d.isMozilla=d.isMoz=(dua.indexOf("Gecko")>=0&&!d.isKhtml)?tv:0;d.isFF=d.isIE=0;if(d.isMoz){d.isFF=parseFloat(dua.split("Firefox/")[1])||0;}if(document.all&&!d.isOpera){d.isIE=parseFloat(dav.split("MSIE ")[1])||0;}if(dojo.isIE&&window.location.protocol==="file:"){dojo.config.ieForceActiveXXhr=true;}var cm=document.compatMode;d.isQuirks=cm=="BackCompat"||cm=="QuirksMode"||d.isIE<6;d.locale=dojo.config.locale||(d.isIE?n.userLanguage:n.language).toLowerCase();d._XMLHTTP_PROGIDS=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];d._xhrObj=function(){var _85=null;var _86=null;if(!dojo.isIE||!dojo.config.ieForceActiveXXhr){try{_85=new XMLHttpRequest();}catch(e){}}if(!_85){for(var i=0;i<3;++i){var _88=d._XMLHTTP_PROGIDS[i];try{_85=new ActiveXObject(_88);}catch(e){_86=e;}if(_85){d._XMLHTTP_PROGIDS=[_88];break;}}}if(!_85){throw new Error("XMLHTTP not available: "+_86);}return _85;};d._isDocumentOk=function(_89){var _8a=_89.status||0;return (_8a>=200&&_8a<300)||_8a==304||_8a==1223||(!_8a&&(location.protocol=="file:"||location.protocol=="chrome:"));};var _8b=window.location+"";var _8c=document.getElementsByTagName("base");var _8d=(_8c&&_8c.length>0);d._getText=function(uri,_8f){var _90=this._xhrObj();if(!_8d&&dojo._Url){uri=(new dojo._Url(_8b,uri)).toString();}if(d.config.cacheBust){uri+=(uri.indexOf("?")==-1?"?":"&")+String(d.config.cacheBust).replace(/\W+/g,"");}_90.open("GET",uri,false);try{_90.send(null);if(!d._isDocumentOk(_90)){var err=Error("Unable to load "+uri+" status:"+_90.status);err.status=_90.status;err.responseText=_90.responseText;throw err;}}catch(e){if(_8f){return null;}throw e;}return _90.responseText;};})();dojo._initFired=false;dojo._loadInit=function(e){dojo._initFired=true;var _93=(e&&e.type)?e.type.toLowerCase():"load";if(arguments.callee.initialized||(_93!="domcontentloaded"&&_93!="load")){return;}arguments.callee.initialized=true;if("_khtmlTimer" in dojo){clearInterval(dojo._khtmlTimer);delete dojo._khtmlTimer;}if(dojo._inFlightCount==0){dojo._modulesLoaded();}};dojo._fakeLoadInit=function(){dojo._loadInit({type:"load"});};if(!dojo.config.afterOnLoad){if(document.addEventListener){if(dojo.isOpera||dojo.isFF>=3||(dojo.isMoz&&dojo.config.enableMozDomContentLoaded===true)){document.addEventListener("DOMContentLoaded",dojo._loadInit,null);}window.addEventListener("load",dojo._loadInit,null);}if(dojo.isAIR){window.addEventListener("load",dojo._loadInit,null);}else{if(/(WebKit|khtml)/i.test(navigator.userAgent)){dojo._khtmlTimer=setInterval(function(){if(/loaded|complete/.test(document.readyState)){dojo._loadInit();}},10);}}}(function(){var _w=window;var _95=function(_96,fp){var _98=_w[_96]||function(){};_w[_96]=function(){fp.apply(_w,arguments);_98.apply(_w,arguments);};};if(dojo.isIE){if(!dojo.config.afterOnLoad){document.write("<scr"+"ipt defer src=\"//:\" "+"onreadystatechange=\"if(this.readyState=='complete'){"+dojo._scopeName+"._loadInit();}\">"+"</scr"+"ipt>");}var _99=true;_95("onbeforeunload",function(){_w.setTimeout(function(){_99=false;},0);});_95("onunload",function(){if(_99){dojo.unloaded();}});try{document.namespaces.add("v","urn:schemas-microsoft-com:vml");document.createStyleSheet().addRule("v\\:*","behavior:url(#default#VML)");}catch(e){}}else{_95("onbeforeunload",function(){dojo.unloaded();});}})();}(function(){var mp=dojo.config["modulePaths"];if(mp){for(var _9b in mp){dojo.registerModulePath(_9b,mp[_9b]);}}})();if(dojo.config.isDebug){dojo.require("dojo._firebug.firebug");}if(dojo.config.debugAtAllCosts){dojo.config.useXDomain=true;dojo.require("dojo._base._loader.loader_xd");dojo.require("dojo._base._loader.loader_debug");dojo.require("dojo.i18n");}if(!dojo._hasResource["dojo._base.lang"]){dojo._hasResource["dojo._base.lang"]=true;dojo.provide("dojo._base.lang");dojo.isString=function(it){return !!arguments.length&&it!=null&&(typeof it=="string"||it instanceof String);};dojo.isArray=function(it){return it&&(it instanceof Array||typeof it=="array");};dojo.isFunction=(function(){var _9e=function(it){return it&&(typeof it=="function"||it instanceof Function);};return dojo.isSafari?function(it){if(typeof it=="function"&&it=="[object NodeList]"){return false;}return _9e(it);}:_9e;})();dojo.isObject=function(it){return it!==undefined&&(it===null||typeof it=="object"||dojo.isArray(it)||dojo.isFunction(it));};dojo.isArrayLike=function(it){var d=dojo;return it&&it!==undefined&&!d.isString(it)&&!d.isFunction(it)&&!(it.tagName&&it.tagName.toLowerCase()=="form")&&(d.isArray(it)||isFinite(it.length));};dojo.isAlien=function(it){return it&&!dojo.isFunction(it)&&/\{\s*\[native code\]\s*\}/.test(String(it));};dojo.extend=function(_a5,_a6){for(var i=1,l=arguments.length;i<l;i++){dojo._mixin(_a5.prototype,arguments[i]);}return _a5;};dojo._hitchArgs=function(_a9,_aa){var pre=dojo._toArray(arguments,2);var _ac=dojo.isString(_aa);return function(){var _ad=dojo._toArray(arguments);var f=_ac?(_a9||dojo.global)[_aa]:_aa;return f&&f.apply(_a9||this,pre.concat(_ad));};};dojo.hitch=function(_af,_b0){if(arguments.length>2){return dojo._hitchArgs.apply(dojo,arguments);}if(!_b0){_b0=_af;_af=null;}if(dojo.isString(_b0)){_af=_af||dojo.global;if(!_af[_b0]){throw (["dojo.hitch: scope[\"",_b0,"\"] is null (scope=\"",_af,"\")"].join(""));}return function(){return _af[_b0].apply(_af,arguments||[]);};}return !_af?_b0:function(){return _b0.apply(_af,arguments||[]);};};dojo.delegate=dojo._delegate=function(obj,_b2){function TMP(){};TMP.prototype=obj;var tmp=new TMP();if(_b2){dojo.mixin(tmp,_b2);}return tmp;};dojo.partial=function(_b4){var arr=[null];return dojo.hitch.apply(dojo,arr.concat(dojo._toArray(arguments)));};dojo._toArray=function(obj,_b7,_b8){var arr=_b8||[];for(var x=_b7||0;x<obj.length;x++){arr.push(obj[x]);}return arr;};dojo.clone=function(o){if(!o){return o;}if(dojo.isArray(o)){var r=[];for(var i=0;i<o.length;++i){r.push(dojo.clone(o[i]));}return r;}if(!dojo.isObject(o)){return o;}if(o.nodeType&&o.cloneNode){return o.cloneNode(true);}if(o instanceof Date){return new Date(o.getTime());}var r=new o.constructor();for(var i in o){if(!(i in r)||r[i]!=o[i]){r[i]=dojo.clone(o[i]);}}return r;};dojo.trim=function(str){return str.replace(/^\s\s*/,"").replace(/\s\s*$/,"");};}if(!dojo._hasResource["dojo._base.declare"]){dojo._hasResource["dojo._base.declare"]=true;dojo.provide("dojo._base.declare");dojo.declare=function(_bf,_c0,_c1){var dd=arguments.callee,_c3;if(dojo.isArray(_c0)){_c3=_c0;_c0=_c3.shift();}if(_c3){dojo.forEach(_c3,function(m){if(!m){throw (_bf+": mixin #"+i+" is null");}_c0=dd._delegate(_c0,m);});}var _c5=(_c1||0).constructor,_c6=dd._delegate(_c0),fn;for(var i in _c1){if(dojo.isFunction(fn=_c1[i])&&!0[i]){fn.nom=i;}}dojo.extend(_c6,{declaredClass:_bf,_constructor:_c5,preamble:null},_c1||0);_c6.prototype.constructor=_c6;return dojo.setObject(_bf,_c6);};dojo.mixin(dojo.declare,{_delegate:function(_c9,_ca){var bp=(_c9||0).prototype,mp=(_ca||0).prototype;var _cd=dojo.declare._makeCtor();dojo.mixin(_cd,{superclass:bp,mixin:mp,extend:dojo.declare._extend});if(_c9){_cd.prototype=dojo._delegate(bp);}dojo.extend(_cd,dojo.declare._core,mp||0,{_constructor:null,preamble:null});_cd.prototype.constructor=_cd;_cd.prototype.declaredClass=(bp||0).declaredClass+"_"+(mp||0).declaredClass;return _cd;},_extend:function(_ce){for(var i in _ce){if(dojo.isFunction(fn=_ce[i])&&!0[i]){fn.nom=i;}}dojo.extend(this,_ce);},_makeCtor:function(){return function(){this._construct(arguments);};},_core:{_construct:function(_d0){var c=_d0.callee,s=c.superclass,ct=s&&s.constructor,m=c.mixin,mct=m&&m.constructor,a=_d0,ii,fn;if(a[0]){if(((fn=a[0].preamble))){a=fn.apply(this,a)||a;}}if((fn=c.prototype.preamble)){a=fn.apply(this,a)||a;}if(ct&&ct.apply){ct.apply(this,a);}if(mct&&mct.apply){mct.apply(this,a);}if((ii=c.prototype._constructor)){ii.apply(this,_d0);}if(this.constructor.prototype==c.prototype&&(ct=this.postscript)){ct.apply(this,_d0);}},_findMixin:function(_d9){var c=this.constructor,p,m;while(c){p=c.superclass;m=c.mixin;if(m==_d9||(m instanceof _d9.constructor)){return p;}if(m&&(m=m._findMixin(_d9))){return m;}c=p&&p.constructor;}},_findMethod:function(_dd,_de,_df,has){var p=_df,c,m,f;do{c=p.constructor;m=c.mixin;if(m&&(m=this._findMethod(_dd,_de,m,has))){return m;}if((f=p[_dd])&&(has==(f==_de))){return p;}p=c.superclass;}while(p);return !has&&(p=this._findMixin(_df))&&this._findMethod(_dd,_de,p,has);},inherited:function(_e5,_e6,_e7){var a=arguments;if(!dojo.isString(a[0])){_e7=_e6;_e6=_e5;_e5=_e6.callee.nom;}a=_e7||_e6;var c=_e6.callee,p=this.constructor.prototype,fn,mp;if(this[_e5]!=c||p[_e5]==c){mp=this._findMethod(_e5,c,p,true);if(!mp){throw (this.declaredClass+": inherited method \""+_e5+"\" mismatch");}p=this._findMethod(_e5,c,mp,false);}fn=p&&p[_e5];if(!fn){throw (mp.declaredClass+": inherited method \""+_e5+"\" not found");}return fn.apply(this,a);}}});}if(!dojo._hasResource["dojo._base.connect"]){dojo._hasResource["dojo._base.connect"]=true;dojo.provide("dojo._base.connect");dojo._listener={getDispatcher:function(){return function(){var ap=Array.prototype,c=arguments.callee,ls=c._listeners,t=c.target;var r=t&&t.apply(this,arguments);for(var i in ls){if(!(i in ap)){ls[i].apply(this,arguments);}}return r;};},add:function(_f3,_f4,_f5){_f3=_f3||dojo.global;var f=_f3[_f4];if(!f||!f._listeners){var d=dojo._listener.getDispatcher();d.target=f;d._listeners=[];f=_f3[_f4]=d;}return f._listeners.push(_f5);},remove:function(_f8,_f9,_fa){var f=(_f8||dojo.global)[_f9];if(f&&f._listeners&&_fa--){delete f._listeners[_fa];}}};dojo.connect=function(obj,_fd,_fe,_ff,_100){var a=arguments,args=[],i=0;args.push(dojo.isString(a[0])?null:a[i++],a[i++]);var a1=a[i+1];args.push(dojo.isString(a1)||dojo.isFunction(a1)?a[i++]:null,a[i++]);for(var l=a.length;i<l;i++){args.push(a[i]);}return dojo._connect.apply(this,args);};dojo._connect=function(obj,_106,_107,_108){var l=dojo._listener,h=l.add(obj,_106,dojo.hitch(_107,_108));return [obj,_106,h,l];};dojo.disconnect=function(_10b){if(_10b&&_10b[0]!==undefined){dojo._disconnect.apply(this,_10b);delete _10b[0];}};dojo._disconnect=function(obj,_10d,_10e,_10f){_10f.remove(obj,_10d,_10e);};dojo._topics={};dojo.subscribe=function(_110,_111,_112){return [_110,dojo._listener.add(dojo._topics,_110,dojo.hitch(_111,_112))];};dojo.unsubscribe=function(_113){if(_113){dojo._listener.remove(dojo._topics,_113[0],_113[1]);}};dojo.publish=function(_114,args){var f=dojo._topics[_114];if(f){f.apply(this,args||[]);}};dojo.connectPublisher=function(_117,obj,_119){var pf=function(){dojo.publish(_117,arguments);};return (_119)?dojo.connect(obj,_119,pf):dojo.connect(obj,pf);};}if(!dojo._hasResource["dojo._base.Deferred"]){dojo._hasResource["dojo._base.Deferred"]=true;dojo.provide("dojo._base.Deferred");dojo.Deferred=function(_11b){this.chain=[];this.id=this._nextId();this.fired=-1;this.paused=0;this.results=[null,null];this.canceller=_11b;this.silentlyCancelled=false;};dojo.extend(dojo.Deferred,{_nextId:(function(){var n=1;return function(){return n++;};})(),cancel:function(){var err;if(this.fired==-1){if(this.canceller){err=this.canceller(this);}else{this.silentlyCancelled=true;}if(this.fired==-1){if(!(err instanceof Error)){var res=err;err=new Error("Deferred Cancelled");err.dojoType="cancel";err.cancelResult=res;}this.errback(err);}}else{if((this.fired==0)&&(this.results[0] instanceof dojo.Deferred)){this.results[0].cancel();}}},_resback:function(res){this.fired=((res instanceof Error)?1:0);this.results[this.fired]=res;this._fire();},_check:function(){if(this.fired!=-1){if(!this.silentlyCancelled){throw new Error("already called!");}this.silentlyCancelled=false;return;}},callback:function(res){this._check();this._resback(res);},errback:function(res){this._check();if(!(res instanceof Error)){res=new Error(res);}this._resback(res);},addBoth:function(cb,cbfn){var _124=dojo.hitch.apply(dojo,arguments);return this.addCallbacks(_124,_124);},addCallback:function(cb,cbfn){return this.addCallbacks(dojo.hitch.apply(dojo,arguments));},addErrback:function(cb,cbfn){return this.addCallbacks(null,dojo.hitch.apply(dojo,arguments));},addCallbacks:function(cb,eb){this.chain.push([cb,eb]);if(this.fired>=0){this._fire();}return this;},_fire:function(){var _12b=this.chain;var _12c=this.fired;var res=this.results[_12c];var self=this;var cb=null;while((_12b.length>0)&&(this.paused==0)){var f=_12b.shift()[_12c];if(!f){continue;}try{res=f(res);_12c=((res instanceof Error)?1:0);if(res instanceof dojo.Deferred){cb=function(res){self._resback(res);self.paused--;if((self.paused==0)&&(self.fired>=0)){self._fire();}};this.paused++;}}catch(err){console.debug(err);_12c=1;res=err;}}this.fired=_12c;this.results[_12c]=res;if((cb)&&(this.paused)){res.addBoth(cb);}}});}if(!dojo._hasResource["dojo._base.json"]){dojo._hasResource["dojo._base.json"]=true;dojo.provide("dojo._base.json");dojo.fromJson=function(json){return eval("("+json+")");};dojo._escapeString=function(str){return ("\""+str.replace(/(["\\])/g,"\\$1")+"\"").replace(/[\f]/g,"\\f").replace(/[\b]/g,"\\b").replace(/[\n]/g,"\\n").replace(/[\t]/g,"\\t").replace(/[\r]/g,"\\r");};dojo.toJsonIndentStr="\t";dojo.toJson=function(it,_135,_136){if(it===undefined){return "undefined";}var _137=typeof it;if(_137=="number"||_137=="boolean"){return it+"";}if(it===null){return "null";}if(dojo.isString(it)){return dojo._escapeString(it);}if(it.nodeType&&it.cloneNode){return "";}var _138=arguments.callee;var _139;_136=_136||"";var _13a=_135?_136+dojo.toJsonIndentStr:"";if(typeof it.__json__=="function"){_139=it.__json__();if(it!==_139){return _138(_139,_135,_13a);}}if(typeof it.json=="function"){_139=it.json();if(it!==_139){return _138(_139,_135,_13a);}}var sep=_135?" ":"";var _13c=_135?"\n":"";if(dojo.isArray(it)){var res=dojo.map(it,function(obj){var val=_138(obj,_135,_13a);if(typeof val!="string"){val="undefined";}return _13c+_13a+val;});return "["+res.join(","+sep)+_13c+_136+"]";}if(_137=="function"){return null;}var _140=[];for(var key in it){var _142;if(typeof key=="number"){_142="\""+key+"\"";}else{if(typeof key=="string"){_142=dojo._escapeString(key);}else{continue;}}val=_138(it[key],_135,_13a);if(typeof val!="string"){continue;}_140.push(_13c+_13a+_142+":"+sep+val);}return "{"+_140.join(","+sep)+_13c+_136+"}";};}if(!dojo._hasResource["dojo._base.array"]){dojo._hasResource["dojo._base.array"]=true;dojo.provide("dojo._base.array");(function(){var _143=function(arr,obj,cb){return [dojo.isString(arr)?arr.split(""):arr,obj||dojo.global,dojo.isString(cb)?new Function("item","index","array",cb):cb];};dojo.mixin(dojo,{indexOf:function(_147,_148,_149,_14a){var step=1,end=_147.length||0,i=0;if(_14a){i=end-1;step=end=-1;}if(_149!=undefined){i=_149;}if((_14a&&i>end)||i<end){for(;i!=end;i+=step){if(_147[i]==_148){return i;}}}return -1;},lastIndexOf:function(_14d,_14e,_14f){return dojo.indexOf(_14d,_14e,_14f,true);},forEach:function(arr,_151,_152){if(!arr||!arr.length){return;}var _p=_143(arr,_152,_151);arr=_p[0];for(var i=0,l=_p[0].length;i<l;i++){_p[2].call(_p[1],arr[i],i,arr);}},_everyOrSome:function(_156,arr,_158,_159){var _p=_143(arr,_159,_158);arr=_p[0];for(var i=0,l=arr.length;i<l;i++){var _15d=!!_p[2].call(_p[1],arr[i],i,arr);if(_156^_15d){return _15d;}}return _156;},every:function(arr,_15f,_160){return this._everyOrSome(true,arr,_15f,_160);},some:function(arr,_162,_163){return this._everyOrSome(false,arr,_162,_163);},map:function(arr,_165,_166){var _p=_143(arr,_166,_165);arr=_p[0];var _168=(arguments[3]?(new arguments[3]()):[]);for(var i=0;i<arr.length;++i){_168.push(_p[2].call(_p[1],arr[i],i,arr));}return _168;},filter:function(arr,_16b,_16c){var _p=_143(arr,_16c,_16b);arr=_p[0];var _16e=[];for(var i=0;i<arr.length;i++){if(_p[2].call(_p[1],arr[i],i,arr)){_16e.push(arr[i]);}}return _16e;}});})();}if(!dojo._hasResource["dojo._base.Color"]){dojo._hasResource["dojo._base.Color"]=true;dojo.provide("dojo._base.Color");dojo.Color=function(_170){if(_170){this.setColor(_170);}};dojo.Color.named={black:[0,0,0],silver:[192,192,192],gray:[128,128,128],white:[255,255,255],maroon:[128,0,0],red:[255,0,0],purple:[128,0,128],fuchsia:[255,0,255],green:[0,128,0],lime:[0,255,0],olive:[128,128,0],yellow:[255,255,0],navy:[0,0,128],blue:[0,0,255],teal:[0,128,128],aqua:[0,255,255]};dojo.extend(dojo.Color,{r:255,g:255,b:255,a:1,_set:function(r,g,b,a){var t=this;t.r=r;t.g=g;t.b=b;t.a=a;},setColor:function(_176){var d=dojo;if(d.isString(_176)){d.colorFromString(_176,this);}else{if(d.isArray(_176)){d.colorFromArray(_176,this);}else{this._set(_176.r,_176.g,_176.b,_176.a);if(!(_176 instanceof d.Color)){this.sanitize();}}}return this;},sanitize:function(){return this;},toRgb:function(){var t=this;return [t.r,t.g,t.b];},toRgba:function(){var t=this;return [t.r,t.g,t.b,t.a];},toHex:function(){var arr=dojo.map(["r","g","b"],function(x){var s=this[x].toString(16);return s.length<2?"0"+s:s;},this);return "#"+arr.join("");},toCss:function(_17d){var t=this,rgb=t.r+", "+t.g+", "+t.b;return (_17d?"rgba("+rgb+", "+t.a:"rgb("+rgb)+")";},toString:function(){return this.toCss(true);}});dojo.blendColors=function(_180,end,_182,obj){var d=dojo,t=obj||new dojo.Color();d.forEach(["r","g","b","a"],function(x){t[x]=_180[x]+(end[x]-_180[x])*_182;if(x!="a"){t[x]=Math.round(t[x]);}});return t.sanitize();};dojo.colorFromRgb=function(_187,obj){var m=_187.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);return m&&dojo.colorFromArray(m[1].split(/\s*,\s*/),obj);};dojo.colorFromHex=function(_18a,obj){var d=dojo,t=obj||new d.Color(),bits=(_18a.length==4)?4:8,mask=(1<<bits)-1;_18a=Number("0x"+_18a.substr(1));if(isNaN(_18a)){return null;}d.forEach(["b","g","r"],function(x){var c=_18a&mask;_18a>>=bits;t[x]=bits==4?17*c:c;});t.a=1;return t;};dojo.colorFromArray=function(a,obj){var t=obj||new dojo.Color();t._set(Number(a[0]),Number(a[1]),Number(a[2]),Number(a[3]));if(isNaN(t.a)){t.a=1;}return t.sanitize();};dojo.colorFromString=function(str,obj){var a=dojo.Color.named[str];return a&&dojo.colorFromArray(a,obj)||dojo.colorFromRgb(str,obj)||dojo.colorFromHex(str,obj);};}if(!dojo._hasResource["dojo._base"]){dojo._hasResource["dojo._base"]=true;dojo.provide("dojo._base");}if(!dojo._hasResource["dojo._base.window"]){dojo._hasResource["dojo._base.window"]=true;dojo.provide("dojo._base.window");dojo._gearsObject=function(){var _198;var _199;var _19a=dojo.getObject("google.gears");if(_19a){return _19a;}if(typeof GearsFactory!="undefined"){_198=new GearsFactory();}else{if(dojo.isIE){try{_198=new ActiveXObject("Gears.Factory");}catch(e){}}else{if(navigator.mimeTypes["application/x-googlegears"]){_198=document.createElement("object");_198.setAttribute("type","application/x-googlegears");_198.setAttribute("width",0);_198.setAttribute("height",0);_198.style.display="none";document.documentElement.appendChild(_198);}}}if(!_198){return null;}dojo.setObject("google.gears.factory",_198);return dojo.getObject("google.gears");};dojo.isGears=(!!dojo._gearsObject())||0;dojo.doc=window["document"]||null;dojo.body=function(){return dojo.doc.body||dojo.doc.getElementsByTagName("body")[0];};dojo.setContext=function(_19b,_19c){dojo.global=_19b;dojo.doc=_19c;};dojo._fireCallback=function(_19d,_19e,_19f){if(_19e&&dojo.isString(_19d)){_19d=_19e[_19d];}return _19d.apply(_19e,_19f||[]);};dojo.withGlobal=function(_1a0,_1a1,_1a2,_1a3){var rval;var _1a5=dojo.global;var _1a6=dojo.doc;try{dojo.setContext(_1a0,_1a0.document);rval=dojo._fireCallback(_1a1,_1a2,_1a3);}finally{dojo.setContext(_1a5,_1a6);}return rval;};dojo.withDoc=function(_1a7,_1a8,_1a9,_1aa){var rval;var _1ac=dojo.doc;try{dojo.doc=_1a7;rval=dojo._fireCallback(_1a8,_1a9,_1aa);}finally{dojo.doc=_1ac;}return rval;};}if(!dojo._hasResource["dojo._base.event"]){dojo._hasResource["dojo._base.event"]=true;dojo.provide("dojo._base.event");(function(){var del=(dojo._event_listener={add:function(node,name,fp){if(!node){return;}name=del._normalizeEventName(name);fp=del._fixCallback(name,fp);var _1b1=name;if(!dojo.isIE&&(name=="mouseenter"||name=="mouseleave")){var ofp=fp;name=(name=="mouseenter")?"mouseover":"mouseout";fp=function(e){if(!dojo.isDescendant(e.relatedTarget,node)){return ofp.call(this,e);}};}node.addEventListener(name,fp,false);return fp;},remove:function(node,_1b5,_1b6){if(node){node.removeEventListener(del._normalizeEventName(_1b5),_1b6,false);}},_normalizeEventName:function(name){return name.slice(0,2)=="on"?name.slice(2):name;},_fixCallback:function(name,fp){return name!="keypress"?fp:function(e){return fp.call(this,del._fixEvent(e,this));};},_fixEvent:function(evt,_1bc){switch(evt.type){case "keypress":del._setKeyChar(evt);break;}return evt;},_setKeyChar:function(evt){evt.keyChar=evt.charCode?String.fromCharCode(evt.charCode):"";}});dojo.fixEvent=function(evt,_1bf){return del._fixEvent(evt,_1bf);};dojo.stopEvent=function(evt){evt.preventDefault();evt.stopPropagation();};var _1c1=dojo._listener;dojo._connect=function(obj,_1c3,_1c4,_1c5,_1c6){var _1c7=obj&&(obj.nodeType||obj.attachEvent||obj.addEventListener);var lid=!_1c7?0:(!_1c6?1:2),l=[dojo._listener,del,_1c1][lid];var h=l.add(obj,_1c3,dojo.hitch(_1c4,_1c5));return [obj,_1c3,h,lid];};dojo._disconnect=function(obj,_1cc,_1cd,_1ce){([dojo._listener,del,_1c1][_1ce]).remove(obj,_1cc,_1cd);};dojo.keys={BACKSPACE:8,TAB:9,CLEAR:12,ENTER:13,SHIFT:16,CTRL:17,ALT:18,PAUSE:19,CAPS_LOCK:20,ESCAPE:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT_ARROW:37,UP_ARROW:38,RIGHT_ARROW:39,DOWN_ARROW:40,INSERT:45,DELETE:46,HELP:47,LEFT_WINDOW:91,RIGHT_WINDOW:92,SELECT:93,NUMPAD_0:96,NUMPAD_1:97,NUMPAD_2:98,NUMPAD_3:99,NUMPAD_4:100,NUMPAD_5:101,NUMPAD_6:102,NUMPAD_7:103,NUMPAD_8:104,NUMPAD_9:105,NUMPAD_MULTIPLY:106,NUMPAD_PLUS:107,NUMPAD_ENTER:108,NUMPAD_MINUS:109,NUMPAD_PERIOD:110,NUMPAD_DIVIDE:111,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,F13:124,F14:125,F15:126,NUM_LOCK:144,SCROLL_LOCK:145};if(dojo.isIE){var _1cf=function(e,code){try{return (e.keyCode=code);}catch(e){return 0;}};var iel=dojo._listener;if(!dojo.config._allow_leaks){_1c1=iel=dojo._ie_listener={handlers:[],add:function(_1d3,_1d4,_1d5){_1d3=_1d3||dojo.global;var f=_1d3[_1d4];if(!f||!f._listeners){var d=dojo._getIeDispatcher();d.target=f&&(ieh.push(f)-1);d._listeners=[];f=_1d3[_1d4]=d;}return f._listeners.push(ieh.push(_1d5)-1);},remove:function(_1d9,_1da,_1db){var f=(_1d9||dojo.global)[_1da],l=f&&f._listeners;if(f&&l&&_1db--){delete ieh[l[_1db]];delete l[_1db];}}};var ieh=iel.handlers;}dojo.mixin(del,{add:function(node,_1df,fp){if(!node){return;}_1df=del._normalizeEventName(_1df);if(_1df=="onkeypress"){var kd=node.onkeydown;if(!kd||!kd._listeners||!kd._stealthKeydownHandle){var h=del.add(node,"onkeydown",del._stealthKeyDown);kd=node.onkeydown;kd._stealthKeydownHandle=h;kd._stealthKeydownRefs=1;}else{kd._stealthKeydownRefs++;}}return iel.add(node,_1df,del._fixCallback(fp));},remove:function(node,_1e4,_1e5){_1e4=del._normalizeEventName(_1e4);iel.remove(node,_1e4,_1e5);if(_1e4=="onkeypress"){var kd=node.onkeydown;if(--kd._stealthKeydownRefs<=0){iel.remove(node,"onkeydown",kd._stealthKeydownHandle);delete kd._stealthKeydownHandle;}}},_normalizeEventName:function(_1e7){return _1e7.slice(0,2)!="on"?"on"+_1e7:_1e7;},_nop:function(){},_fixEvent:function(evt,_1e9){if(!evt){var w=_1e9&&(_1e9.ownerDocument||_1e9.document||_1e9).parentWindow||window;evt=w.event;}if(!evt){return (evt);}evt.target=evt.srcElement;evt.currentTarget=(_1e9||evt.srcElement);evt.layerX=evt.offsetX;evt.layerY=evt.offsetY;var se=evt.srcElement,doc=(se&&se.ownerDocument)||document;var _1ed=((dojo.isIE<6)||(doc["compatMode"]=="BackCompat"))?doc.body:doc.documentElement;var _1ee=dojo._getIeDocumentElementOffset();evt.pageX=evt.clientX+dojo._fixIeBiDiScrollLeft(_1ed.scrollLeft||0)-_1ee.x;evt.pageY=evt.clientY+(_1ed.scrollTop||0)-_1ee.y;if(evt.type=="mouseover"){evt.relatedTarget=evt.fromElement;}if(evt.type=="mouseout"){evt.relatedTarget=evt.toElement;}evt.stopPropagation=del._stopPropagation;evt.preventDefault=del._preventDefault;return del._fixKeys(evt);},_fixKeys:function(evt){switch(evt.type){case "keypress":var c=("charCode" in evt?evt.charCode:evt.keyCode);if(c==10){c=0;evt.keyCode=13;}else{if(c==13||c==27){c=0;}else{if(c==3){c=99;}}}evt.charCode=c;del._setKeyChar(evt);break;}return evt;},_punctMap:{106:42,111:47,186:59,187:43,188:44,189:45,190:46,191:47,192:96,219:91,220:92,221:93,222:39},_stealthKeyDown:function(evt){var kp=evt.currentTarget.onkeypress;if(!kp||!kp._listeners){return;}var k=evt.keyCode;var _1f4=(k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);if(_1f4||evt.ctrlKey){var c=_1f4?0:k;if(evt.ctrlKey){if(k==3||k==13){return;}else{if(c>95&&c<106){c-=48;}else{if((!evt.shiftKey)&&(c>=65&&c<=90)){c+=32;}else{c=del._punctMap[c]||c;}}}}var faux=del._synthesizeEvent(evt,{type:"keypress",faux:true,charCode:c});kp.call(evt.currentTarget,faux);evt.cancelBubble=faux.cancelBubble;evt.returnValue=faux.returnValue;_1cf(evt,faux.keyCode);}},_stopPropagation:function(){this.cancelBubble=true;},_preventDefault:function(){this.bubbledKeyCode=this.keyCode;if(this.ctrlKey){_1cf(this,0);}this.returnValue=false;}});dojo.stopEvent=function(evt){evt=evt||window.event;del._stopPropagation.call(evt);del._preventDefault.call(evt);};}del._synthesizeEvent=function(evt,_1f9){var faux=dojo.mixin({},evt,_1f9);del._setKeyChar(faux);faux.preventDefault=function(){evt.preventDefault();};faux.stopPropagation=function(){evt.stopPropagation();};return faux;};if(dojo.isOpera){dojo.mixin(del,{_fixEvent:function(evt,_1fc){switch(evt.type){case "keypress":var c=evt.which;if(c==3){c=99;}c=((c<41)&&(!evt.shiftKey)?0:c);if((evt.ctrlKey)&&(!evt.shiftKey)&&(c>=65)&&(c<=90)){c+=32;}return del._synthesizeEvent(evt,{charCode:c});}return evt;}});}if(dojo.isSafari){dojo.mixin(del,{_fixEvent:function(evt,_1ff){switch(evt.type){case "keypress":var c=evt.charCode,s=evt.shiftKey,k=evt.keyCode;k=k||_203[evt.keyIdentifier]||0;if(evt.keyIdentifier=="Enter"){c=0;}else{if((evt.ctrlKey)&&(c>0)&&(c<27)){c+=96;}else{if(c==dojo.keys.SHIFT_TAB){c=dojo.keys.TAB;s=true;}else{c=(c>=32&&c<63232?c:0);}}}return del._synthesizeEvent(evt,{charCode:c,shiftKey:s,keyCode:k});}return evt;}});dojo.mixin(dojo.keys,{SHIFT_TAB:25,UP_ARROW:63232,DOWN_ARROW:63233,LEFT_ARROW:63234,RIGHT_ARROW:63235,F1:63236,F2:63237,F3:63238,F4:63239,F5:63240,F6:63241,F7:63242,F8:63243,F9:63244,F10:63245,F11:63246,F12:63247,PAUSE:63250,DELETE:63272,HOME:63273,END:63275,PAGE_UP:63276,PAGE_DOWN:63277,INSERT:63302,PRINT_SCREEN:63248,SCROLL_LOCK:63249,NUM_LOCK:63289});var dk=dojo.keys,_203={"Up":dk.UP_ARROW,"Down":dk.DOWN_ARROW,"Left":dk.LEFT_ARROW,"Right":dk.RIGHT_ARROW,"PageUp":dk.PAGE_UP,"PageDown":dk.PAGE_DOWN};}})();if(dojo.isIE){dojo._ieDispatcher=function(args,_206){var ap=Array.prototype,h=dojo._ie_listener.handlers,c=args.callee,ls=c._listeners,t=h[c.target];var r=t&&t.apply(_206,args);for(var i in ls){if(!(i in ap)){h[ls[i]].apply(_206,args);}}return r;};dojo._getIeDispatcher=function(){return new Function(dojo._scopeName+"._ieDispatcher(arguments, this)");};dojo._event_listener._fixCallback=function(fp){var f=dojo._event_listener._fixEvent;return function(e){return fp.call(this,f(e,this));};};}}if(!dojo._hasResource["dojo._base.html"]){dojo._hasResource["dojo._base.html"]=true;dojo.provide("dojo._base.html");try{document.execCommand("BackgroundImageCache",false,true);}catch(e){}if(dojo.isIE||dojo.isOpera){dojo.byId=function(id,doc){if(dojo.isString(id)){var _d=doc||dojo.doc;var te=_d.getElementById(id);if(te&&te.attributes.id.value==id){return te;}else{var eles=_d.all[id];if(!eles||!eles.length){return eles;}var i=0;while((te=eles[i++])){if(te.attributes.id.value==id){return te;}}}}else{return id;}};}else{dojo.byId=function(id,doc){return dojo.isString(id)?(doc||dojo.doc).getElementById(id):id;};}(function(){var d=dojo;var _21a=null;dojo.addOnUnload(function(){_21a=null;});dojo._destroyElement=function(node){node=d.byId(node);try{if(!_21a){_21a=document.createElement("div");}_21a.appendChild(node.parentNode?node.parentNode.removeChild(node):node);_21a.innerHTML="";}catch(e){}};dojo.isDescendant=function(node,_21d){try{node=d.byId(node);_21d=d.byId(_21d);while(node){if(node===_21d){return true;}node=node.parentNode;}}catch(e){}return false;};dojo.setSelectable=function(node,_21f){node=d.byId(node);if(d.isMozilla){node.style.MozUserSelect=_21f?"":"none";}else{if(d.isKhtml){node.style.KhtmlUserSelect=_21f?"auto":"none";}else{if(d.isIE){node.unselectable=_21f?"":"on";d.query("*",node).forEach(function(_220){_220.unselectable=_21f?"":"on";});}}}};var _221=function(node,ref){ref.parentNode.insertBefore(node,ref);return true;};var _224=function(node,ref){var pn=ref.parentNode;if(ref==pn.lastChild){pn.appendChild(node);}else{return _221(node,ref.nextSibling);}return true;};dojo.place=function(node,_229,_22a){if(!node||!_229||_22a===undefined){return false;}node=d.byId(node);_229=d.byId(_229);if(typeof _22a=="number"){var cn=_229.childNodes;if((_22a==0&&cn.length==0)||cn.length==_22a){_229.appendChild(node);return true;}if(_22a==0){return _221(node,_229.firstChild);}return _224(node,cn[_22a-1]);}switch(_22a.toLowerCase()){case "before":return _221(node,_229);case "after":return _224(node,_229);case "first":if(_229.firstChild){return _221(node,_229.firstChild);}default:_229.appendChild(node);return true;}};dojo.boxModel="content-box";if(d.isIE){var _dcm=document.compatMode;d.boxModel=_dcm=="BackCompat"||_dcm=="QuirksMode"||d.isIE<6?"border-box":"content-box";}var gcs,dv=document.defaultView;if(d.isSafari){gcs=function(node){var s=dv.getComputedStyle(node,null);if(!s&&node.style){node.style.display="";s=dv.getComputedStyle(node,null);}return s||{};};}else{if(d.isIE){gcs=function(node){return node.currentStyle;};}else{gcs=function(node){return dv.getComputedStyle(node,null);};}}dojo.getComputedStyle=gcs;if(!d.isIE){dojo._toPixelValue=function(_233,_234){return parseFloat(_234)||0;};}else{dojo._toPixelValue=function(_235,_236){if(!_236){return 0;}if(_236=="medium"){return 4;}if(_236.slice&&(_236.slice(-2)=="px")){return parseFloat(_236);}with(_235){var _237=style.left;var _238=runtimeStyle.left;runtimeStyle.left=currentStyle.left;try{style.left=_236;_236=style.pixelLeft;}catch(e){_236=0;}style.left=_237;runtimeStyle.left=_238;}return _236;};}var px=d._toPixelValue;dojo._getOpacity=d.isIE?function(node){try{return node.filters.alpha.opacity/100;}catch(e){return 1;}}:function(node){return gcs(node).opacity;};dojo._setOpacity=d.isIE?function(node,_23d){if(_23d==1){var _23e=/FILTER:[^;]*;?/i;node.style.cssText=node.style.cssText.replace(_23e,"");if(node.nodeName.toLowerCase()=="tr"){d.query("> td",node).forEach(function(i){i.style.cssText=i.style.cssText.replace(_23e,"");});}}else{var o="Alpha(Opacity="+_23d*100+")";node.style.filter=o;}if(node.nodeName.toLowerCase()=="tr"){d.query("> td",node).forEach(function(i){i.style.filter=o;});}return _23d;}:function(node,_243){return node.style.opacity=_243;};var _244={left:true,top:true};var _245=/margin|padding|width|height|max|min|offset/;var _246=function(node,type,_249){type=type.toLowerCase();if(d.isIE&&_249=="auto"){if(type=="height"){return node.offsetHeight;}if(type=="width"){return node.offsetWidth;}}if(!(type in _244)){_244[type]=_245.test(type);}return _244[type]?px(node,_249):_249;};var _24a=d.isIE?"styleFloat":"cssFloat";var _24b={"cssFloat":_24a,"styleFloat":_24a,"float":_24a};dojo.style=function(node,_24d,_24e){var n=d.byId(node),args=arguments.length,op=(_24d=="opacity");_24d=_24b[_24d]||_24d;if(args==3){return op?d._setOpacity(n,_24e):n.style[_24d]=_24e;}if(args==2&&op){return d._getOpacity(n);}var s=gcs(n);if(args==2&&!d.isString(_24d)){for(var x in _24d){d.style(node,x,_24d[x]);}return s;}return (args==1)?s:_246(n,_24d,s[_24d]);};dojo._getPadExtents=function(n,_255){var s=_255||gcs(n),l=px(n,s.paddingLeft),t=px(n,s.paddingTop);return {l:l,t:t,w:l+px(n,s.paddingRight),h:t+px(n,s.paddingBottom)};};dojo._getBorderExtents=function(n,_25a){var ne="none",s=_25a||gcs(n),bl=(s.borderLeftStyle!=ne?px(n,s.borderLeftWidth):0),bt=(s.borderTopStyle!=ne?px(n,s.borderTopWidth):0);return {l:bl,t:bt,w:bl+(s.borderRightStyle!=ne?px(n,s.borderRightWidth):0),h:bt+(s.borderBottomStyle!=ne?px(n,s.borderBottomWidth):0)};};dojo._getPadBorderExtents=function(n,_260){var s=_260||gcs(n),p=d._getPadExtents(n,s),b=d._getBorderExtents(n,s);return {l:p.l+b.l,t:p.t+b.t,w:p.w+b.w,h:p.h+b.h};};dojo._getMarginExtents=function(n,_265){var s=_265||gcs(n),l=px(n,s.marginLeft),t=px(n,s.marginTop),r=px(n,s.marginRight),b=px(n,s.marginBottom);if(d.isSafari&&(s.position!="absolute")){r=l;}return {l:l,t:t,w:l+r,h:t+b};};dojo._getMarginBox=function(node,_26c){var s=_26c||gcs(node),me=d._getMarginExtents(node,s);var l=node.offsetLeft-me.l,t=node.offsetTop-me.t;if(d.isMoz){var sl=parseFloat(s.left),st=parseFloat(s.top);if(!isNaN(sl)&&!isNaN(st)){l=sl,t=st;}else{var p=node.parentNode;if(p&&p.style){var pcs=gcs(p);if(pcs.overflow!="visible"){var be=d._getBorderExtents(p,pcs);l+=be.l,t+=be.t;}}}}else{if(d.isOpera){var p=node.parentNode;if(p){var be=d._getBorderExtents(p);l-=be.l,t-=be.t;}}}return {l:l,t:t,w:node.offsetWidth+me.w,h:node.offsetHeight+me.h};};dojo._getContentBox=function(node,_277){var s=_277||gcs(node),pe=d._getPadExtents(node,s),be=d._getBorderExtents(node,s),w=node.clientWidth,h;if(!w){w=node.offsetWidth,h=node.offsetHeight;}else{h=node.clientHeight,be.w=be.h=0;}if(d.isOpera){pe.l+=be.l;pe.t+=be.t;}return {l:pe.l,t:pe.t,w:w-pe.w-be.w,h:h-pe.h-be.h};};dojo._getBorderBox=function(node,_27e){var s=_27e||gcs(node),pe=d._getPadExtents(node,s),cb=d._getContentBox(node,s);return {l:cb.l-pe.l,t:cb.t-pe.t,w:cb.w+pe.w,h:cb.h+pe.h};};dojo._setBox=function(node,l,t,w,h,u){u=u||"px";var s=node.style;if(!isNaN(l)){s.left=l+u;}if(!isNaN(t)){s.top=t+u;}if(w>=0){s.width=w+u;}if(h>=0){s.height=h+u;}};dojo._usesBorderBox=function(node){var n=node.tagName;return d.boxModel=="border-box"||n=="TABLE"||n=="BUTTON";};dojo._setContentSize=function(node,_28c,_28d,_28e){if(d._usesBorderBox(node)){var pb=d._getPadBorderExtents(node,_28e);if(_28c>=0){_28c+=pb.w;}if(_28d>=0){_28d+=pb.h;}}d._setBox(node,NaN,NaN,_28c,_28d);};dojo._setMarginBox=function(node,_291,_292,_293,_294,_295){var s=_295||gcs(node);var bb=d._usesBorderBox(node),pb=bb?_299:d._getPadBorderExtents(node,s),mb=d._getMarginExtents(node,s);if(_293>=0){_293=Math.max(_293-pb.w-mb.w,0);}if(_294>=0){_294=Math.max(_294-pb.h-mb.h,0);}d._setBox(node,_291,_292,_293,_294);};var _299={l:0,t:0,w:0,h:0};dojo.marginBox=function(node,box){var n=d.byId(node),s=gcs(n),b=box;return !b?d._getMarginBox(n,s):d._setMarginBox(n,b.l,b.t,b.w,b.h,s);};dojo.contentBox=function(node,box){var n=dojo.byId(node),s=gcs(n),b=box;return !b?d._getContentBox(n,s):d._setContentSize(n,b.w,b.h,s);};var _2a5=function(node,prop){if(!(node=(node||0).parentNode)){return 0;}var val,_2a9=0,_b=d.body();while(node&&node.style){if(gcs(node).position=="fixed"){return 0;}val=node[prop];if(val){_2a9+=val-0;if(node==_b){break;}}node=node.parentNode;}return _2a9;};dojo._docScroll=function(){var _b=d.body(),_w=d.global,de=d.doc.documentElement;return {y:(_w.pageYOffset||de.scrollTop||_b.scrollTop||0),x:(_w.pageXOffset||d._fixIeBiDiScrollLeft(de.scrollLeft)||_b.scrollLeft||0)};};dojo._isBodyLtr=function(){return !("_bodyLtr" in d)?d._bodyLtr=gcs(d.body()).direction=="ltr":d._bodyLtr;};dojo._getIeDocumentElementOffset=function(){var de=d.doc.documentElement;return (d.isIE>=7)?{x:de.getBoundingClientRect().left,y:de.getBoundingClientRect().top}:{x:d._isBodyLtr()||window.parent==window?de.clientLeft:de.offsetWidth-de.clientWidth-de.clientLeft,y:de.clientTop};};dojo._fixIeBiDiScrollLeft=function(_2af){var dd=d.doc;if(d.isIE&&!dojo._isBodyLtr()){var de=dd.compatMode=="BackCompat"?dd.body:dd.documentElement;return _2af+de.clientWidth-de.scrollWidth;}return _2af;};dojo._abs=function(node,_2b3){var _2b4=node.ownerDocument;var ret={x:0,y:0};var db=d.body();if(d.isIE||(d.isFF>=3)){var _2b7=node.getBoundingClientRect();var _2b8=(d.isIE)?d._getIeDocumentElementOffset():{x:0,y:0};ret.x=_2b7.left-_2b8.x;ret.y=_2b7.top-_2b8.y;}else{if(_2b4["getBoxObjectFor"]){var bo=_2b4.getBoxObjectFor(node),b=d._getBorderExtents(node);ret.x=bo.x-b.l-_2a5(node,"scrollLeft");ret.y=bo.y-b.t-_2a5(node,"scrollTop");}else{if(node["offsetParent"]){var _2bb;if(d.isSafari&&(gcs(node).position=="absolute")&&(node.parentNode==db)){_2bb=db;}else{_2bb=db.parentNode;}if(node.parentNode!=db){var nd=node;if(d.isOpera){nd=db;}ret.x-=_2a5(nd,"scrollLeft");ret.y-=_2a5(nd,"scrollTop");}var _2bd=node;do{var n=_2bd.offsetLeft;if(!d.isOpera||n>0){ret.x+=isNaN(n)?0:n;}var t=_2bd.offsetTop;ret.y+=isNaN(t)?0:t;if(d.isSafari&&_2bd!=node){var cs=gcs(_2bd);ret.x+=px(_2bd,cs.borderLeftWidth);ret.y+=px(_2bd,cs.borderTopWidth);}_2bd=_2bd.offsetParent;}while((_2bd!=_2bb)&&_2bd);}else{if(node.x&&node.y){ret.x+=isNaN(node.x)?0:node.x;ret.y+=isNaN(node.y)?0:node.y;}}}}if(_2b3){var _2c1=d._docScroll();ret.y+=_2c1.y;ret.x+=_2c1.x;}return ret;};dojo.coords=function(node,_2c3){var n=d.byId(node),s=gcs(n),mb=d._getMarginBox(n,s);var abs=d._abs(n,_2c3);mb.x=abs.x;mb.y=abs.y;return mb;};var _2c8=function(name){switch(name.toLowerCase()){case "tabindex":return (d.isIE&&d.isIE<8)?"tabIndex":"tabindex";default:return name;}};var _2ca={colspan:"colSpan",enctype:"enctype",frameborder:"frameborder",method:"method",rowspan:"rowSpan",scrolling:"scrolling",shape:"shape",span:"span",type:"type",valuetype:"valueType"};dojo.hasAttr=function(node,name){var attr=d.byId(node).getAttributeNode(_2c8(name));return attr?attr.specified:false;};var _2ce={};var _ctr=0;var _2d0=dojo._scopeName+"attrid";dojo.attr=function(node,name,_2d3){var args=arguments.length;if(args==2&&!d.isString(name)){for(var x in name){d.attr(node,x,name[x]);}return;}node=d.byId(node);name=_2c8(name);if(args==3){if(d.isFunction(_2d3)){var _2d6=d.attr(node,_2d0);if(!_2d6){_2d6=_ctr++;d.attr(node,_2d0,_2d6);}if(!_2ce[_2d6]){_2ce[_2d6]={};}var h=_2ce[_2d6][name];if(h){d.disconnect(h);}else{try{delete node[name];}catch(e){}}_2ce[_2d6][name]=d.connect(node,name,_2d3);}else{if(typeof _2d3=="boolean"){node[name]=_2d3;}else{node.setAttribute(name,_2d3);}}return;}else{var prop=_2ca[name.toLowerCase()];if(prop){return node[prop];}else{var _2d3=node[name];return (typeof _2d3=="boolean"||typeof _2d3=="function")?_2d3:(d.hasAttr(node,name)?node.getAttribute(name):null);}}};dojo.removeAttr=function(node,name){d.byId(node).removeAttribute(_2c8(name));};})();dojo.hasClass=function(node,_2dc){return ((" "+dojo.byId(node).className+" ").indexOf(" "+_2dc+" ")>=0);};dojo.addClass=function(node,_2de){node=dojo.byId(node);var cls=node.className;if((" "+cls+" ").indexOf(" "+_2de+" ")<0){node.className=cls+(cls?" ":"")+_2de;}};dojo.removeClass=function(node,_2e1){node=dojo.byId(node);var t=dojo.trim((" "+node.className+" ").replace(" "+_2e1+" "," "));if(node.className!=t){node.className=t;}};dojo.toggleClass=function(node,_2e4,_2e5){if(_2e5===undefined){_2e5=!dojo.hasClass(node,_2e4);}dojo[_2e5?"addClass":"removeClass"](node,_2e4);};}if(!dojo._hasResource["dojo._base.NodeList"]){dojo._hasResource["dojo._base.NodeList"]=true;dojo.provide("dojo._base.NodeList");(function(){var d=dojo;var tnl=function(arr){arr.constructor=dojo.NodeList;dojo._mixin(arr,dojo.NodeList.prototype);return arr;};var _2e9=function(func,_2eb){return function(){var _a=arguments;var aa=d._toArray(_a,0,[null]);var s=this.map(function(i){aa[0]=i;return d[func].apply(d,aa);});return (_2eb||((_a.length>1)||!d.isString(_a[0])))?this:s;};};dojo.NodeList=function(){return tnl(Array.apply(null,arguments));};dojo.NodeList._wrap=tnl;dojo.extend(dojo.NodeList,{slice:function(){var a=dojo._toArray(arguments);return tnl(a.slice.apply(this,a));},splice:function(){var a=dojo._toArray(arguments);return tnl(a.splice.apply(this,a));},concat:function(){var a=dojo._toArray(arguments,0,[this]);return tnl(a.concat.apply([],a));},indexOf:function(_2f3,_2f4){return d.indexOf(this,_2f3,_2f4);},lastIndexOf:function(){return d.lastIndexOf.apply(d,d._toArray(arguments,0,[this]));},every:function(_2f5,_2f6){return d.every(this,_2f5,_2f6);},some:function(_2f7,_2f8){return d.some(this,_2f7,_2f8);},map:function(func,obj){return d.map(this,func,obj,d.NodeList);},forEach:function(_2fb,_2fc){d.forEach(this,_2fb,_2fc);return this;},coords:function(){return d.map(this,d.coords);},attr:_2e9("attr"),style:_2e9("style"),addClass:_2e9("addClass",true),removeClass:_2e9("removeClass",true),toggleClass:_2e9("toggleClass",true),connect:_2e9("connect",true),place:function(_2fd,_2fe){var item=d.query(_2fd)[0];return this.forEach(function(i){d.place(i,item,(_2fe||"last"));});},orphan:function(_301){var _302=_301?d._filterQueryResult(this,_301):this;_302.forEach(function(item){if(item.parentNode){item.parentNode.removeChild(item);}});return _302;},adopt:function(_304,_305){var item=this[0];return d.query(_304).forEach(function(ai){d.place(ai,item,_305||"last");});},query:function(_308){if(!_308){return this;}var ret=d.NodeList();this.forEach(function(item){d.query(_308,item).forEach(function(_30b){if(_30b!==undefined){ret.push(_30b);}});});return ret;},filter:function(_30c){var _30d=this;var _a=arguments;var r=d.NodeList();var rp=function(t){if(t!==undefined){r.push(t);}};if(d.isString(_30c)){_30d=d._filterQueryResult(this,_a[0]);if(_a.length==1){return _30d;}_a.shift();}d.forEach(d.filter(_30d,_a[0],_a[1]),rp);return r;},addContent:function(_312,_313){var ta=d.doc.createElement("span");if(d.isString(_312)){ta.innerHTML=_312;}else{ta.appendChild(_312);}if(_313===undefined){_313="last";}var ct=(_313=="first"||_313=="after")?"lastChild":"firstChild";this.forEach(function(item){var tn=ta.cloneNode(true);while(tn[ct]){d.place(tn[ct],item,_313);}});return this;},empty:function(){return this.forEach("item.innerHTML='';");},instantiate:function(_318,_319){var c=d.isFunction(_318)?_318:d.getObject(_318);return this.forEach(function(i){new c(_319||{},i);});}});d.forEach(["blur","focus","click","keydown","keypress","keyup","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup"],function(evt){var _oe="on"+evt;dojo.NodeList.prototype[_oe]=function(a,b){return this.connect(_oe,a,b);};});})();}if(!dojo._hasResource["dojo._base.query"]){dojo._hasResource["dojo._base.query"]=true;dojo.provide("dojo._base.query");(function(){var d=dojo;var _321=dojo.isIE?"children":"childNodes";var _322=false;var _323=function(_324){if(">~+".indexOf(_324.charAt(_324.length-1))>=0){_324+=" *";}_324+=" ";var ts=function(s,e){return d.trim(_324.slice(s,e));};var _328=[];var _329=-1;var _32a=-1;var _32b=-1;var _32c=-1;var _32d=-1;var inId=-1;var _32f=-1;var lc="";var cc="";var _332;var x=0;var ql=_324.length;var _335=null;var _cp=null;var _337=function(){if(_32f>=0){var tv=(_32f==x)?null:ts(_32f,x).toLowerCase();_335[(">~+".indexOf(tv)<0)?"tag":"oper"]=tv;_32f=-1;}};var _339=function(){if(inId>=0){_335.id=ts(inId,x).replace(/\\/g,"");inId=-1;}};var _33a=function(){if(_32d>=0){_335.classes.push(ts(_32d+1,x).replace(/\\/g,""));_32d=-1;}};var _33b=function(){_339();_337();_33a();};for(;lc=cc,cc=_324.charAt(x),x<ql;x++){if(lc=="\\"){continue;}if(!_335){_332=x;_335={query:null,pseudos:[],attrs:[],classes:[],tag:null,oper:null,id:null};_32f=x;}if(_329>=0){if(cc=="]"){if(!_cp.attr){_cp.attr=ts(_329+1,x);}else{_cp.matchFor=ts((_32b||_329+1),x);}var cmf=_cp.matchFor;if(cmf){if((cmf.charAt(0)=="\"")||(cmf.charAt(0)=="'")){_cp.matchFor=cmf.substring(1,cmf.length-1);}}_335.attrs.push(_cp);_cp=null;_329=_32b=-1;}else{if(cc=="="){var _33d=("|~^$*".indexOf(lc)>=0)?lc:"";_cp.type=_33d+cc;_cp.attr=ts(_329+1,x-_33d.length);_32b=x+1;}}}else{if(_32a>=0){if(cc==")"){if(_32c>=0){_cp.value=ts(_32a+1,x);}_32c=_32a=-1;}}else{if(cc=="#"){_33b();inId=x+1;}else{if(cc=="."){_33b();_32d=x;}else{if(cc==":"){_33b();_32c=x;}else{if(cc=="["){_33b();_329=x;_cp={};}else{if(cc=="("){if(_32c>=0){_cp={name:ts(_32c+1,x),value:null};_335.pseudos.push(_cp);}_32a=x;}else{if(cc==" "&&lc!=cc){_33b();if(_32c>=0){_335.pseudos.push({name:ts(_32c+1,x)});}_335.hasLoops=(_335.pseudos.length||_335.attrs.length||_335.classes.length);_335.query=ts(_332,x);_335.tag=(_335["oper"])?null:(_335.tag||"*");_328.push(_335);_335=null;}}}}}}}}}return _328;};var _33e={"*=":function(attr,_340){return "[contains(@"+attr+", '"+_340+"')]";},"^=":function(attr,_342){return "[starts-with(@"+attr+", '"+_342+"')]";},"$=":function(attr,_344){return "[substring(@"+attr+", string-length(@"+attr+")-"+(_344.length-1)+")='"+_344+"']";},"~=":function(attr,_346){return "[contains(concat(' ',@"+attr+",' '), ' "+_346+" ')]";},"|=":function(attr,_348){return "[contains(concat(' ',@"+attr+",' '), ' "+_348+"-')]";},"=":function(attr,_34a){return "[@"+attr+"='"+_34a+"']";}};var _34b=function(_34c,_34d,_34e,_34f){d.forEach(_34d.attrs,function(attr){var _351;if(attr.type&&_34c[attr.type]){_351=_34c[attr.type](attr.attr,attr.matchFor);}else{if(attr.attr.length){_351=_34e(attr.attr);}}if(_351){_34f(_351);}});};var _352=function(_353){var _354=".";var _355=_323(d.trim(_353));while(_355.length){var tqp=_355.shift();var _357;var _358="";if(tqp.oper==">"){_357="/";tqp=_355.shift();}else{if(tqp.oper=="~"){_357="/following-sibling::";tqp=_355.shift();}else{if(tqp.oper=="+"){_357="/following-sibling::";_358="[position()=1]";tqp=_355.shift();}else{_357="//";}}}_354+=_357+tqp.tag+_358;if(tqp.id){_354+="[@id='"+tqp.id+"'][1]";}d.forEach(tqp.classes,function(cn){var cnl=cn.length;var _35b=" ";if(cn.charAt(cnl-1)=="*"){_35b="";cn=cn.substr(0,cnl-1);}_354+="[contains(concat(' ',@class,' '), ' "+cn+_35b+"')]";});_34b(_33e,tqp,function(_35c){return "[@"+_35c+"]";},function(_35d){_354+=_35d;});}return _354;};var _35e={};var _35f=function(path){if(_35e[path]){return _35e[path];}var doc=d.doc;var _362=_352(path);var tf=function(_364){var ret=[];var _366;try{_366=doc.evaluate(_362,_364,null,XPathResult.ANY_TYPE,null);}catch(e){console.debug("failure in exprssion:",_362,"under:",_364);console.debug(e);}var _367=_366.iterateNext();while(_367){ret.push(_367);_367=_366.iterateNext();}return ret;};return _35e[path]=tf;};var _368={};var _369={};var _36a=function(_36b,_36c){if(!_36b){return _36c;}if(!_36c){return _36b;}return function(){return _36b.apply(window,arguments)&&_36c.apply(window,arguments);};};var _36d=function(root){var ret=[];var te,x=0,tret=root[_321];while(te=tret[x++]){if(te.nodeType==1){ret.push(te);}}return ret;};var _373=function(root,_375){var ret=[];var te=root;while(te=te.nextSibling){if(te.nodeType==1){ret.push(te);if(_375){break;}}}return ret;};var _378=function(_379,_37a,_37b,idx){var nidx=idx+1;var _37e=(_37a.length==nidx);var tqp=_37a[idx];if(tqp.oper){var ecn=(tqp.oper==">")?_36d(_379):_373(_379,(tqp.oper=="+"));if(!ecn||!ecn.length){return;}nidx++;_37e=(_37a.length==nidx);var tf=_382(_37a[idx+1]);for(var x=0,ecnl=ecn.length,te;x<ecnl,te=ecn[x];x++){if(tf(te)){if(_37e){_37b.push(te);}else{_378(te,_37a,_37b,nidx);}}}}var _386=_387(tqp)(_379);if(_37e){while(_386.length){_37b.push(_386.shift());}}else{while(_386.length){_378(_386.shift(),_37a,_37b,nidx);}}};var _388=function(_389,_38a){var ret=[];var x=_389.length-1,te;while(te=_389[x--]){_378(te,_38a,ret,0);}return ret;};var _382=function(q){if(_368[q.query]){return _368[q.query];}var ff=null;if(q.tag){if(q.tag=="*"){ff=_36a(ff,function(elem){return (elem.nodeType==1);});}else{ff=_36a(ff,function(elem){return ((elem.nodeType==1)&&(q.tag==elem.tagName.toLowerCase()));});}}if(q.id){ff=_36a(ff,function(elem){return ((elem.nodeType==1)&&(elem.id==q.id));});}if(q.hasLoops){ff=_36a(ff,_393(q));}return _368[q.query]=ff;};var _394=function(node){var pn=node.parentNode;var pnc=pn.childNodes;var nidx=-1;var _399=pn.firstChild;if(!_399){return nidx;}var ci=node["__cachedIndex"];var cl=pn["__cachedLength"];if(((typeof cl=="number")&&(cl!=pnc.length))||(typeof ci!="number")){pn["__cachedLength"]=pnc.length;var idx=1;do{if(_399===node){nidx=idx;}if(_399.nodeType==1){_399["__cachedIndex"]=idx;idx++;}_399=_399.nextSibling;}while(_399);}else{nidx=ci;}return nidx;};var _39d=0;var _39e="";var _39f=function(elem,attr){if(attr=="class"){return elem.className||_39e;}if(attr=="for"){return elem.htmlFor||_39e;}return elem.getAttribute(attr,2)||_39e;};var _3a2={"*=":function(attr,_3a4){return function(elem){return (_39f(elem,attr).indexOf(_3a4)>=0);};},"^=":function(attr,_3a7){return function(elem){return (_39f(elem,attr).indexOf(_3a7)==0);};},"$=":function(attr,_3aa){var tval=" "+_3aa;return function(elem){var ea=" "+_39f(elem,attr);return (ea.lastIndexOf(_3aa)==(ea.length-_3aa.length));};},"~=":function(attr,_3af){var tval=" "+_3af+" ";return function(elem){var ea=" "+_39f(elem,attr)+" ";return (ea.indexOf(tval)>=0);};},"|=":function(attr,_3b4){var _3b5=" "+_3b4+"-";return function(elem){var ea=" "+(elem.getAttribute(attr,2)||"");return ((ea==_3b4)||(ea.indexOf(_3b5)==0));};},"=":function(attr,_3b9){return function(elem){return (_39f(elem,attr)==_3b9);};}};var _3bb={"first-child":function(name,_3bd){return function(elem){if(elem.nodeType!=1){return false;}var fc=elem.previousSibling;while(fc&&(fc.nodeType!=1)){fc=fc.previousSibling;}return (!fc);};},"last-child":function(name,_3c1){return function(elem){if(elem.nodeType!=1){return false;}var nc=elem.nextSibling;while(nc&&(nc.nodeType!=1)){nc=nc.nextSibling;}return (!nc);};},"empty":function(name,_3c5){return function(elem){var cn=elem.childNodes;var cnl=elem.childNodes.length;for(var x=cnl-1;x>=0;x--){var nt=cn[x].nodeType;if((nt==1)||(nt==3)){return false;}}return true;};},"contains":function(name,_3cc){return function(elem){return (elem.innerHTML.indexOf(_3cc)>=0);};},"not":function(name,_3cf){var ntf=_382(_323(_3cf)[0]);return function(elem){return (!ntf(elem));};},"nth-child":function(name,_3d3){var pi=parseInt;if(_3d3=="odd"){return function(elem){return (((_394(elem))%2)==1);};}else{if((_3d3=="2n")||(_3d3=="even")){return function(elem){return ((_394(elem)%2)==0);};}else{if(_3d3.indexOf("0n+")==0){var _3d7=pi(_3d3.substr(3));return function(elem){return (elem.parentNode[_321][_3d7-1]===elem);};}else{if((_3d3.indexOf("n+")>0)&&(_3d3.length>3)){var _3d9=_3d3.split("n+",2);var pred=pi(_3d9[0]);var idx=pi(_3d9[1]);return function(elem){return ((_394(elem)%pred)==idx);};}else{if(_3d3.indexOf("n")==-1){var _3d7=pi(_3d3);return function(elem){return (_394(elem)==_3d7);};}}}}}}};var _3de=(d.isIE)?function(cond){var clc=cond.toLowerCase();return function(elem){return elem[cond]||elem[clc];};}:function(cond){return function(elem){return (elem&&elem.getAttribute&&elem.hasAttribute(cond));};};var _393=function(_3e4){var _3e5=(_369[_3e4.query]||_368[_3e4.query]);if(_3e5){return _3e5;}var ff=null;if(_3e4.id){if(_3e4.tag!="*"){ff=_36a(ff,function(elem){return (elem.tagName.toLowerCase()==_3e4.tag);});}}d.forEach(_3e4.classes,function(_3e8,idx,arr){var _3eb=_3e8.charAt(_3e8.length-1)=="*";if(_3eb){_3e8=_3e8.substr(0,_3e8.length-1);}var re=new RegExp("(?:^|\\s)"+_3e8+(_3eb?".*":"")+"(?:\\s|$)");ff=_36a(ff,function(elem){return re.test(elem.className);});ff.count=idx;});d.forEach(_3e4.pseudos,function(_3ee){if(_3bb[_3ee.name]){ff=_36a(ff,_3bb[_3ee.name](_3ee.name,_3ee.value));}});_34b(_3a2,_3e4,_3de,function(_3ef){ff=_36a(ff,_3ef);});if(!ff){ff=function(){return true;};}return _369[_3e4.query]=ff;};var _3f0={};var _387=function(_3f1,root){var fHit=_3f0[_3f1.query];if(fHit){return fHit;}if(_3f1.id&&!_3f1.hasLoops&&!_3f1.tag){return _3f0[_3f1.query]=function(root){return [d.byId(_3f1.id)];};}var _3f5=_393(_3f1);var _3f6;if(_3f1.tag&&_3f1.id&&!_3f1.hasLoops){_3f6=function(root){var te=d.byId(_3f1.id);if(_3f5(te)){return [te];}};}else{var tret;if(!_3f1.hasLoops){_3f6=function(root){var ret=[];var te,x=0,tret=root.getElementsByTagName(_3f1.tag);while(te=tret[x++]){ret.push(te);}return ret;};}else{_3f6=function(root){var ret=[];var te,x=0,tret=root.getElementsByTagName(_3f1.tag);while(te=tret[x++]){if(_3f5(te)){ret.push(te);}}return ret;};}}return _3f0[_3f1.query]=_3f6;};var _402={};var _403={"*":d.isIE?function(root){return root.all;}:function(root){return root.getElementsByTagName("*");},"~":_373,"+":function(root){return _373(root,true);},">":_36d};var _407=function(_408){var _409=_323(d.trim(_408));if(_409.length==1){var tt=_387(_409[0]);tt.nozip=true;return tt;}var sqf=function(root){var _40d=_409.slice(0);var _40e;if(_40d[0].oper==">"){_40e=[root];}else{_40e=_387(_40d.shift())(root);}return _388(_40e,_40d);};return sqf;};var _40f=((document["evaluate"]&&!d.isSafari)?function(_410){var _411=_410.split(" ");if((document["evaluate"])&&(_410.indexOf(":")==-1)&&(_410.indexOf("+")==-1)){if(((_411.length>2)&&(_410.indexOf(">")==-1))||(_411.length>3)||(_410.indexOf("[")>=0)||((1==_411.length)&&(0<=_410.indexOf(".")))){return _35f(_410);}}return _407(_410);}:_407);var _412=function(_413){var qcz=_413.charAt(0);if(d.doc["querySelectorAll"]&&((!d.isSafari)||(d.isSafari>3.1))&&(">+~".indexOf(qcz)==-1)){return function(root){var r=root.querySelectorAll(_413);r.nozip=true;return r;};}if(_403[_413]){return _403[_413];}if(0>_413.indexOf(",")){return _403[_413]=_40f(_413);}else{var _417=_413.split(/\s*,\s*/);var tf=function(root){var _41a=0;var ret=[];var tp;while(tp=_417[_41a++]){ret=ret.concat(_40f(tp,tp.indexOf(" "))(root));}return ret;};return _403[_413]=tf;}};var _41d=0;var _zip=function(arr){if(arr&&arr.nozip){return d.NodeList._wrap(arr);}var ret=new d.NodeList();if(!arr){return ret;}if(arr[0]){ret.push(arr[0]);}if(arr.length<2){return ret;}_41d++;arr[0]["_zipIdx"]=_41d;for(var x=1,te;te=arr[x];x++){if(arr[x]["_zipIdx"]!=_41d){ret.push(te);}te["_zipIdx"]=_41d;}return ret;};d.query=function(_423,root){if(_423.constructor==d.NodeList){return _423;}if(!d.isString(_423)){return new d.NodeList(_423);}if(d.isString(root)){root=d.byId(root);}return _zip(_412(_423)(root||d.doc));};d.query.pseudos=_3bb;d._filterQueryResult=function(_425,_426){var tnl=new d.NodeList();var ff=(_426)?_382(_323(_426)[0]):function(){return true;};for(var x=0,te;te=_425[x];x++){if(ff(te)){tnl.push(te);}}return tnl;};})();}if(!dojo._hasResource["dojo._base.xhr"]){dojo._hasResource["dojo._base.xhr"]=true;dojo.provide("dojo._base.xhr");(function(){var _d=dojo;function setValue(obj,name,_42e){var val=obj[name];if(_d.isString(val)){obj[name]=[val,_42e];}else{if(_d.isArray(val)){val.push(_42e);}else{obj[name]=_42e;}}};dojo.formToObject=function(_430){var ret={};var iq="input:not([type=file]):not([type=submit]):not([type=image]):not([type=reset]):not([type=button]), select, textarea";_d.query(iq,_430).filter(function(node){return !node.disabled&&node.name;}).forEach(function(item){var _in=item.name;var type=(item.type||"").toLowerCase();if(type=="radio"||type=="checkbox"){if(item.checked){setValue(ret,_in,item.value);}}else{if(item.multiple){ret[_in]=[];_d.query("option",item).forEach(function(opt){if(opt.selected){setValue(ret,_in,opt.value);}});}else{setValue(ret,_in,item.value);if(type=="image"){ret[_in+".x"]=ret[_in+".y"]=ret[_in].x=ret[_in].y=0;}}}});return ret;};dojo.objectToQuery=function(map){var enc=encodeURIComponent;var _43a=[];var _43b={};for(var name in map){var _43d=map[name];if(_43d!=_43b[name]){var _43e=enc(name)+"=";if(_d.isArray(_43d)){for(var i=0;i<_43d.length;i++){_43a.push(_43e+enc(_43d[i]));}}else{_43a.push(_43e+enc(_43d));}}}return _43a.join("&");};dojo.formToQuery=function(_440){return _d.objectToQuery(_d.formToObject(_440));};dojo.formToJson=function(_441,_442){return _d.toJson(_d.formToObject(_441),_442);};dojo.queryToObject=function(str){var ret={};var qp=str.split("&");var dec=decodeURIComponent;_d.forEach(qp,function(item){if(item.length){var _448=item.split("=");var name=dec(_448.shift());var val=dec(_448.join("="));if(_d.isString(ret[name])){ret[name]=[ret[name]];}if(_d.isArray(ret[name])){ret[name].push(val);}else{ret[name]=val;}}});return ret;};dojo._blockAsync=false;dojo._contentHandlers={"text":function(xhr){return xhr.responseText;},"json":function(xhr){if(!dojo.config.usePlainJson){console.warn("Consider using mimetype:text/json-comment-filtered"+" to avoid potential security issues with JSON endpoints"+" (use djConfig.usePlainJson=true to turn off this message)");}return (xhr.status==204)?undefined:_d.fromJson(xhr.responseText);},"json-comment-filtered":function(xhr){var _44e=xhr.responseText;var _44f=_44e.indexOf("/*");var _450=_44e.lastIndexOf("*/");if(_44f==-1||_450==-1){throw new Error("JSON was not comment filtered");}return (xhr.status==204)?undefined:_d.fromJson(_44e.substring(_44f+2,_450));},"javascript":function(xhr){return _d.eval(xhr.responseText);},"xml":function(xhr){var _453=xhr.responseXML;if(_d.isIE&&(!_453||window.location.protocol=="file:")){_d.forEach(["MSXML2","Microsoft","MSXML","MSXML3"],function(_454){try{var dom=new ActiveXObject(_454+".XMLDOM");dom.async=false;dom.loadXML(xhr.responseText);_453=dom;}catch(e){}});}return _453;}};dojo._contentHandlers["json-comment-optional"]=function(xhr){var _457=_d._contentHandlers;try{return _457["json-comment-filtered"](xhr);}catch(e){return _457["json"](xhr);}};dojo._ioSetArgs=function(args,_459,_45a,_45b){var _45c={args:args,url:args.url};var _45d=null;if(args.form){var form=_d.byId(args.form);var _45f=form.getAttributeNode("action");_45c.url=_45c.url||(_45f?_45f.value:null);_45d=_d.formToObject(form);}var _460=[{}];if(_45d){_460.push(_45d);}if(args.content){_460.push(args.content);}if(args.preventCache){_460.push({"dojo.preventCache":new Date().valueOf()});}_45c.query=_d.objectToQuery(_d.mixin.apply(null,_460));_45c.handleAs=args.handleAs||"text";var d=new _d.Deferred(_459);d.addCallbacks(_45a,function(_462){return _45b(_462,d);});var ld=args.load;if(ld&&_d.isFunction(ld)){d.addCallback(function(_464){return ld.call(args,_464,_45c);});}var err=args.error;if(err&&_d.isFunction(err)){d.addErrback(function(_466){return err.call(args,_466,_45c);});}var _467=args.handle;if(_467&&_d.isFunction(_467)){d.addBoth(function(_468){return _467.call(args,_468,_45c);});}d.ioArgs=_45c;return d;};var _469=function(dfd){dfd.canceled=true;var xhr=dfd.ioArgs.xhr;var _at=typeof xhr.abort;if(_at=="function"||_at=="unknown"){xhr.abort();}var err=new Error("xhr cancelled");err.dojoType="cancel";return err;};var _46e=function(dfd){return _d._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);};var _470=function(_471,dfd){console.debug(_471);return _471;};var _473=function(args){var dfd=_d._ioSetArgs(args,_469,_46e,_470);dfd.ioArgs.xhr=_d._xhrObj(dfd.ioArgs.args);return dfd;};var _476=null;var _477=[];var _478=function(){var now=(new Date()).getTime();if(!_d._blockAsync){for(var i=0,tif;i<_477.length&&(tif=_477[i]);i++){var dfd=tif.dfd;try{if(!dfd||dfd.canceled||!tif.validCheck(dfd)){_477.splice(i--,1);}else{if(tif.ioCheck(dfd)){_477.splice(i--,1);tif.resHandle(dfd);}else{if(dfd.startTime){if(dfd.startTime+(dfd.ioArgs.args.timeout||0)<now){_477.splice(i--,1);var err=new Error("timeout exceeded");err.dojoType="timeout";dfd.errback(err);dfd.cancel();}}}}}catch(e){console.debug(e);dfd.errback(new Error("_watchInFlightError!"));}}}if(!_477.length){clearInterval(_476);_476=null;return;}};dojo._ioCancelAll=function(){try{_d.forEach(_477,function(i){i.dfd.cancel();});}catch(e){}};if(_d.isIE){_d.addOnUnload(_d._ioCancelAll);}_d._ioWatch=function(dfd,_480,_481,_482){if(dfd.ioArgs.args.timeout){dfd.startTime=(new Date()).getTime();}_477.push({dfd:dfd,validCheck:_480,ioCheck:_481,resHandle:_482});if(!_476){_476=setInterval(_478,50);}_478();};var _483="application/x-www-form-urlencoded";var _484=function(dfd){return dfd.ioArgs.xhr.readyState;};var _486=function(dfd){return 4==dfd.ioArgs.xhr.readyState;};var _488=function(dfd){var xhr=dfd.ioArgs.xhr;if(_d._isDocumentOk(xhr)){dfd.callback(dfd);}else{var err=new Error("Unable to load "+dfd.ioArgs.url+" status:"+xhr.status);err.status=xhr.status;err.responseText=xhr.responseText;dfd.errback(err);}};var _48c=function(type,dfd){var _48f=dfd.ioArgs;var args=_48f.args;var xhr=_48f.xhr;xhr.open(type,_48f.url,args.sync!==true,args.user||undefined,args.password||undefined);if(args.headers){for(var hdr in args.headers){if(hdr.toLowerCase()==="content-type"&&!args.contentType){args.contentType=args.headers[hdr];}else{xhr.setRequestHeader(hdr,args.headers[hdr]);}}}xhr.setRequestHeader("Content-Type",args.contentType||_483);if(!args.headers||!args.headers["X-Requested-With"]){xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");}try{xhr.send(_48f.query);}catch(e){dfd.cancel();}_d._ioWatch(dfd,_484,_486,_488);xhr=null;return dfd;};dojo._ioAddQueryToUrl=function(_493){if(_493.query.length){_493.url+=(_493.url.indexOf("?")==-1?"?":"&")+_493.query;_493.query=null;}};dojo.xhr=function(_494,args,_496){var dfd=_473(args);if(!_496){_d._ioAddQueryToUrl(dfd.ioArgs);}return _48c(_494,dfd);};dojo.xhrGet=function(args){return _d.xhr("GET",args);};dojo.xhrPost=function(args){return _d.xhr("POST",args,true);};dojo.rawXhrPost=function(args){var dfd=_473(args);dfd.ioArgs.query=args.postData;return _48c("POST",dfd);};dojo.xhrPut=function(args){return _d.xhr("PUT",args,true);};dojo.rawXhrPut=function(args){var dfd=_473(args);var _49f=dfd.ioArgs;if(args.putData){_49f.query=args.putData;args.putData=null;}return _48c("PUT",dfd);};dojo.xhrDelete=function(args){return _d.xhr("DELETE",args);};})();}if(!dojo._hasResource["dojo._base.fx"]){dojo._hasResource["dojo._base.fx"]=true;dojo.provide("dojo._base.fx");(function(){var d=dojo;dojo._Line=function(_4a2,end){this.start=_4a2;this.end=end;this.getValue=function(n){return ((this.end-this.start)*n)+this.start;};};d.declare("dojo._Animation",null,{constructor:function(args){d.mixin(this,args);if(d.isArray(this.curve)){this.curve=new d._Line(this.curve[0],this.curve[1]);}},duration:350,repeat:0,rate:10,_percent:0,_startRepeatCount:0,_fire:function(evt,args){try{if(this[evt]){this[evt].apply(this,args||[]);}}catch(e){console.error("exception in animation handler for:",evt);console.error(e);}return this;},play:function(_4a8,_4a9){var _t=this;if(_4a9){_t._stopTimer();_t._active=_t._paused=false;_t._percent=0;}else{if(_t._active&&!_t._paused){return _t;}}_t._fire("beforeBegin");var de=_4a8||_t.delay;var _p=dojo.hitch(_t,"_play",_4a9);if(de>0){setTimeout(_p,de);return _t;}_p();return _t;},_play:function(_4ad){var _t=this;_t._startTime=new Date().valueOf();if(_t._paused){_t._startTime-=_t.duration*_t._percent;}_t._endTime=_t._startTime+_t.duration;_t._active=true;_t._paused=false;var _4af=_t.curve.getValue(_t._percent);if(!_t._percent){if(!_t._startRepeatCount){_t._startRepeatCount=_t.repeat;}_t._fire("onBegin",[_4af]);}_t._fire("onPlay",[_4af]);_t._cycle();return _t;},pause:function(){this._stopTimer();if(!this._active){return this;}this._paused=true;this._fire("onPause",[this.curve.getValue(this._percent)]);return this;},gotoPercent:function(_4b0,_4b1){this._stopTimer();this._active=this._paused=true;this._percent=_4b0;if(_4b1){this.play();}return this;},stop:function(_4b2){if(!this._timer){return this;}this._stopTimer();if(_4b2){this._percent=1;}this._fire("onStop",[this.curve.getValue(this._percent)]);this._active=this._paused=false;return this;},status:function(){if(this._active){return this._paused?"paused":"playing";}return "stopped";},_cycle:function(){var _t=this;if(_t._active){var curr=new Date().valueOf();var step=(curr-_t._startTime)/(_t._endTime-_t._startTime);if(step>=1){step=1;}_t._percent=step;if(_t.easing){step=_t.easing(step);}_t._fire("onAnimate",[_t.curve.getValue(step)]);if(_t._percent<1){_t._startTimer();}else{_t._active=false;if(_t.repeat>0){_t.repeat--;_t.play(null,true);}else{if(_t.repeat==-1){_t.play(null,true);}else{if(_t._startRepeatCount){_t.repeat=_t._startRepeatCount;_t._startRepeatCount=0;}}}_t._percent=0;_t._fire("onEnd");_t._stopTimer();}}return _t;}});var ctr=0;var _4b7=[];var _4b8={run:function(){}};var _4b9=null;dojo._Animation.prototype._startTimer=function(){if(!this._timer){this._timer=d.connect(_4b8,"run",this,"_cycle");ctr++;}if(!_4b9){_4b9=setInterval(d.hitch(_4b8,"run"),this.rate);}};dojo._Animation.prototype._stopTimer=function(){if(this._timer){d.disconnect(this._timer);this._timer=null;ctr--;}if(ctr<=0){clearInterval(_4b9);_4b9=null;ctr=0;}};var _4ba=(d.isIE)?function(node){var ns=node.style;if(!ns.zoom.length&&d.style(node,"zoom")=="normal"){ns.zoom="1";}if(!ns.width.length&&d.style(node,"width")=="auto"){ns.width="auto";}}:function(){};dojo._fade=function(args){args.node=d.byId(args.node);var _4be=d.mixin({properties:{}},args);var _4bf=(_4be.properties.opacity={});_4bf.start=!("start" in _4be)?function(){return Number(d.style(_4be.node,"opacity"));}:_4be.start;_4bf.end=_4be.end;var anim=d.animateProperty(_4be);d.connect(anim,"beforeBegin",d.partial(_4ba,_4be.node));return anim;};dojo.fadeIn=function(args){return d._fade(d.mixin({end:1},args));};dojo.fadeOut=function(args){return d._fade(d.mixin({end:0},args));};dojo._defaultEasing=function(n){return 0.5+((Math.sin((n+1.5)*Math.PI))/2);};var _4c4=function(_4c5){this._properties=_4c5;for(var p in _4c5){var prop=_4c5[p];if(prop.start instanceof d.Color){prop.tempColor=new d.Color();}}this.getValue=function(r){var ret={};for(var p in this._properties){var prop=this._properties[p];var _4cc=prop.start;if(_4cc instanceof d.Color){ret[p]=d.blendColors(_4cc,prop.end,r,prop.tempColor).toCss();}else{if(!d.isArray(_4cc)){ret[p]=((prop.end-_4cc)*r)+_4cc+(p!="opacity"?prop.units||"px":"");}}}return ret;};};dojo.animateProperty=function(args){args.node=d.byId(args.node);if(!args.easing){args.easing=d._defaultEasing;}var anim=new d._Animation(args);d.connect(anim,"beforeBegin",anim,function(){var pm={};for(var p in this.properties){if(p=="width"||p=="height"){this.node.display="block";}var prop=this.properties[p];prop=pm[p]=d.mixin({},(d.isObject(prop)?prop:{end:prop}));if(d.isFunction(prop.start)){prop.start=prop.start();}if(d.isFunction(prop.end)){prop.end=prop.end();}var _4d2=(p.toLowerCase().indexOf("color")>=0);function getStyle(node,p){var v=({height:node.offsetHeight,width:node.offsetWidth})[p];if(v!==undefined){return v;}v=d.style(node,p);return (p=="opacity")?Number(v):(_4d2?v:parseFloat(v));};if(!("end" in prop)){prop.end=getStyle(this.node,p);}else{if(!("start" in prop)){prop.start=getStyle(this.node,p);}}if(_4d2){prop.start=new d.Color(prop.start);prop.end=new d.Color(prop.end);}else{prop.start=(p=="opacity")?Number(prop.start):parseFloat(prop.start);}}this.curve=new _4c4(pm);});d.connect(anim,"onAnimate",anim,function(_4d6){for(var s in _4d6){d.style(this.node,s,_4d6[s]);}});return anim;};dojo.anim=function(node,_4d9,_4da,_4db,_4dc,_4dd){return d.animateProperty({node:node,duration:_4da||d._Animation.prototype.duration,properties:_4d9,easing:_4db,onEnd:_4dc}).play(_4dd||0);};})();}if(!dojo._hasResource["dojo._base.browser"]){dojo._hasResource["dojo._base.browser"]=true;dojo.provide("dojo._base.browser");if(dojo.config.require){dojo.forEach(dojo.config.require,"dojo['require'](item);");}}if(dojo.config.afterOnLoad&&dojo.isBrowser){window.setTimeout(dojo._fakeLoadInit,1000);}})();
diff --git a/webapps/cometd/examples/dojo/dojo/dojo.js.uncompressed.js b/webapps/cometd/examples/dojo/dojo/dojo.js.uncompressed.js
new file mode 100644 (file)
index 0000000..6746345
--- /dev/null
@@ -0,0 +1,8165 @@
+/*
+       Copyright (c) 2004-2008, The Dojo Foundation
+       All Rights Reserved.
+
+       Licensed under the Academic Free License version 2.1 or above OR the
+       modified BSD license. For more information on Dojo licensing, see:
+
+               http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing
+*/
+
+/*
+       This is a compiled version of Dojo, built for deployment and not for
+       development. To get an editable version, please visit:
+
+               http://dojotoolkit.org
+
+       for documentation and information on getting the source.
+*/
+
+;(function(){
+
+       /*
+       dojo, dijit, and dojox must always be the first three, and in that order.
+       djConfig.scopeMap = [
+               ["dojo", "fojo"],
+               ["dijit", "fijit"],
+               ["dojox", "fojox"]
+       
+       ]
+       */
+
+       /**Build will replace this comment with a scoped djConfig **/
+
+       //The null below can be relaced by a build-time value used instead of djConfig.scopeMap.
+       var sMap = null;
+
+       //See if new scopes need to be defined.
+       if((sMap || (typeof djConfig != "undefined" && djConfig.scopeMap)) && (typeof window != "undefined")){
+               var scopeDef = "", scopePrefix = "", scopeSuffix = "", scopeMap = {}, scopeMapRev = {};
+               sMap = sMap || djConfig.scopeMap;
+               for(var i = 0; i < sMap.length; i++){
+                       //Make local variables, then global variables that use the locals.
+                       var newScope = sMap[i];
+                       scopeDef += "var " + newScope[0] + " = {}; " + newScope[1] + " = " + newScope[0] + ";" + newScope[1] + "._scopeName = '" + newScope[1] + "';";
+                       scopePrefix += (i == 0 ? "" : ",") + newScope[0];
+                       scopeSuffix += (i == 0 ? "" : ",") + newScope[1];
+                       scopeMap[newScope[0]] = newScope[1];
+                       scopeMapRev[newScope[1]] = newScope[0];
+               }
+
+               eval(scopeDef + "dojo._scopeArgs = [" + scopeSuffix + "];");
+
+               dojo._scopePrefixArgs = scopePrefix;
+               dojo._scopePrefix = "(function(" + scopePrefix + "){";
+               dojo._scopeSuffix = "})(" + scopeSuffix + ")";
+               dojo._scopeMap = scopeMap;
+               dojo._scopeMapRev = scopeMapRev;
+       }
+
+/*=====
+// note:
+//             'djConfig' does not exist under 'dojo.*' so that it can be set before the
+//             'dojo' variable exists.
+// note:
+//             Setting any of these variables *after* the library has loaded does
+//             nothing at all.
+
+djConfig = {
+       // summary:
+       //              Application code can set the global 'djConfig' prior to loading
+       //              the library to override certain global settings for how dojo works.
+       //
+       // isDebug: Boolean
+       //              Defaults to `false`. If set to `true`, ensures that Dojo provides
+       //              extende debugging feedback via Firebug. If Firebug is not available
+       //              on your platform, setting `isDebug` to `true` will force Dojo to
+       //              pull in (and display) the version of Firebug Lite which is
+       //              integrated into the Dojo distribution, thereby always providing a
+       //              debugging/logging console when `isDebug` is enabled. Note that
+       //              Firebug's `console.*` methods are ALWAYS defined by Dojo. If
+       //              `isDebug` is false and you are on a platform without Firebug, these
+       //              methods will be defined as no-ops.
+       isDebug: false,
+       // debugAtAllCosts: Boolean
+       //              Defaults to `false`. If set to `true`, this triggers an alternate
+       //              mode of the package system in which dependencies are detected and
+       //              only then are resources evaluated in dependency order via
+       //              `<script>` tag inclusion. This may double-request resources and
+       //              cause problems with scripts which expect `dojo.require()` to
+       //              preform synchronously. `debugAtAllCosts` can be an invaluable
+       //              debugging aid, but when using it, ensure that all code which
+       //              depends on Dojo modules is wrapped in `dojo.addOnLoad()` handlers.
+       //              Due to the somewhat unpredictable side-effects of using
+       //              `debugAtAllCosts`, it is strongly recommended that you enable this
+       //              flag as a last resort. `debugAtAllCosts` has no effect when loading
+       //              resources across domains. For usage information, see the
+       //              [Dojo Book](http://dojotoolkit.org/book/book-dojo/part-4-meta-dojo-making-your-dojo-code-run-faster-and-better/debugging-facilities/deb)
+       debugAtAllCosts: false,
+       // locale: String
+       //              The locale to assume for loading localized resources in this page,
+       //              specified according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
+       //              Must be specified entirely in lowercase, e.g. `en-us` and `zh-cn`.
+       //              See the documentation for `dojo.i18n` and `dojo.requireLocalization`
+       //              for details on loading localized resources. If no locale is specified,
+       //              Dojo assumes the locale of the user agent, according to `navigator.userLanguage`
+       //              or `navigator.language` properties.
+       locale: undefined,
+       // extraLocale: Array
+       //              No default value. Specifies additional locales whose
+       //              resources should also be loaded alongside the default locale when
+       //              calls to `dojo.requireLocalization()` are processed.
+       extraLocale: undefined,
+       // baseUrl: String
+       //              The directory in which `dojo.js` is located. Under normal
+       //              conditions, Dojo auto-detects the correct location from which it
+       //              was loaded. You may need to manually configure `baseUrl` in cases
+       //              where you have renamed `dojo.js` or in which `<base>` tags confuse
+       //              some browsers (e.g., IE 6). The variable `dojo.baseUrl` is assigned
+       //              either the value of `djConfig.baseUrl` if one is provided or the
+       //              auto-detected root if not. Other modules are located relative to
+       //              this path.
+       baseUrl: undefined,
+       // modulePaths: Object
+       //              A map of module names to paths relative to `dojo.baseUrl`. The
+       //              key/value pairs correspond directly to the arguments which
+       //              `dojo.registerModulePath` accepts. Specifiying
+       //              `djConfig.modulePaths = { "foo": "../../bar" }` is the equivalent
+       //              of calling `dojo.registerModulePath("foo", "../../bar");`. Multiple
+       //              modules may be configured via `djConfig.modulePaths`.
+       modulePaths: {},
+}
+=====*/
+
+(function(){
+       // firebug stubs
+
+       // if((!this["console"])||(!console["firebug"])){
+
+       if(!this["console"]){
+               this.console = {
+                       log: function(){} // no-op
+               };
+       }
+
+       var cn = [
+               "assert", "count", "debug", "dir", "dirxml", "error", "group",
+               "groupEnd", "info", "profile", "profileEnd", "time", "timeEnd",
+               "trace", "warn", "log" 
+       ];
+       var i=0, tn;
+       while((tn=cn[i++])){
+               if(!console[tn]){
+                       (function(){
+                               var tcn = tn+"";
+                               console[tcn] = function(){ 
+                                       var a = Array.apply({}, arguments);
+                                       a.unshift(tcn+":");
+                                       console.log(a.join(" "));
+                               }
+                       })();
+               }
+       }
+
+       //TODOC:  HOW TO DOC THIS?
+       // dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
+       if(typeof dojo == "undefined"){
+               this.dojo = {
+                       _scopeName: "dojo",
+                       _scopePrefix: "",
+                       _scopePrefixArgs: "",
+                       _scopeSuffix: "",
+                       _scopeMap: {},
+                       _scopeMapRev: {}
+               };
+       }
+
+       var d = dojo;
+
+       //Need placeholders for dijit and dojox for scoping code.
+       if(typeof dijit == "undefined"){
+               this.dijit = {_scopeName: "dijit"};
+       }
+       if(typeof dojox == "undefined"){
+               this.dojox = {_scopeName: "dojox"};
+       }
+       
+       if(!d._scopeArgs){
+               d._scopeArgs = [dojo, dijit, dojox];
+       }
+
+/*=====
+dojo.global = {
+       //      summary:
+       //              Alias for the global scope
+       //              (e.g. the window object in a browser).
+       //      description:
+       //              Refer to 'dojo.global' rather than referring to window to ensure your
+       //              code runs correctly in contexts other than web browsers (e.g. Rhino on a server).
+}
+=====*/
+       d.global = this;
+
+       d.config =/*===== djConfig = =====*/{
+               isDebug: false,
+               debugAtAllCosts: false
+       };
+
+       if(typeof djConfig != "undefined"){
+               for(var opt in djConfig){
+                       d.config[opt] = djConfig[opt];
+               }
+       }
+
+       var _platforms = ["Browser", "Rhino", "Spidermonkey", "Mobile"];
+       var t;
+       while((t=_platforms.shift())){
+               d["is"+t] = false;
+       }
+
+/*=====
+       // Override locale setting, if specified
+       dojo.locale = {
+               // summary: the locale as defined by Dojo (read-only)
+       };
+=====*/
+       dojo.locale = d.config.locale;
+       
+       var rev = "$Rev: 13707 $".match(/\d+/);
+
+       dojo.version = {
+               // summary: 
+               //              version number of dojo
+               //      major: Integer
+               //              Major version. If total version is "1.2.0beta1", will be 1
+               //      minor: Integer
+               //              Minor version. If total version is "1.2.0beta1", will be 2
+               //      patch: Integer
+               //              Patch version. If total version is "1.2.0beta1", will be 0
+               //      flag: String
+               //              Descriptor flag. If total version is "1.2.0beta1", will be "beta1"
+               //      revision: Number
+               //              The SVN rev from which dojo was pulled
+               major: 1, minor: 1, patch: 1, flag: "",
+               revision: rev ? +rev[0] : 999999, //FIXME: use NaN?
+               toString: function(){
+                       with(d.version){
+                               return major + "." + minor + "." + patch + flag + " (" + revision + ")";        // String
+                       }
+               }
+       }
+
+       // Register with the OpenAjax hub
+       if(typeof OpenAjax != "undefined"){
+               OpenAjax.hub.registerLibrary(dojo._scopeName, "http://dojotoolkit.org", d.version.toString());
+       }
+
+       dojo._mixin = function(/*Object*/ obj, /*Object*/ props){
+               // summary:
+               //              Adds all properties and methods of props to obj. This addition
+               //              is "prototype extension safe", so that instances of objects
+               //              will not pass along prototype defaults.
+               var tobj = {};
+               for(var x in props){
+                       // the "tobj" condition avoid copying properties in "props"
+                       // inherited from Object.prototype.  For example, if obj has a custom
+                       // toString() method, don't overwrite it with the toString() method
+                       // that props inherited from Object.prototype
+                       if(tobj[x] === undefined || tobj[x] != props[x]){
+                               obj[x] = props[x];
+                       }
+               }
+               // IE doesn't recognize custom toStrings in for..in
+               if(d["isIE"] && props){
+                       var p = props.toString;
+                       if(typeof p == "function" && p != obj.toString && p != tobj.toString &&
+                               p != "\nfunction toString() {\n    [native code]\n}\n"){
+                                       obj.toString = props.toString;
+                       }
+               }
+               return obj; // Object
+       }
+
+       dojo.mixin = function(/*Object*/obj, /*Object...*/props){
+               // summary:     
+               //              Adds all properties and methods of props to obj and returns the
+               //              (now modified) obj.
+               //      description:
+               //              `dojo.mixin` can mix multiple source objects into a
+               //              destionation object which is then returned. Unlike regular
+               //              `for...in` iteration, `dojo.mixin` is also smart about avoiding
+               //              extensions which other toolkits may unwisely add to the root
+               //              object prototype
+               //      obj:
+               //              The object to mix properties into. Also the return value.
+               //      props:
+               //              One or more objects whose values are successively copied into
+               //              obj. If more than one of these objects contain the same value,
+               //              the one specified last in the function call will "win".
+               //      example:
+               //              make a shallow copy of an object
+               //      |       var copy = dojo.mixin({}, source);
+               //      example:
+               //              many class constructors often take an object which specifies
+               //              values to be configured on the object. In this case, it is
+               //              often simplest to call `dojo.mixin` on the `this` object:
+               //      |       dojo.declare("acme.Base", null, {
+               //      |               constructor: function(properties){
+               //      |                       // property configuration:
+               //      |                       dojo.mixin(this, properties);
+               //      |       
+               //      |                       console.debug(this.quip);
+               //      |                       //  ...
+               //      |               },
+               //      |               quip: "I wasn't born yesterday, you know - I've seen movies.",
+               //      |               // ...
+               //      |       });
+               //      |
+               //      |       // create an instance of the class and configure it
+               //      |       var b = new acme.Base({quip: "That's what it does!" });
+               //      example:
+               //              copy in properties from multiple objects
+               //      |       var flattened = dojo.mixin(
+               //      |               {
+               //      |                       name: "Frylock",
+               //      |                       braces: true,
+               //      |               }
+               //      |               {
+               //      |                       name: "Carl Brutanananadilewski"
+               //      |               }
+               //      |       );
+               //      |       
+               //      |       // will print "Carl Brutanananadilewski"
+               //      |       console.debug(flattened.name);
+               //      |       // will print "true"
+               //      |       console.debug(flattened.braces);
+               for(var i=1, l=arguments.length; i<l; i++){
+                       d._mixin(obj, arguments[i]);
+               }
+               return obj; // Object
+       }
+
+       dojo._getProp = function(/*Array*/parts, /*Boolean*/create, /*Object*/context){
+               var obj=context || d.global;
+               for(var i=0, p; obj && (p=parts[i]); i++){
+                       if(i == 0 && this._scopeMap[p]){
+                               p = this._scopeMap[p];
+                       }
+                       obj = (p in obj ? obj[p] : (create ? obj[p]={} : undefined));
+               }
+               return obj; // mixed
+       }
+
+       dojo.setObject = function(/*String*/name, /*Object*/value, /*Object?*/context){
+               // summary: 
+               //              Set a property from a dot-separated string, such as "A.B.C"
+               //      description: 
+               //              Useful for longer api chains where you have to test each object in
+               //              the chain, or when you have an object reference in string format.
+               //              Objects are created as needed along `path`. Returns the passed
+               //              value if setting is successful or `undefined` if not.
+               //      name:   
+               //              Path to a property, in the form "A.B.C".
+               //      context:
+               //              Optional. Object to use as root of path. Defaults to
+               //              `dojo.global`.
+               //      example:
+               //              set the value of `foo.bar.baz`, regardless of whether
+               //              intermediate objects already exist:
+               //      |       dojo.setObject("foo.bar.baz", value);
+               //      example:
+               //              without `dojo.setObject`, we often see code like this:
+               //      |       // ensure that intermediate objects are available
+               //      |       if(!obj["parent"]){ obj.parent = {}; }
+               //      |       if(!obj.parent["child"]){ obj.parent.child= {}; }
+               //      |       // now we can safely set the property
+               //      |       obj.parent.child.prop = "some value";
+               //              wheras with `dojo.setObject`, we can shorten that to:
+               //      |       dojo.setObject("parent.child.prop", "some value", obj);
+               var parts=name.split("."), p=parts.pop(), obj=d._getProp(parts, true, context);
+               return obj && p ? (obj[p]=value) : undefined; // Object
+       }
+
+       dojo.getObject = function(/*String*/name, /*Boolean*/create, /*Object*/context){
+               // summary: 
+               //              Get a property from a dot-separated string, such as "A.B.C"
+               //      description: 
+               //              Useful for longer api chains where you have to test each object in
+               //              the chain, or when you have an object reference in string format.
+               //      name:   
+               //              Path to an property, in the form "A.B.C".
+               //      context:
+               //              Optional. Object to use as root of path. Defaults to
+               //              'dojo.global'. Null may be passed.
+               //      create: 
+               //              Optional. Defaults to `false`. If `true`, Objects will be
+               //              created at any point along the 'path' that is undefined.
+               return d._getProp(name.split("."), create, context); // Object
+       }
+
+       dojo.exists = function(/*String*/name, /*Object?*/obj){
+               //      summary: 
+               //              determine if an object supports a given method
+               //      description: 
+               //              useful for longer api chains where you have to test each object in
+               //              the chain
+               //      name:   
+               //              Path to an object, in the form "A.B.C".
+               //      obj:
+               //              Object to use as root of path. Defaults to
+               //              'dojo.global'. Null may be passed.
+               //      example:
+               //      |       // define an object
+               //      |       var foo = {
+               //      |               bar: { }
+               //      |       };
+               //      |
+               //      |       // search the global scope
+               //      |       dojo.exists("foo.bar"); // true
+               //      |       dojo.exists("foo.bar.baz"); // false
+               //      |
+               //      |       // search from a particular scope
+               //      |       dojo.exists("bar", foo); // true
+               //      |       dojo.exists("bar.baz", foo); // false
+               return !!d.getObject(name, false, obj); // Boolean
+       }
+
+
+       dojo["eval"] = function(/*String*/ scriptFragment){
+               //      summary: 
+               //              Perform an evaluation in the global scope. Use this rather than
+               //              calling 'eval()' directly.
+               //      description: 
+               //              Placed in a separate function to minimize size of trapped
+               //              exceptions. Calling eval() directly from some other scope may
+               //              complicate tracebacks on some platforms.
+               //      return:
+               //              The result of the evaluation. Often `undefined`
+
+
+               // note:
+               //       - JSC eval() takes an optional second argument which can be 'unsafe'.
+               //       - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
+               //       scope object for new symbols.
+
+               // FIXME: investigate Joseph Smarr's technique for IE:
+               //              http://josephsmarr.com/2007/01/31/fixing-eval-to-use-global-scope-in-ie/
+               //      see also:
+               //              http://trac.dojotoolkit.org/ticket/744
+               return d.global.eval ? d.global.eval(scriptFragment) : eval(scriptFragment);    // Object
+       }
+
+       /*=====
+               dojo.deprecated = function(behaviour, extra, removal){
+                       //      summary: 
+                       //              Log a debug message to indicate that a behavior has been
+                       //              deprecated.
+                       //      behaviour: String
+                       //              The API or behavior being deprecated. Usually in the form
+                       //              of "myApp.someFunction()".
+                       //      extra: String?
+                       //              Text to append to the message. Often provides advice on a
+                       //              new function or facility to achieve the same goal during
+                       //              the deprecation period.
+                       //      removal: String?
+                       //              Text to indicate when in the future the behavior will be
+                       //              removed. Usually a version number.
+                       //      example:
+                       //      |       dojo.deprecated("myApp.getTemp()", "use myApp.getLocaleTemp() instead", "1.0");
+               }
+
+               dojo.experimental = function(moduleName, extra){
+                       //      summary: Marks code as experimental.
+                       //      description: 
+                       //              This can be used to mark a function, file, or module as
+                       //              experimental.  Experimental code is not ready to be used, and the
+                       //              APIs are subject to change without notice.  Experimental code may be
+                       //              completed deleted without going through the normal deprecation
+                       //              process.
+                       //      moduleName: String
+                       //              The name of a module, or the name of a module file or a specific
+                       //              function
+                       //      extra: String?
+                       //              some additional message for the user
+                       //      example:
+                       //      |       dojo.experimental("dojo.data.Result");
+                       //      example:
+                       //      |       dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA");
+               }
+       =====*/
+
+       //Real functions declared in dojo._firebug.firebug.
+       d.deprecated = d.experimental = function(){};
+
+})();
+// vim:ai:ts=4:noet
+
+/*
+ * loader.js - A bootstrap module.  Runs before the hostenv_*.js file. Contains
+ * all of the package loading methods.
+ */
+
+(function(){
+       var d = dojo;
+
+       d.mixin(d, {
+               _loadedModules: {},
+               _inFlightCount: 0,
+               _hasResource: {},
+
+               _modulePrefixes: {
+                       dojo:   {       name: "dojo", value: "." },
+                       // dojox:       {       name: "dojox", value: "../dojox" },
+                       // dijit:       {       name: "dijit", value: "../dijit" },
+                       doh:    {       name: "doh", value: "../util/doh" },
+                       tests:  {       name: "tests", value: "tests" }
+               },
+
+               _moduleHasPrefix: function(/*String*/module){
+                       // summary: checks to see if module has been established
+                       var mp = this._modulePrefixes;
+                       return !!(mp[module] && mp[module].value); // Boolean
+               },
+
+               _getModulePrefix: function(/*String*/module){
+                       // summary: gets the prefix associated with module
+                       var mp = this._modulePrefixes;
+                       if(this._moduleHasPrefix(module)){
+                               return mp[module].value; // String
+                       }
+                       return module; // String
+               },
+
+               _loadedUrls: [],
+
+               //WARNING: 
+               //              This variable is referenced by packages outside of bootstrap:
+               //              FloatingPane.js and undo/browser.js
+               _postLoad: false,
+               
+               //Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.
+               _loaders: [],
+               _unloaders: [],
+               _loadNotifying: false
+       });
+
+
+               dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
+               //      summary:
+               //              Load a Javascript module given a relative path
+               //
+               //      description:
+               //              Loads and interprets the script located at relpath, which is
+               //              relative to the script root directory.  If the script is found but
+               //              its interpretation causes a runtime exception, that exception is
+               //              not caught by us, so the caller will see it.  We return a true
+               //              value if and only if the script is found.
+               //
+               // relpath: 
+               //              A relative path to a script (no leading '/', and typically ending
+               //              in '.js').
+               // module: 
+               //              A module whose existance to check for after loading a path.  Can be
+               //              used to determine success or failure of the load.
+               // cb: 
+               //              a callback function to pass the result of evaluating the script
+
+               var uri = ((relpath.charAt(0) == '/' || relpath.match(/^\w+:/)) ? "" : this.baseUrl) + relpath;
+               try{
+                       return !module ? this._loadUri(uri, cb) : this._loadUriAndCheck(uri, module, cb); // Boolean
+               }catch(e){
+                       console.error(e);
+                       return false; // Boolean
+               }
+       }
+
+       dojo._loadUri = function(/*String*/uri, /*Function?*/cb){
+               //      summary:
+               //              Loads JavaScript from a URI
+               //      description:
+               //              Reads the contents of the URI, and evaluates the contents.  This is
+               //              used to load modules as well as resource bundles. Returns true if
+               //              it succeeded. Returns false if the URI reading failed.  Throws if
+               //              the evaluation throws.
+               //      uri: a uri which points at the script to be loaded
+               //      cb: 
+               //              a callback function to process the result of evaluating the script
+               //              as an expression, typically used by the resource bundle loader to
+               //              load JSON-style resources
+
+               if(this._loadedUrls[uri]){
+                       return true; // Boolean
+               }
+               var contents = this._getText(uri, true);
+               if(!contents){ return false; } // Boolean
+               this._loadedUrls[uri] = true;
+               this._loadedUrls.push(uri);
+               if(cb){
+                       contents = '('+contents+')';
+               }else{
+                       //Only do the scoping if no callback. If a callback is specified,
+                       //it is most likely the i18n bundle stuff.
+                       contents = this._scopePrefix + contents + this._scopeSuffix;
+               }
+               if(d.isMoz){ contents += "\r\n//@ sourceURL=" + uri; } // debugging assist for Firebug
+               var value = d["eval"](contents);
+               if(cb){ cb(value); }
+               return true; // Boolean
+       }
+       
+       // FIXME: probably need to add logging to this method
+       dojo._loadUriAndCheck = function(/*String*/uri, /*String*/moduleName, /*Function?*/cb){
+               // summary: calls loadUri then findModule and returns true if both succeed
+               var ok = false;
+               try{
+                       ok = this._loadUri(uri, cb);
+               }catch(e){
+                       console.error("failed loading " + uri + " with error: " + e);
+               }
+               return !!(ok && this._loadedModules[moduleName]); // Boolean
+       }
+
+       dojo.loaded = function(){
+               // summary:
+               //              signal fired when initial environment and package loading is
+               //              complete. You may use dojo.addOnLoad() or dojo.connect() to
+               //              this method in order to handle initialization tasks that
+               //              require the environment to be initialized. In a browser host,
+               //              declarative widgets will be constructed when this function
+               //              finishes runing.
+               this._loadNotifying = true;
+               this._postLoad = true;
+               var mll = d._loaders;
+               
+               //Clear listeners so new ones can be added
+               //For other xdomain package loads after the initial load.
+               this._loaders = [];
+
+               for(var x = 0; x < mll.length; x++){
+                       try{
+                               mll[x]();
+                       }catch(e){
+                               throw e;
+                               console.error("dojo.addOnLoad callback failed: " + e, e); /* let other load events fire, like the parser, but report the error */
+                       }
+               }
+
+               this._loadNotifying = false;
+               
+               //Make sure nothing else got added to the onload queue
+               //after this first run. If something did, and we are not waiting for any
+               //more inflight resources, run again.
+               if(d._postLoad && d._inFlightCount == 0 && mll.length){
+                       d._callLoaded();
+               }
+       }
+
+       dojo.unloaded = function(){
+               // summary:
+               //              signal fired by impending environment destruction. You may use
+               //              dojo.addOnUnload() or dojo.connect() to this method to perform
+               //              page/application cleanup methods.
+               var mll = this._unloaders;
+               while(mll.length){
+                       (mll.pop())();
+               }
+       }
+
+       var onto = function(arr, obj, fn){
+               if(!fn){
+                       arr.push(obj);
+               }else if(fn){
+                       var func = (typeof fn == "string") ? obj[fn] : fn;
+                       arr.push(function(){ func.call(obj); });
+               }
+       }
+
+       dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName){
+               // summary:
+               //              Registers a function to be triggered after the DOM has finished
+               //              loading and widgets declared in markup have been instantiated.
+               //              Images and CSS files may or may not have finished downloading when
+               //              the specified function is called.  (Note that widgets' CSS and HTML
+               //              code is guaranteed to be downloaded before said widgets are
+               //              instantiated.)
+               // example:
+               //      |       dojo.addOnLoad(functionPointer);
+               //      |       dojo.addOnLoad(object, "functionName");
+               //      |       dojo.addOnLoad(object, function(){ /* ... */});
+
+               onto(d._loaders, obj, functionName);
+
+               //Added for xdomain loading. dojo.addOnLoad is used to
+               //indicate callbacks after doing some dojo.require() statements.
+               //In the xdomain case, if all the requires are loaded (after initial
+               //page load), then immediately call any listeners.
+               if(d._postLoad && d._inFlightCount == 0 && !d._loadNotifying){
+                       d._callLoaded();
+               }
+       }
+
+       dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
+               // summary:
+               //              registers a function to be triggered when the page unloads
+               // example:
+               //      |       dojo.addOnUnload(functionPointer)
+               //      |       dojo.addOnUnload(object, "functionName")
+               //      |       dojo.addOnUnload(object, function(){ /* ... */});
+
+               onto(d._unloaders, obj, functionName);
+       }
+
+       dojo._modulesLoaded = function(){
+               if(d._postLoad){ return; }
+               if(d._inFlightCount > 0){ 
+                       console.warn("files still in flight!");
+                       return;
+               }
+               d._callLoaded();
+       }
+
+       dojo._callLoaded = function(){
+
+               // The "object" check is for IE, and the other opera check fixes an
+               // issue in Opera where it could not find the body element in some
+               // widget test cases.  For 0.9, maybe route all browsers through the
+               // setTimeout (need protection still for non-browser environments
+               // though). This might also help the issue with FF 2.0 and freezing
+               // issues where we try to do sync xhr while background css images are
+               // being loaded (trac #2572)? Consider for 0.9.
+               if(typeof setTimeout == "object" || (dojo.config.useXDomain && d.isOpera)){
+                       if(dojo.isAIR){
+                               setTimeout(function(){dojo.loaded();}, 0);
+                       }else{
+                               setTimeout(dojo._scopeName + ".loaded();", 0);
+                       }
+               }else{
+                       d.loaded();
+               }
+       }
+
+       dojo._getModuleSymbols = function(/*String*/modulename){
+               // summary:
+               //              Converts a module name in dotted JS notation to an array
+               //              representing the path in the source tree
+               var syms = modulename.split(".");
+               for(var i = syms.length; i>0; i--){
+                       var parentModule = syms.slice(0, i).join(".");
+                       if((i==1) && !this._moduleHasPrefix(parentModule)){             
+                               // Support default module directory (sibling of dojo) for top-level modules 
+                               syms[0] = "../" + syms[0];
+                       }else{
+                               var parentModulePath = this._getModulePrefix(parentModule);
+                               if(parentModulePath != parentModule){
+                                       syms.splice(0, i, parentModulePath);
+                                       break;
+                               }
+                       }
+               }
+               // console.debug(syms);
+               return syms; // Array
+       }
+
+       dojo._global_omit_module_check = false;
+
+       dojo._loadModule = dojo.require = function(/*String*/moduleName, /*Boolean?*/omitModuleCheck){
+               //      summary:
+               //              loads a Javascript module from the appropriate URI
+               //      moduleName:
+               //              module name to load. Module paths are de-referenced by dojo's
+               //              internal mapping of locations to names and are disambiguated by
+               //              longest prefix. See `dojo.registerModulePath()` for details on
+               //              registering new modules.
+               //      omitModuleCheck:
+               //              if `true`, omitModuleCheck skips the step of ensuring that the
+               //              loaded file actually defines the symbol it is referenced by.
+               //              For example if it called as `dojo._loadModule("a.b.c")` and the
+               //              file located at `a/b/c.js` does not define an object `a.b.c`,
+               //              and exception will be throws whereas no exception is raised
+               //              when called as `dojo._loadModule("a.b.c", true)`
+               //      description:
+               //              `dojo._loadModule("A.B")` first checks to see if symbol A.B is
+               //              defined. If it is, it is simply returned (nothing to do).
+               //      
+               //              If it is not defined, it will look for `A/B.js` in the script root
+               //              directory.
+               //      
+               //              `dojo._loadModule` throws an excpetion if it cannot find a file
+               //              to load, or if the symbol `A.B` is not defined after loading.
+               //      
+               //              It returns the object `A.B`.
+               //      
+               //              `dojo._loadModule()` does nothing about importing symbols into
+               //              the current namespace.  It is presumed that the caller will
+               //              take care of that. For example, to import all symbols into a
+               //              local block, you might write:
+               //      
+               //              |       with (dojo._loadModule("A.B")) {
+               //              |               ...
+               //              |       }
+               //      
+               //              And to import just the leaf symbol to a local variable:
+               //      
+               //              |       var B = dojo._loadModule("A.B");
+               //              |       ...
+               //      returns: the required namespace object
+               omitModuleCheck = this._global_omit_module_check || omitModuleCheck;
+
+               //Check if it is already loaded.
+               var module = this._loadedModules[moduleName];
+               if(module){
+                       return module;
+               }
+
+               // convert periods to slashes
+               var relpath = this._getModuleSymbols(moduleName).join("/") + '.js';
+
+               var modArg = (!omitModuleCheck) ? moduleName : null;
+               var ok = this._loadPath(relpath, modArg);
+
+               if(!ok && !omitModuleCheck){
+                       throw new Error("Could not load '" + moduleName + "'; last tried '" + relpath + "'");
+               }
+
+               // check that the symbol was defined
+               // Don't bother if we're doing xdomain (asynchronous) loading.
+               if(!omitModuleCheck && !this._isXDomain){
+                       // pass in false so we can give better error
+                       module = this._loadedModules[moduleName];
+                       if(!module){
+                               throw new Error("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); 
+                       }
+               }
+
+               return module;
+       }
+
+       dojo.provide = function(/*String*/ resourceName){
+               //      summary:
+               //              Each javascript source file must have at least one
+               //              `dojo.provide()` call at the top of the file, corresponding to
+               //              the file name.  For example, `js/dojo/foo.js` must have
+               //              `dojo.provide("dojo.foo");` before any calls to
+               //              `dojo.require()` are made.
+               //      description:
+               //              Each javascript source file is called a resource.  When a
+               //              resource is loaded by the browser, `dojo.provide()` registers
+               //              that it has been loaded.
+               //      
+               //              For backwards compatibility reasons, in addition to registering
+               //              the resource, `dojo.provide()` also ensures that the javascript
+               //              object for the module exists.  For example,
+               //              `dojo.provide("dojox.data.FlickrStore")`, in addition to
+               //              registering that `FlickrStore.js` is a resource for the
+               //              `dojox.data` module, will ensure that the `dojox.data`
+               //              javascript object exists, so that calls like 
+               //              `dojo.data.foo = function(){ ... }` don't fail.
+               //
+               //              In the case of a build where multiple javascript source files
+               //              are combined into one bigger file (similar to a .lib or .jar
+               //              file), that file may contain multiple dojo.provide() calls, to
+               //              note that it includes multiple resources.
+
+               //Make sure we have a string.
+               resourceName = resourceName + "";
+               return (d._loadedModules[resourceName] = d.getObject(resourceName, true)); // Object
+       }
+
+       //Start of old bootstrap2:
+
+       dojo.platformRequire = function(/*Object*/modMap){
+               //      summary:
+               //              require one or more modules based on which host environment
+               //              Dojo is currently operating in
+               //      description:
+               //              This method takes a "map" of arrays which one can use to
+               //              optionally load dojo modules. The map is indexed by the
+               //              possible dojo.name_ values, with two additional values:
+               //              "default" and "common". The items in the "default" array will
+               //              be loaded if none of the other items have been choosen based on
+               //              dojo.name_, set by your host environment. The items in the
+               //              "common" array will *always* be loaded, regardless of which
+               //              list is chosen.
+               //      example:
+               //              |       dojo.platformRequire({
+               //              |               browser: [
+               //              |                       "foo.sample", // simple module
+               //              |                       "foo.test",
+               //              |                       ["foo.bar.baz", true] // skip object check in _loadModule
+               //              |               ],
+               //              |               default: [ "foo.sample._base" ],
+               //              |               common: [ "important.module.common" ]
+               //              |       });
+
+               var common = modMap.common || [];
+               var result = common.concat(modMap[d._name] || modMap["default"] || []);
+
+               for(var x=0; x<result.length; x++){
+                       var curr = result[x];
+                       if(curr.constructor == Array){
+                               d._loadModule.apply(d, curr);
+                       }else{
+                               d._loadModule(curr);
+                       }
+               }
+       }
+
+       dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){
+               // summary:
+               //              If the condition is true then call dojo.require() for the specified
+               //              resource
+               if(condition === true){
+                       // FIXME: why do we support chained require()'s here? does the build system?
+                       var args = [];
+                       for(var i = 1; i < arguments.length; i++){ 
+                               args.push(arguments[i]);
+                       }
+                       d.require.apply(d, args);
+               }
+       }
+
+       dojo.requireAfterIf = d.requireIf;
+
+       dojo.registerModulePath = function(/*String*/module, /*String*/prefix){
+               //      summary: 
+               //              maps a module name to a path
+               //      description: 
+               //              An unregistered module is given the default path of ../[module],
+               //              relative to Dojo root. For example, module acme is mapped to
+               //              ../acme.  If you want to use a different module name, use
+               //              dojo.registerModulePath. 
+               //      example:
+               //              If your dojo.js is located at this location in the web root:
+               //      |       /myapp/js/dojo/dojo/dojo.js
+               //              and your modules are located at:
+               //      |       /myapp/js/foo/bar.js
+               //      |       /myapp/js/foo/baz.js
+               //      |       /myapp/js/foo/thud/xyzzy.js
+               //              Your application can tell Dojo to locate the "foo" namespace by calling:
+               //      |       dojo.registerModulePath("foo", "../../foo");
+               //              At which point you can then use dojo.require() to load the
+               //              modules (assuming they provide() the same things which are
+               //              required). The full code might be:
+               //      |       <script type="text/javascript" 
+               //      |               src="/myapp/js/dojo/dojo/dojo.js"></script>
+               //      |       <script type="text/javascript">
+               //      |               dojo.registerModulePath("foo", "../../foo");
+               //      |               dojo.require("foo.bar");
+               //      |               dojo.require("foo.baz");
+               //      |               dojo.require("foo.thud.xyzzy");
+               //      |       </script>
+               d._modulePrefixes[module] = { name: module, value: prefix };
+       }
+
+       dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
+               // summary:
+               //              Declares translated resources and loads them if necessary, in the
+               //              same style as dojo.require.  Contents of the resource bundle are
+               //              typically strings, but may be any name/value pair, represented in
+               //              JSON format.  See also dojo.i18n.getLocalization.
+               // moduleName: 
+               //              name of the package containing the "nls" directory in which the
+               //              bundle is found
+               // bundleName: 
+               //              bundle name, i.e. the filename without the '.js' suffix
+               // locale: 
+               //              the locale to load (optional)  By default, the browser's user
+               //              locale as defined by dojo.locale
+               // availableFlatLocales: 
+               //              A comma-separated list of the available, flattened locales for this
+               //              bundle. This argument should only be set by the build process.
+               // description:
+               //              Load translated resource bundles provided underneath the "nls"
+               //              directory within a package.  Translated resources may be located in
+               //              different packages throughout the source tree.  For example, a
+               //              particular widget may define one or more resource bundles,
+               //              structured in a program as follows, where moduleName is
+               //              mycode.mywidget and bundleNames available include bundleone and
+               //              bundletwo:
+               //
+               //      |               ...
+               //      |               mycode/
+               //      |                mywidget/
+               //      |                 nls/
+               //      |                  bundleone.js (the fallback translation, English in this example)
+               //      |                  bundletwo.js (also a fallback translation)
+               //      |                  de/
+               //      |                   bundleone.js
+               //      |                   bundletwo.js
+               //      |                  de-at/
+               //      |                   bundleone.js
+               //      |                  en/
+               //      |                   (empty; use the fallback translation)
+               //      |                  en-us/
+               //      |                   bundleone.js
+               //      |                  en-gb/
+               //      |                   bundleone.js
+               //      |                  es/
+               //      |                   bundleone.js
+               //      |                   bundletwo.js
+               //      |                 ...etc
+               //      |               ...
+               //
+               //              Each directory is named for a locale as specified by RFC 3066,
+               //              (http://www.ietf.org/rfc/rfc3066.txt), normalized in lowercase.
+               //              Note that the two bundles in the example do not define all the
+               //              same variants.  For a given locale, bundles will be loaded for
+               //              that locale and all more general locales above it, including a
+               //              fallback at the root directory.  For example, a declaration for
+               //              the "de-at" locale will first load `nls/de-at/bundleone.js`,
+               //              then `nls/de/bundleone.js` and finally `nls/bundleone.js`.  The
+               //              data will be flattened into a single Object so that lookups
+               //              will follow this cascading pattern.  An optional build step can
+               //              preload the bundles to avoid data redundancy and the multiple
+               //              network hits normally required to load these resources.
+
+               d.require("dojo.i18n");
+               d.i18n._requireLocalization.apply(d.hostenv, arguments);
+       };
+
+
+       var ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$");
+       var ire = new RegExp("^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$");
+
+       dojo._Url = function(/*dojo._Url||String...*/){
+               // summary: 
+               //              Constructor to create an object representing a URL.
+               //              It is marked as private, since we might consider removing
+               //              or simplifying it.
+               // description: 
+               //              Each argument is evaluated in order relative to the next until
+               //              a canonical uri is produced. To get an absolute Uri relative to
+               //              the current document use:
+               //              new dojo._Url(document.baseURI, url)
+
+               var n = null;
+
+               // TODO: support for IPv6, see RFC 2732
+               var _a = arguments;
+               var uri = [_a[0]];
+               // resolve uri components relative to each other
+               for(var i = 1; i<_a.length; i++){
+                       if(!_a[i]){ continue; }
+
+                       // Safari doesn't support this.constructor so we have to be explicit
+                       // FIXME: Tracked (and fixed) in Webkit bug 3537.
+                       //              http://bugs.webkit.org/show_bug.cgi?id=3537
+                       var relobj = new d._Url(_a[i]+"");
+                       var uriobj = new d._Url(uri[0]+"");
+
+                       if(
+                               relobj.path == "" &&
+                               !relobj.scheme &&
+                               !relobj.authority &&
+                               !relobj.query
+                       ){
+                               if(relobj.fragment != n){
+                                       uriobj.fragment = relobj.fragment;
+                               }
+                               relobj = uriobj;
+                       }else if(!relobj.scheme){
+                               relobj.scheme = uriobj.scheme;
+
+                               if(!relobj.authority){
+                                       relobj.authority = uriobj.authority;
+
+                                       if(relobj.path.charAt(0) != "/"){
+                                               var path = uriobj.path.substring(0,
+                                                       uriobj.path.lastIndexOf("/") + 1) + relobj.path;
+
+                                               var segs = path.split("/");
+                                               for(var j = 0; j < segs.length; j++){
+                                                       if(segs[j] == "."){
+                                                               // flatten "./" references
+                                                               if(j == segs.length - 1){
+                                                                       segs[j] = "";
+                                                               }else{
+                                                                       segs.splice(j, 1);
+                                                                       j--;
+                                                               }
+                                                       }else if(j > 0 && !(j == 1 && segs[0] == "") &&
+                                                               segs[j] == ".." && segs[j-1] != ".."){
+                                                               // flatten "../" references
+                                                               if(j == (segs.length - 1)){
+                                                                       segs.splice(j, 1);
+                                                                       segs[j - 1] = "";
+                                                               }else{
+                                                                       segs.splice(j - 1, 2);
+                                                                       j -= 2;
+                                                               }
+                                                       }
+                                               }
+                                               relobj.path = segs.join("/");
+                                       }
+                               }
+                       }
+
+                       uri = [];
+                       if(relobj.scheme){ 
+                               uri.push(relobj.scheme, ":");
+                       }
+                       if(relobj.authority){
+                               uri.push("//", relobj.authority);
+                       }
+                       uri.push(relobj.path);
+                       if(relobj.query){
+                               uri.push("?", relobj.query);
+                       }
+                       if(relobj.fragment){
+                               uri.push("#", relobj.fragment);
+                       }
+               }
+
+               this.uri = uri.join("");
+
+               // break the uri into its main components
+               var r = this.uri.match(ore);
+
+               this.scheme = r[2] || (r[1] ? "" : n);
+               this.authority = r[4] || (r[3] ? "" : n);
+               this.path = r[5]; // can never be undefined
+               this.query = r[7] || (r[6] ? "" : n);
+               this.fragment  = r[9] || (r[8] ? "" : n);
+
+               if(this.authority != n){
+                       // server based naming authority
+                       r = this.authority.match(ire);
+
+                       this.user = r[3] || n;
+                       this.password = r[4] || n;
+                       this.host = r[5];
+                       this.port = r[7] || n;
+               }
+       }
+
+       dojo._Url.prototype.toString = function(){ return this.uri; };
+
+       dojo.moduleUrl = function(/*String*/module, /*dojo._Url||String*/url){
+               //      summary: 
+               //              Returns a `dojo._Url` object relative to a module.
+               //      example:
+               //      |       var pngPath = dojo.moduleUrl("acme","images/small.png");
+               //      |       console.dir(pngPath); // list the object properties
+               //      |       // create an image and set it's source to pngPath's value:
+               //      |       var img = document.createElement("img");
+               //      |       // NOTE: we assign the string representation of the url object
+               //      |       img.src = pngPath.toString(); 
+               //      |       // add our image to the document
+               //      |       dojo.body().appendChild(img);
+               //      example: 
+               //              you may de-reference as far as you like down the package
+               //              hierarchy.  This is sometimes handy to avoid lenghty relative
+               //              urls or for building portable sub-packages. In this example,
+               //              the `acme.widget` and `acme.util` directories may be located
+               //              under different roots (see `dojo.registerModulePath`) but the
+               //              the modules which reference them can be unaware of their
+               //              relative locations on the filesystem:
+               //      |       // somewhere in a configuration block
+               //      |       dojo.registerModulePath("acme.widget", "../../acme/widget");
+               //      |       dojo.registerModulePath("acme.util", "../../util");
+               //      |       
+               //      |       // ...
+               //      |       
+               //      |       // code in a module using acme resources
+               //      |       var tmpltPath = dojo.moduleUrl("acme.widget","templates/template.html");
+               //      |       var dataPath = dojo.moduleUrl("acme.util","resources/data.json");
+
+               var loc = d._getModuleSymbols(module).join('/');
+               if(!loc){ return null; }
+               if(loc.lastIndexOf("/") != loc.length-1){
+                       loc += "/";
+               }
+               
+               //If the path is an absolute path (starts with a / or is on another
+               //domain/xdomain) then don't add the baseUrl.
+               var colonIndex = loc.indexOf(":");
+               if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > loc.indexOf("/"))){
+                       loc = d.baseUrl + loc;
+               }
+
+               return new d._Url(loc, url); // String
+       }
+})();
+
+/*=====
+dojo.isBrowser = {
+       //      example:
+       //      |       if(dojo.isBrowser){ ... }
+};
+
+dojo.isFF = {
+       //      example:
+       //      |       if(dojo.isFF > 1){ ... }
+};
+
+dojo.isIE = {
+       // example:
+       //      |       if(dojo.isIE > 6){
+       //      |               // we are IE7
+       //      |       }
+};
+
+dojo.isSafari = {
+       //      example:
+       //      |       if(dojo.isSafari){ ... }
+       //      example: 
+       //              Detect iPhone:
+       //      |       if(dojo.isSafari && (navigator.userAgent.indexOf("iPhone") < 0)){ 
+       //      |               // we are iPhone. iPod touch reports "iPod" above
+       //      |       }
+};
+
+dojo = {
+       // isBrowser: Boolean
+       //              True if the client is a web-browser
+       isBrowser: true,
+       //      isFF: Number
+       //              Greater than zero if client is FireFox. 0 otherwise. Corresponds to
+       //              major detected FireFox version (1.5, 2, 3, etc.)
+       isFF: 2,
+       //      isIE: Number
+       //              Greater than zero if client is MSIE(PC). 0 otherwise. Corresponds to
+       //              major detected IE version (6, 7, 8, etc.)
+       isIE: 6,
+       //      isKhtml: Number
+       //              Greater than zero if client is a KTHML-derived browser (Konqueror,
+       //              Safari, etc.). 0 otherwise. Corresponds to major detected version.
+       isKhtml: 0,
+       //      isMozilla: Number
+       //              Greater than zero if client is a Mozilla-based browser (Firefox,
+       //              SeaMonkey). 0 otherwise. Corresponds to major detected version.
+       isMozilla: 0,
+       //      isOpera: Number
+       //              Greater than zero if client is Opera. 0 otherwise. Corresponds to
+       //              major detected version.
+       isOpera: 0,
+       //      isSafari: Number
+       //              Greater than zero if client is Safari or iPhone. 0 otherwise.
+       isSafari: 0
+}
+=====*/
+
+if(typeof window != 'undefined'){
+       dojo.isBrowser = true;
+       dojo._name = "browser";
+
+
+       // attempt to figure out the path to dojo if it isn't set in the config
+       (function(){
+               var d = dojo;
+               // this is a scope protection closure. We set browser versions and grab
+               // the URL we were loaded from here.
+
+               // grab the node we were loaded from
+               if(document && document.getElementsByTagName){
+                       var scripts = document.getElementsByTagName("script");
+                       var rePkg = /dojo(\.xd)?\.js(\W|$)/i;
+                       for(var i = 0; i < scripts.length; i++){
+                               var src = scripts[i].getAttribute("src");
+                               if(!src){ continue; }
+                               var m = src.match(rePkg);
+                               if(m){
+                                       // find out where we came from
+                                       if(!d.config.baseUrl){
+                                               d.config.baseUrl = src.substring(0, m.index);
+                                       }
+                                       // and find out if we need to modify our behavior
+                                       var cfg = scripts[i].getAttribute("djConfig");
+                                       if(cfg){
+                                               var cfgo = eval("({ "+cfg+" })");
+                                               for(var x in cfgo){
+                                                       dojo.config[x] = cfgo[x];
+                                               }
+                                       }
+                                       break; // "first Dojo wins"
+                               }
+                       }
+               }
+               d.baseUrl = d.config.baseUrl;
+
+               // fill in the rendering support information in dojo.render.*
+               var n = navigator;
+               var dua = n.userAgent;
+               var dav = n.appVersion;
+               var tv = parseFloat(dav);
+
+               d.isOpera = (dua.indexOf("Opera") >= 0) ? tv : 0;
+               // safari detection derived from:
+               //              http://developer.apple.com/internet/safari/faq.html#anchor2
+               //              http://developer.apple.com/internet/safari/uamatrix.html
+               var idx = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0);
+               if(idx){
+                       // try to grab the explicit Safari version first. If we don't get
+                       // one, look for 419.3+ as the indication that we're on something
+                       // "Safari 3-ish". Lastly, default to "Safari 2" handling.
+                       d.isSafari = parseFloat(dav.split("Version/")[1]) || ( ( parseFloat(dav.substr(idx+7)) >= 419.3 ) ? 3 : 2 ) || 2;
+               }
+               d.isAIR = (dua.indexOf("AdobeAIR") >= 0) ? 1 : 0;
+               d.isKhtml = (dav.indexOf("Konqueror") >= 0 || d.isSafari) ? tv : 0;
+               d.isMozilla = d.isMoz = (dua.indexOf("Gecko") >= 0 && !d.isKhtml) ? tv : 0;
+               d.isFF = d.isIE = 0;
+               if(d.isMoz){
+                       d.isFF = parseFloat(dua.split("Firefox/")[1]) || 0;
+               }
+               if(document.all && !d.isOpera){
+                       d.isIE = parseFloat(dav.split("MSIE ")[1]) || 0;
+               }
+
+               //Workaround to get local file loads of dojo to work on IE 7
+               //by forcing to not use native xhr.
+               if(dojo.isIE && window.location.protocol === "file:"){
+                       dojo.config.ieForceActiveXXhr=true;
+               }
+
+               var cm = document.compatMode;
+               d.isQuirks = cm == "BackCompat" || cm == "QuirksMode" || d.isIE < 6;
+
+               // TODO: is the HTML LANG attribute relevant?
+               d.locale = dojo.config.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase();
+
+               // These are in order of decreasing likelihood; this will change in time.
+               d._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
+
+               d._xhrObj = function(){
+                       // summary: 
+                       //              does the work of portably generating a new XMLHTTPRequest
+                       //              object.
+                       var http = null;
+                       var last_e = null;
+                       if(!dojo.isIE || !dojo.config.ieForceActiveXXhr){
+                               try{ http = new XMLHttpRequest(); }catch(e){}
+                       }
+                       if(!http){
+                               for(var i=0; i<3; ++i){
+                                       var progid = d._XMLHTTP_PROGIDS[i];
+                                       try{
+                                               http = new ActiveXObject(progid);
+                                       }catch(e){
+                                               last_e = e;
+                                       }
+
+                                       if(http){
+                                               d._XMLHTTP_PROGIDS = [progid];  // so faster next time
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if(!http){
+                               throw new Error("XMLHTTP not available: "+last_e);
+                       }
+
+                       return http; // XMLHTTPRequest instance
+               }
+
+               d._isDocumentOk = function(http){
+                       var stat = http.status || 0;
+                       return (stat >= 200 && stat < 300) ||   // Boolean
+                               stat == 304 ||                                          // allow any 2XX response code
+                               stat == 1223 ||                                                 // get it out of the cache
+                               (!stat && (location.protocol=="file:" || location.protocol=="chrome:") ); // Internet Explorer mangled the status code
+               }
+
+               //See if base tag is in use.
+               //This is to fix http://trac.dojotoolkit.org/ticket/3973,
+               //but really, we need to find out how to get rid of the dojo._Url reference
+               //below and still have DOH work with the dojo.i18n test following some other
+               //test that uses the test frame to load a document (trac #2757).
+               //Opera still has problems, but perhaps a larger issue of base tag support
+               //with XHR requests (hasBase is true, but the request is still made to document
+               //path, not base path).
+               var owloc = window.location+"";
+               var base = document.getElementsByTagName("base");
+               var hasBase = (base && base.length > 0);
+
+               d._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){
+                       // summary: Read the contents of the specified uri and return those contents.
+                       // uri:
+                       //              A relative or absolute uri. If absolute, it still must be in
+                       //              the same "domain" as we are.
+                       // fail_ok:
+                       //              Default false. If fail_ok and loading fails, return null
+                       //              instead of throwing.
+                       // returns: The response text. null is returned when there is a
+                       //              failure and failure is okay (an exception otherwise)
+
+                       // alert("_getText: " + uri);
+
+                       // NOTE: must be declared before scope switches ie. this._xhrObj()
+                       var http = this._xhrObj();
+
+                       if(!hasBase && dojo._Url){
+                               uri = (new dojo._Url(owloc, uri)).toString();
+                       }
+                       /*
+                       console.debug("_getText:", uri);
+                       console.debug(window.location+"");
+                       alert(uri);
+                       */
+
+                       if(d.config.cacheBust){
+                               uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(d.config.cacheBust).replace(/\W+/g,"");
+                       }
+
+                       http.open('GET', uri, false);
+                       try{
+                               http.send(null);
+                               // alert(http);
+                               if(!d._isDocumentOk(http)){
+                                       var err = Error("Unable to load "+uri+" status:"+ http.status);
+                                       err.status = http.status;
+                                       err.responseText = http.responseText;
+                                       throw err;
+                               }
+                       }catch(e){
+                               if(fail_ok){ return null; } // null
+                               // rethrow the exception
+                               throw e;
+                       }
+                       return http.responseText; // String
+               }
+       })();
+
+       dojo._initFired = false;
+       //      BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
+       dojo._loadInit = function(e){
+               dojo._initFired = true;
+               // allow multiple calls, only first one will take effect
+               // A bug in khtml calls events callbacks for document for event which isnt supported
+               // for example a created contextmenu event calls DOMContentLoaded, workaround
+               var type = (e && e.type) ? e.type.toLowerCase() : "load";
+               if(arguments.callee.initialized || (type != "domcontentloaded" && type != "load")){ return; }
+               arguments.callee.initialized = true;
+               if("_khtmlTimer" in dojo){
+                       clearInterval(dojo._khtmlTimer);
+                       delete dojo._khtmlTimer;
+               }
+
+               if(dojo._inFlightCount == 0){
+                       dojo._modulesLoaded();
+               }
+       }
+
+       dojo._fakeLoadInit = function(){
+               dojo._loadInit({type: "load"});
+       }
+
+       if(!dojo.config.afterOnLoad){
+               //      START DOMContentLoaded
+               // Mozilla and Opera 9 expose the event we could use
+               if(document.addEventListener){
+                       // NOTE: 
+                       //              due to a threading issue in Firefox 2.0, we can't enable
+                       //              DOMContentLoaded on that platform. For more information, see:
+                       //              http://trac.dojotoolkit.org/ticket/1704
+                       if(dojo.isOpera || dojo.isFF >= 3 || (dojo.isMoz && dojo.config.enableMozDomContentLoaded === true)){
+                               document.addEventListener("DOMContentLoaded", dojo._loadInit, null);
+                       }
+       
+                       //      mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
+                       //  also used for Mozilla because of trac #1640
+                       window.addEventListener("load", dojo._loadInit, null);
+               }
+       
+               if(dojo.isAIR){
+                       window.addEventListener("load", dojo._loadInit, null);
+               }else if(/(WebKit|khtml)/i.test(navigator.userAgent)){ // sniff
+                       dojo._khtmlTimer = setInterval(function(){
+                               if(/loaded|complete/.test(document.readyState)){
+                                       dojo._loadInit(); // call the onload handler
+                               }
+                       }, 10);
+               }
+               //      END DOMContentLoaded
+       }
+
+       (function(){
+               var _w = window;
+               var _handleNodeEvent = function(/*String*/evtName, /*Function*/fp){
+                       // summary:
+                       //              non-destructively adds the specified function to the node's
+                       //              evtName handler.
+                       // evtName: should be in the form "onclick" for "onclick" handlers.
+                       // Make sure you pass in the "on" part.
+                       var oldHandler = _w[evtName] || function(){};
+                       _w[evtName] = function(){
+                               fp.apply(_w, arguments);
+                               oldHandler.apply(_w, arguments);
+                       };
+               };
+
+               if(dojo.isIE){
+                       //      for Internet Explorer. readyState will not be achieved on init
+                       //      call, but dojo doesn't need it however, we'll include it
+                       //      because we don't know if there are other functions added that
+                       //      might.  Note that this has changed because the build process
+                       //      strips all comments -- including conditional ones.
+                       if(!dojo.config.afterOnLoad){
+                               document.write('<scr'+'ipt defer src="//:" '
+                                       + 'onreadystatechange="if(this.readyState==\'complete\'){' + dojo._scopeName + '._loadInit();}">'
+                                       + '</scr'+'ipt>'
+                               );
+                       }
+
+                       // IE WebControl hosted in an application can fire "beforeunload" and "unload"
+                       // events when control visibility changes, causing Dojo to unload too soon. The
+                       // following code fixes the problem
+                       // Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155
+                       var _unloading = true;
+                       _handleNodeEvent("onbeforeunload", function(){
+                               _w.setTimeout(function(){ _unloading = false; }, 0);
+                       });
+                       _handleNodeEvent("onunload", function(){
+                               if(_unloading){ dojo.unloaded(); }
+                       });
+
+                       try{
+                               document.namespaces.add("v","urn:schemas-microsoft-com:vml");
+                               document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
+                       }catch(e){}
+               }else{
+                       // FIXME: dojo.unloaded requires dojo scope, so using anon function wrapper.
+                       _handleNodeEvent("onbeforeunload", function() { dojo.unloaded(); });
+               }
+
+       })();
+
+       /*
+       OpenAjax.subscribe("OpenAjax", "onload", function(){
+               if(dojo._inFlightCount == 0){
+                       dojo._modulesLoaded();
+               }
+       });
+
+       OpenAjax.subscribe("OpenAjax", "onunload", function(){
+               dojo.unloaded();
+       });
+       */
+} //if (typeof window != 'undefined')
+
+//Register any module paths set up in djConfig. Need to do this
+//in the hostenvs since hostenv_browser can read djConfig from a
+//script tag's attribute.
+(function(){
+       var mp = dojo.config["modulePaths"];
+       if(mp){
+               for(var param in mp){
+                       dojo.registerModulePath(param, mp[param]);
+               }
+       }
+})();
+
+//Load debug code if necessary.
+if(dojo.config.isDebug){
+       dojo.require("dojo._firebug.firebug");
+}
+
+if(dojo.config.debugAtAllCosts){
+       dojo.config.useXDomain = true;
+       dojo.require("dojo._base._loader.loader_xd");
+       dojo.require("dojo._base._loader.loader_debug");
+       dojo.require("dojo.i18n");
+}
+
+if(!dojo._hasResource["dojo._base.lang"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.lang"] = true;
+dojo.provide("dojo._base.lang");
+
+// Crockford (ish) functions
+
+dojo.isString = function(/*anything*/ it){
+       //      summary:
+       //              Return true if it is a String
+       return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean
+}
+
+dojo.isArray = function(/*anything*/ it){
+       //      summary:
+       //              Return true if it is an Array
+       return it && (it instanceof Array || typeof it == "array"); // Boolean
+}
+
+/*=====
+dojo.isFunction = function(it){
+       // summary: Return true if it is a Function
+       // it: anything
+       //      return: Boolean
+}
+=====*/
+
+dojo.isFunction = (function(){
+       var _isFunction = function(/*anything*/ it){
+               return it && (typeof it == "function" || it instanceof Function); // Boolean
+       };
+
+       return dojo.isSafari ?
+               // only slow this down w/ gratuitious casting in Safari since it's what's b0rken
+               function(/*anything*/ it){
+                       if(typeof it == "function" && it == "[object NodeList]"){ return false; }
+                       return _isFunction(it); // Boolean
+               } : _isFunction;
+})();
+
+dojo.isObject = function(/*anything*/ it){
+       // summary: 
+       //              Returns true if it is a JavaScript object (or an Array, a Function
+       //              or null)
+       return it !== undefined &&
+               (it === null || typeof it == "object" || dojo.isArray(it) || dojo.isFunction(it)); // Boolean
+}
+
+dojo.isArrayLike = function(/*anything*/ it){
+       //      summary:
+       //              similar to dojo.isArray() but more permissive
+       //      description:
+       //              Doesn't strongly test for "arrayness".  Instead, settles for "isn't
+       //              a string or number and has a length property". Arguments objects
+       //              and DOM collections will return true when passed to
+       //              dojo.isArrayLike(), but will return false when passed to
+       //              dojo.isArray().
+       //      return:
+       //              If it walks like a duck and quicks like a duck, return `true`
+       var d = dojo;
+       return it && it !== undefined &&
+               // keep out built-in constructors (Number, String, ...) which have length
+               // properties
+               !d.isString(it) && !d.isFunction(it) &&
+               !(it.tagName && it.tagName.toLowerCase() == 'form') &&
+               (d.isArray(it) || isFinite(it.length)); // Boolean
+}
+
+dojo.isAlien = function(/*anything*/ it){
+       // summary: 
+       //              Returns true if it is a built-in function or some other kind of
+       //              oddball that *should* report as a function but doesn't
+       return it && !dojo.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
+}
+
+dojo.extend = function(/*Object*/ constructor, /*Object...*/ props){
+       // summary:
+       //              Adds all properties and methods of props to constructor's
+       //              prototype, making them available to all instances created with
+       //              constructor.
+       for(var i=1, l=arguments.length; i<l; i++){
+               dojo._mixin(constructor.prototype, arguments[i]);
+       }
+       return constructor; // Object
+}
+
+dojo._hitchArgs = function(scope, method /*,...*/){
+       var pre = dojo._toArray(arguments, 2);
+       var named = dojo.isString(method);
+       return function(){
+               // arrayify arguments
+               var args = dojo._toArray(arguments);
+               // locate our method
+               var f = named ? (scope||dojo.global)[method] : method;
+               // invoke with collected args
+               return f && f.apply(scope || this, pre.concat(args)); // mixed
+       } // Function
+}
+
+dojo.hitch = function(/*Object*/scope, /*Function|String*/method /*,...*/){
+       //      summary: 
+       //              Returns a function that will only ever execute in the a given scope. 
+       //              This allows for easy use of object member functions
+       //              in callbacks and other places in which the "this" keyword may
+       //              otherwise not reference the expected scope. 
+       //              Any number of default positional arguments may be passed as parameters 
+       //              beyond "method".
+       //              Each of these values will be used to "placehold" (similar to curry)
+       //              for the hitched function. 
+       //      scope: 
+       //              The scope to use when method executes. If method is a string, 
+       //              scope is also the object containing method.
+       //      method:
+       //              A function to be hitched to scope, or the name of the method in
+       //              scope to be hitched.
+       //      example:
+       //      |       dojo.hitch(foo, "bar")(); 
+       //              runs foo.bar() in the scope of foo
+       //      example:
+       //      |       dojo.hitch(foo, myFunction);
+       //              returns a function that runs myFunction in the scope of foo
+       if(arguments.length > 2){
+               return dojo._hitchArgs.apply(dojo, arguments); // Function
+       }
+       if(!method){
+               method = scope;
+               scope = null;
+       }
+       if(dojo.isString(method)){
+               scope = scope || dojo.global;
+               if(!scope[method]){ throw(['dojo.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); }
+               return function(){ return scope[method].apply(scope, arguments || []); }; // Function
+       }
+       return !scope ? method : function(){ return method.apply(scope, arguments || []); }; // Function
+}
+
+/*=====
+dojo.delegate = function(obj, props){
+       //      summary:
+       //              returns a new object which "looks" to obj for properties which it
+       //              does not have a value for. Optionally takes a bag of properties to
+       //              seed the returned object with initially. 
+       //      description:
+       //              This is a small implementaton of the Boodman/Crockford delegation
+       //              pattern in JavaScript. An intermediate object constructor mediates
+       //              the prototype chain for the returned object, using it to delegate
+       //              down to obj for property lookup when object-local lookup fails.
+       //              This can be thought of similarly to ES4's "wrap", save that it does
+       //              not act on types but rather on pure objects.
+       //      obj:
+       //              The object to delegate to for properties not found directly on the
+       //              return object or in props.
+       //      props:
+       //              an object containing properties to assign to the returned object
+       //      returns:
+       //              an Object of anonymous type
+       //      example:
+       //      |       var foo = { bar: "baz" };
+       //      |       var thinger = dojo.delegate(foo, { thud: "xyzzy"});
+       //      |       thinger.bar == "baz"; // delegated to foo
+       //      |       foo.thud == undefined; // by definition
+       //      |       thinger.thud == "xyzzy"; // mixed in from props
+       //      |       foo.bar = "thonk";
+       //      |       thinger.bar == "thonk"; // still delegated to foo's bar
+}
+=====*/
+
+
+dojo.delegate = dojo._delegate = function(obj, props){
+
+       // boodman/crockford delegation
+       function TMP(){};
+       TMP.prototype = obj;
+       var tmp = new TMP();
+       if(props){
+               dojo.mixin(tmp, props);
+       }
+       return tmp; // Object
+}
+
+dojo.partial = function(/*Function|String*/method /*, ...*/){
+       //      summary:
+       //              similar to hitch() except that the scope object is left to be
+       //              whatever the execution context eventually becomes.
+       //      description:
+       //              Calling dojo.partial is the functional equivalent of calling:
+       //              |       dojo.hitch(null, funcName, ...);
+       var arr = [ null ];
+       return dojo.hitch.apply(dojo, arr.concat(dojo._toArray(arguments))); // Function
+}
+
+dojo._toArray = function(/*Object*/obj, /*Number?*/offset, /*Array?*/ startWith){
+       //      summary:
+       //              Converts an array-like object (i.e. arguments, DOMCollection) to an
+       //              array. Returns a new Array with the elements of obj.
+       //      obj:
+       //              the object to "arrayify". We expect the object to have, at a
+       //              minimum, a length property which corresponds to integer-indexed
+       //              properties.
+       //      offset:
+       //              the location in obj to start iterating from. Defaults to 0.
+       //              Optional.
+       //      startWith:
+       //              An array to pack with the properties of obj. If provided,
+       //              properties in obj are appended at the end of startWith and
+       //              startWith is the returned array.
+       var arr = startWith||[];
+       for(var x = offset || 0; x < obj.length; x++){
+               arr.push(obj[x]);
+       }
+       return arr; // Array
+}
+
+dojo.clone = function(/*anything*/ o){
+       // summary:
+       //              Clones objects (including DOM nodes) and all children.
+       //              Warning: do not clone cyclic structures.
+       if(!o){ return o; }
+       if(dojo.isArray(o)){
+               var r = [];
+               for(var i = 0; i < o.length; ++i){
+                       r.push(dojo.clone(o[i]));
+               }
+               return r; // Array
+       }
+       if(!dojo.isObject(o)){
+               return o;       /*anything*/
+       }
+       if(o.nodeType && o.cloneNode){ // isNode
+               return o.cloneNode(true); // Node
+       }
+       if(o instanceof Date){
+               return new Date(o.getTime());   // Date
+       }
+       // Generic objects
+       var r = new o.constructor(); // specific to dojo.declare()'d classes!
+       for(var i in o){
+               if(!(i in r) || r[i] != o[i]){
+                       r[i] = dojo.clone(o[i]);
+               }
+       }
+       return r; // Object
+}
+
+dojo.trim = function(/*String*/ str){
+       // summary: 
+       //              trims whitespaces from both sides of the string
+       // description:
+       //              This version of trim() was selected for inclusion into the base due
+       //              to its compact size and relatively good performance (see Steven
+       //              Levithan's blog:
+       //              http://blog.stevenlevithan.com/archives/faster-trim-javascript).
+       //              The fastest but longest version of this function is located at
+       //              dojo.string.trim()
+       return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // String
+}
+
+}
+
+if(!dojo._hasResource["dojo._base.declare"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.declare"] = true;
+dojo.provide("dojo._base.declare");
+
+
+// this file courtesy of the TurboAjax group, licensed under a Dojo CLA
+
+dojo.declare = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){
+       //      summary: 
+       //              Create a feature-rich constructor from compact notation
+       //      className:
+       //              The name of the constructor (loosely, a "class")
+       //              stored in the "declaredClass" property in the created prototype
+       //      superclass:
+       //              May be null, a Function, or an Array of Functions. If an array, 
+       //              the first element is used as the prototypical ancestor and
+       //              any following Functions become mixin ancestors.
+       //      props:
+       //              An object whose properties are copied to the
+       //              created prototype.
+       //              Add an instance-initialization function by making it a property 
+       //              named "constructor".
+       //      description:
+       //              Create a constructor using a compact notation for inheritance and
+       //              prototype extension. 
+       //
+       //              All superclasses (including mixins) must be Functions (not simple Objects).
+       //
+       //              Mixin ancestors provide a type of multiple inheritance. Prototypes of mixin 
+       //              ancestors are copied to the new class: changes to mixin prototypes will
+       //              not affect classes to which they have been mixed in.
+       //
+       //              "className" is cached in "declaredClass" property of the new class.
+       //
+       //      example:
+       //      |       dojo.declare("my.classes.bar", my.classes.foo, {
+       //      |               // properties to be added to the class prototype
+       //      |               someValue: 2,
+       //      |               // initialization function
+       //      |               constructor: function(){
+       //      |                       this.myComplicatedObject = new ReallyComplicatedObject(); 
+       //      |               },
+       //      |               // other functions
+       //      |               someMethod: function(){ 
+       //      |                       doStuff(); 
+       //      |               }
+       //      |       );
+
+       // process superclass argument
+       // var dd=dojo.declare, mixins=null;
+       var dd = arguments.callee, mixins;
+       if(dojo.isArray(superclass)){
+               mixins = superclass;
+               superclass = mixins.shift();
+       }
+       // construct intermediate classes for mixins
+       if(mixins){
+               dojo.forEach(mixins, function(m){
+                       if(!m){ throw(className + ": mixin #" + i + " is null"); } // It's likely a required module is not loaded
+                       superclass = dd._delegate(superclass, m);
+               });
+       }
+       // prepare values
+       var init = (props||0).constructor, ctor = dd._delegate(superclass), fn;
+       // name methods (experimental)
+       for(var i in props){ if(dojo.isFunction(fn = props[i]) && !0[i]){fn.nom = i;} } // 0[i] checks Object.prototype
+       // decorate prototype
+       dojo.extend(ctor, {declaredClass: className, _constructor: init, preamble: null}, props || 0); 
+       // special help for IE
+       ctor.prototype.constructor = ctor;
+       // create named reference
+       return dojo.setObject(className, ctor); // Function
+};
+
+dojo.mixin(dojo.declare, {
+       _delegate: function(base, mixin){
+               var bp = (base||0).prototype, mp = (mixin||0).prototype;
+               // fresh constructor, fresh prototype
+               var ctor = dojo.declare._makeCtor();
+               // cache ancestry
+               dojo.mixin(ctor, {superclass: bp, mixin: mp, extend: dojo.declare._extend});
+               // chain prototypes
+               if(base){ctor.prototype = dojo._delegate(bp);}
+               // add mixin and core
+               dojo.extend(ctor, dojo.declare._core, mp||0, {_constructor: null, preamble: null});
+               // special help for IE
+               ctor.prototype.constructor = ctor;
+               // name this class for debugging
+               ctor.prototype.declaredClass = (bp||0).declaredClass + '_' + (mp||0).declaredClass;
+               return ctor;
+       },
+       _extend: function(props){
+               for(var i in props){ if(dojo.isFunction(fn=props[i]) && !0[i]){fn.nom=i;} }
+               dojo.extend(this, props);
+       },
+       _makeCtor: function(){
+               // we have to make a function, but don't want to close over anything
+               return function(){ this._construct(arguments); };
+       },
+       _core: { 
+               _construct: function(args){
+                       var c=args.callee, s=c.superclass, ct=s&&s.constructor, m=c.mixin, mct=m&&m.constructor, a=args, ii, fn;
+                       // side-effect of = used on purpose here, lint may complain, don't try this at home
+                       if(a[0]){ 
+                               // FIXME: preambles for each mixin should be allowed
+                               // FIXME: 
+                               //              should we allow the preamble here NOT to modify the
+                               //              default args, but instead to act on each mixin
+                               //              independently of the class instance being constructed
+                               //              (for impedence matching)?
+
+                               // allow any first argument w/ a "preamble" property to act as a
+                               // class preamble (not exclusive of the prototype preamble)
+                               if(/*dojo.isFunction*/((fn = a[0].preamble))){ 
+                                       a = fn.apply(this, a) || a; 
+                               }
+                       } 
+                       // prototype preamble
+                       if((fn = c.prototype.preamble)){a = fn.apply(this, a) || a;}
+                       // FIXME: 
+                       //              need to provide an optional prototype-settable
+                       //              "_explicitSuper" property which disables this
+                       // initialize superclass
+                       if(ct&&ct.apply){ct.apply(this, a);}
+                       // initialize mixin
+                       if(mct&&mct.apply){mct.apply(this, a);}
+                       // initialize self
+                       if((ii=c.prototype._constructor)){ii.apply(this, args);}
+                       // post construction
+                       if(this.constructor.prototype==c.prototype && (ct=this.postscript)){ ct.apply(this, args); }
+               },
+               _findMixin: function(mixin){
+                       var c = this.constructor, p, m;
+                       while(c){
+                               p = c.superclass;
+                               m = c.mixin;
+                               if(m==mixin || (m instanceof mixin.constructor)){return p;}
+                               if(m && (m=m._findMixin(mixin))){return m;}
+                               c = p && p.constructor;
+                       }
+               },
+               _findMethod: function(name, method, ptype, has){
+                       // consciously trading readability for bytes and speed in this low-level method
+                       var p=ptype, c, m, f;
+                       do{
+                               c = p.constructor;
+                               m = c.mixin;
+                               // find method by name in our mixin ancestor
+                               if(m && (m=this._findMethod(name, method, m, has))){return m;}
+                               // if we found a named method that either exactly-is or exactly-is-not 'method'
+                               if((f=p[name])&&(has==(f==method))){return p;}
+                               // ascend chain
+                               p = c.superclass;
+                       }while(p);
+                       // if we couldn't find an ancestor in our primary chain, try a mixin chain
+                       return !has && (p=this._findMixin(ptype)) && this._findMethod(name, method, p, has);
+               },
+               inherited: function(name, args, newArgs){
+                       // optionalize name argument (experimental)
+                       var a = arguments;
+                       if(!dojo.isString(a[0])){newArgs=args; args=name; name=args.callee.nom;}
+                       a = newArgs||args;
+                       var c = args.callee, p = this.constructor.prototype, fn, mp;
+                       // if not an instance override 
+                       if(this[name] != c || p[name] == c){
+                               mp = this._findMethod(name, c, p, true);
+                               if(!mp){throw(this.declaredClass + ': inherited method "' + name + '" mismatch');}
+                               p = this._findMethod(name, c, mp, false);
+                       }
+                       fn = p && p[name];
+                       if(!fn){throw(mp.declaredClass + ': inherited method "' + name + '" not found');}
+                       // if the function exists, invoke it in our scope
+                       return fn.apply(this, a);
+               }
+       }
+});
+
+}
+
+if(!dojo._hasResource["dojo._base.connect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.connect"] = true;
+dojo.provide("dojo._base.connect");
+
+
+// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
+
+// low-level delegation machinery
+dojo._listener = {
+       // create a dispatcher function
+       getDispatcher: function(){
+               // following comments pulled out-of-line to prevent cloning them 
+               // in the returned function.
+               // - indices (i) that are really in the array of listeners (ls) will 
+               //   not be in Array.prototype. This is the 'sparse array' trick
+               //   that keeps us safe from libs that take liberties with built-in 
+               //   objects
+               // - listener is invoked with current scope (this)
+               return function(){
+                       var ap=Array.prototype, c=arguments.callee, ls=c._listeners, t=c.target;
+                       // return value comes from original target function
+                       var r=t && t.apply(this, arguments);
+                       // invoke listeners after target function
+                       for(var i in ls){
+                               if(!(i in ap)){
+                                       ls[i].apply(this, arguments);
+                               }
+                       }
+                       // return value comes from original target function
+                       return r;
+               }
+       },
+       // add a listener to an object
+       add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
+               // Whenever 'method' is invoked, 'listener' will have the same scope.
+               // Trying to supporting a context object for the listener led to 
+               // complexity. 
+               // Non trivial to provide 'once' functionality here
+               // because listener could be the result of a dojo.hitch call,
+               // in which case two references to the same hitch target would not
+               // be equivalent. 
+               source = source || dojo.global;
+               // The source method is either null, a dispatcher, or some other function
+               var f = source[method];
+               // Ensure a dispatcher
+               if(!f||!f._listeners){
+                       var d = dojo._listener.getDispatcher();
+                       // original target function is special
+                       d.target = f;
+                       // dispatcher holds a list of listeners
+                       d._listeners = []; 
+                       // redirect source to dispatcher
+                       f = source[method] = d;
+               }
+               // The contract is that a handle is returned that can 
+               // identify this listener for disconnect. 
+               //
+               // The type of the handle is private. Here is it implemented as Integer. 
+               // DOM event code has this same contract but handle is Function 
+               // in non-IE browsers.
+               //
+               // We could have separate lists of before and after listeners.
+               return f._listeners.push(listener) ; /*Handle*/
+       },
+       // remove a listener from an object
+       remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
+               var f = (source||dojo.global)[method];
+               // remember that handle is the index+1 (0 is not a valid handle)
+               if(f && f._listeners && handle--){
+                       delete f._listeners[handle]; 
+               }
+       }
+};
+
+// Multiple delegation for arbitrary methods.
+
+// This unit knows nothing about DOM, 
+// but we include DOM aware 
+// documentation and dontFix
+// argument here to help the autodocs.
+// Actual DOM aware code is in event.js.
+
+dojo.connect = function(/*Object|null*/ obj, 
+                                               /*String*/ event, 
+                                               /*Object|null*/ context, 
+                                               /*String|Function*/ method,
+                                               /*Boolean*/ dontFix){
+       // summary:
+       //              Create a link that calls one function when another executes. 
+       //
+       // description:
+       //              Connects method to event, so that after event fires, method
+       //              does too. All connected functions are passed the same arguments as
+       //              the event function was initially called with. You may connect as
+       //              many methods to event as needed.
+       //
+       //              event must be a string. If obj is null, dojo.global is used.
+       //
+       //              null arguments may simply be omitted.
+       //
+       //              obj[event] can resolve to a function or undefined (null). 
+       //              If obj[event] is null, it is assigned a function.
+       //
+       //              The return value is a handle that is needed to 
+       //              remove this connection with dojo.disconnect.
+       //
+       // obj: 
+       //              The source object for the event function. 
+       //              Defaults to dojo.global if null.
+       //              If obj is a DOM node, the connection is delegated 
+       //              to the DOM event manager (unless dontFix is true).
+       //
+       // event:
+       //              String name of the event function in obj. 
+       //              I.e. identifies a property obj[event].
+       //
+       // context: 
+       //              The object that method will receive as "this".
+       //
+       //              If context is null and method is a function, then method
+       //              inherits the context of event.
+       //      
+       //              If method is a string then context must be the source 
+       //              object object for method (context[method]). If context is null,
+       //              dojo.global is used.
+       //
+       // method:
+       //              A function reference, or name of a function in context. 
+       //              The function identified by method fires after event does. 
+       //              method receives the same arguments as the event.
+       //              See context argument comments for information on method's scope.
+       //
+       // dontFix:
+       //              If obj is a DOM node, set dontFix to true to prevent delegation 
+       //              of this connection to the DOM event manager. 
+       //
+       // example:
+       //              When obj.onchange(), do ui.update():
+       //      |       dojo.connect(obj, "onchange", ui, "update");
+       //      |       dojo.connect(obj, "onchange", ui, ui.update); // same
+       //
+       // example:
+       //              Using return value for disconnect:
+       //      |       var link = dojo.connect(obj, "onchange", ui, "update");
+       //      |       ...
+       //      |       dojo.disconnect(link);
+       //
+       // example:
+       //              When onglobalevent executes, watcher.handler is invoked:
+       //      |       dojo.connect(null, "onglobalevent", watcher, "handler");
+       //
+       // example:
+       //              When ob.onCustomEvent executes, customEventHandler is invoked:
+       //      |       dojo.connect(ob, "onCustomEvent", null, "customEventHandler");
+       //      |       dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same
+       //
+       // example:
+       //              When ob.onCustomEvent executes, customEventHandler is invoked
+       //              with the same scope (this):
+       //      |       dojo.connect(ob, "onCustomEvent", null, customEventHandler);
+       //      |       dojo.connect(ob, "onCustomEvent", customEventHandler); // same
+       //
+       // example:
+       //              When globalEvent executes, globalHandler is invoked
+       //              with the same scope (this):
+       //      |       dojo.connect(null, "globalEvent", null, globalHandler);
+       //      |       dojo.connect("globalEvent", globalHandler); // same
+
+       // normalize arguments
+       var a=arguments, args=[], i=0;
+       // if a[0] is a String, obj was ommited
+       args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]);
+       // if the arg-after-next is a String or Function, context was NOT omitted
+       var a1 = a[i+1];
+       args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]);
+       // absorb any additional arguments
+       for(var l=a.length; i<l; i++){  args.push(a[i]); }
+       // do the actual work
+       return dojo._connect.apply(this, args); /*Handle*/
+}
+
+// used by non-browser hostenvs. always overriden by event.js
+dojo._connect = function(obj, event, context, method){
+       var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method)); 
+       return [obj, event, h, l]; // Handle
+}
+
+dojo.disconnect = function(/*Handle*/ handle){
+       // summary:
+       //              Remove a link created by dojo.connect.
+       // description:
+       //              Removes the connection between event and the method referenced by handle.
+       // handle:
+       //              the return value of the dojo.connect call that created the connection.
+       if(handle && handle[0] !== undefined){
+               dojo._disconnect.apply(this, handle);
+               // let's not keep this reference
+               delete handle[0];
+       }
+}
+
+dojo._disconnect = function(obj, event, handle, listener){
+       listener.remove(obj, event, handle);
+}
+
+// topic publish/subscribe
+
+dojo._topics = {};
+
+dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){
+       //      summary:
+       //              Attach a listener to a named topic. The listener function is invoked whenever the
+       //              named topic is published (see: dojo.publish).
+       //              Returns a handle which is needed to unsubscribe this listener.
+       //      context:
+       //              Scope in which method will be invoked, or null for default scope.
+       //      method:
+       //              The name of a function in context, or a function reference. This is the function that
+       //              is invoked when topic is published.
+       //      example:
+       //      |       dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
+       //      |       dojo.publish("alerts", [ "read this", "hello world" ]);                                                                                                                                 
+
+       // support for 2 argument invocation (omitting context) depends on hitch
+       return [topic, dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method))]; /*Handle*/
+}
+
+dojo.unsubscribe = function(/*Handle*/ handle){
+       //      summary:
+       //              Remove a topic listener. 
+       //      handle:
+       //              The handle returned from a call to subscribe.
+       //      example:
+       //      |       var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
+       //      |       ...
+       //      |       dojo.unsubscribe(alerter);
+       if(handle){
+               dojo._listener.remove(dojo._topics, handle[0], handle[1]);
+       }
+}
+
+dojo.publish = function(/*String*/ topic, /*Array*/ args){
+       //      summary:
+       //              Invoke all listener method subscribed to topic.
+       //      topic:
+       //              The name of the topic to publish.
+       //      args:
+       //              An array of arguments. The arguments will be applied 
+       //              to each topic subscriber (as first class parameters, via apply).
+       //      example:
+       //      |       dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
+       //      |       dojo.publish("alerts", [ "read this", "hello world" ]); 
+
+       // Note that args is an array, which is more efficient vs variable length
+       // argument list.  Ideally, var args would be implemented via Array
+       // throughout the APIs.
+       var f = dojo._topics[topic];
+       if(f){
+               f.apply(this, args||[]);
+       }
+}
+
+dojo.connectPublisher = function(      /*String*/ topic, 
+                                                                       /*Object|null*/ obj, 
+                                                                       /*String*/ event){
+       //      summary:
+       //              Ensure that everytime obj.event() is called, a message is published
+       //              on the topic. Returns a handle which can be passed to
+       //              dojo.disconnect() to disable subsequent automatic publication on
+       //              the topic.
+       //      topic:
+       //              The name of the topic to publish.
+       //      obj: 
+       //              The source object for the event function. Defaults to dojo.global
+       //              if null.
+       //      event:
+       //              The name of the event function in obj. 
+       //              I.e. identifies a property obj[event].
+       //      example:
+       //      |       dojo.connectPublisher("/ajax/start", dojo, "xhrGet"};
+       var pf = function(){ dojo.publish(topic, arguments); }
+       return (event) ? dojo.connect(obj, event, pf) : dojo.connect(obj, pf); //Handle
+};
+
+}
+
+if(!dojo._hasResource["dojo._base.Deferred"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.Deferred"] = true;
+dojo.provide("dojo._base.Deferred");
+
+
+dojo.Deferred = function(/*Function?*/ canceller){
+       // summary:
+       //              Encapsulates a sequence of callbacks in response to a value that
+       //              may not yet be available.  This is modeled after the Deferred class
+       //              from Twisted <http://twistedmatrix.com>.
+       // description:
+       //              JavaScript has no threads, and even if it did, threads are hard.
+       //              Deferreds are a way of abstracting non-blocking events, such as the
+       //              final response to an XMLHttpRequest. Deferreds create a promise to
+       //              return a response a some point in the future and an easy way to
+       //              register your interest in receiving that response.
+       //
+       //              The most important methods for Deffered users are:
+       //
+       //                      * addCallback(handler)
+       //                      * addErrback(handler)
+       //                      * callback(result)
+       //                      * errback(result)
+       //
+       //              In general, when a function returns a Deferred, users then "fill
+       //              in" the second half of the contract by registering callbacks and
+       //              error handlers. You may register as many callback and errback
+       //              handlers as you like and they will be executed in the order
+       //              registered when a result is provided. Usually this result is
+       //              provided as the result of an asynchronous operation. The code
+       //              "managing" the Deferred (the code that made the promise to provide
+       //              an answer later) will use the callback() and errback() methods to
+       //              communicate with registered listeners about the result of the
+       //              operation. At this time, all registered result handlers are called
+       //              *with the most recent result value*.
+       //
+       //              Deferred callback handlers are treated as a chain, and each item in
+       //              the chain is required to return a value that will be fed into
+       //              successive handlers. The most minimal callback may be registered
+       //              like this:
+       //
+       //              |       var d = new dojo.Deferred();
+       //              |       d.addCallback(function(result){ return result; });
+       //
+       //              Perhaps the most common mistake when first using Deferreds is to
+       //              forget to return a value (in most cases, the value you were
+       //              passed).
+       //
+       //              The sequence of callbacks is internally represented as a list of
+       //              2-tuples containing the callback/errback pair.  For example, the
+       //              following call sequence:
+       //              
+       //              |       var d = new dojo.Deferred();
+       //              |       d.addCallback(myCallback);
+       //              |       d.addErrback(myErrback);
+       //              |       d.addBoth(myBoth);
+       //              |       d.addCallbacks(myCallback, myErrback);
+       //
+       //              is translated into a Deferred with the following internal
+       //              representation:
+       //
+       //              |       [
+       //              |               [myCallback, null],
+       //              |               [null, myErrback],
+       //              |               [myBoth, myBoth],
+       //              |               [myCallback, myErrback]
+       //              |       ]
+       //
+       //              The Deferred also keeps track of its current status (fired).  Its
+       //              status may be one of three things:
+       //
+       //                      * -1: no value yet (initial condition)
+       //                      * 0: success
+       //                      * 1: error
+       //      
+       //              A Deferred will be in the error state if one of the following three
+       //              conditions are met:
+       //
+       //                      1. The result given to callback or errback is "instanceof" Error
+       //                      2. The previous callback or errback raised an exception while
+       //                         executing
+       //                      3. The previous callback or errback returned a value
+       //                         "instanceof" Error
+       //
+       //              Otherwise, the Deferred will be in the success state. The state of
+       //              the Deferred determines the next element in the callback sequence
+       //              to run.
+       //
+       //              When a callback or errback occurs with the example deferred chain,
+       //              something equivalent to the following will happen (imagine
+       //              that exceptions are caught and returned):
+       //
+       //              |       // d.callback(result) or d.errback(result)
+       //              |       if(!(result instanceof Error)){
+       //              |               result = myCallback(result);
+       //              |       }
+       //              |       if(result instanceof Error){
+       //              |               result = myErrback(result);
+       //              |       }
+       //              |       result = myBoth(result);
+       //              |       if(result instanceof Error){
+       //              |               result = myErrback(result);
+       //              |       }else{
+       //              |               result = myCallback(result);
+       //              |       }
+       //
+       //              The result is then stored away in case another step is added to the
+       //              callback sequence.      Since the Deferred already has a value
+       //              available, any new callbacks added will be called immediately.
+       //
+       //              There are two other "advanced" details about this implementation
+       //              that are useful:
+       //
+       //              Callbacks are allowed to return Deferred instances themselves, so
+       //              you can build complicated sequences of events with ease.
+       //
+       //              The creator of the Deferred may specify a canceller.  The canceller
+       //              is a function that will be called if Deferred.cancel is called
+       //              before the Deferred fires. You can use this to implement clean
+       //              aborting of an XMLHttpRequest, etc. Note that cancel will fire the
+       //              deferred with a CancelledError (unless your canceller returns
+       //              another kind of error), so the errbacks should be prepared to
+       //              handle that error for cancellable Deferreds.
+       // example:
+       //      |       var deferred = new dojo.Deferred();
+       //      |       setTimeout(function(){ deferred.callback({success: true}); }, 1000);
+       //      |       return deferred;
+       // example:
+       //              Deferred objects are often used when making code asynchronous. It
+       //              may be easiest to write functions in a synchronous manner and then
+       //              split code using a deferred to trigger a response to a long-lived
+       //              operation. For example, instead of register a callback function to
+       //              denote when a rendering operation completes, the function can
+       //              simply return a deferred:
+       //
+       //              |       // callback style:
+       //              |       function renderLotsOfData(data, callback){
+       //              |               var success = false
+       //              |               try{
+       //              |                       for(var x in data){
+       //              |                               renderDataitem(data[x]);
+       //              |                       }
+       //              |                       success = true;
+       //              |               }catch(e){ }
+       //              |               if(callback){
+       //              |                       callback(success);
+       //              |               }
+       //              |       }
+       //
+       //              |       // using callback style
+       //              |       renderLotsOfData(someDataObj, function(success){
+       //              |               // handles success or failure
+       //              |               if(!success){
+       //              |                       promptUserToRecover();
+       //              |               }
+       //              |       });
+       //              |       // NOTE: no way to add another callback here!!
+       // example:
+       //              Using a Deferred doesn't simplify the sending code any, but it
+       //              provides a standard interface for callers and senders alike,
+       //              providing both with a simple way to service multiple callbacks for
+       //              an operation and freeing both sides from worrying about details
+       //              such as "did this get called already?". With Deferreds, new
+       //              callbacks can be added at any time.
+       //
+       //              |       // Deferred style:
+       //              |       function renderLotsOfData(data){
+       //              |               var d = new dojo.Deferred();
+       //              |               try{
+       //              |                       for(var x in data){
+       //              |                               renderDataitem(data[x]);
+       //              |                       }
+       //              |                       d.callback(true);
+       //              |               }catch(e){ 
+       //              |                       d.errback(new Error("rendering failed"));
+       //              |               }
+       //              |               return d;
+       //              |       }
+       //
+       //              |       // using Deferred style
+       //              |       renderLotsOfData(someDataObj).addErrback(function(){
+       //              |               promptUserToRecover();
+       //              |       });
+       //              |       // NOTE: addErrback and addCallback both return the Deferred
+       //              |       // again, so we could chain adding callbacks or save the
+       //              |       // deferred for later should we need to be notified again.
+       // example:
+       //              In this example, renderLotsOfData is syncrhonous and so both
+       //              versions are pretty artificial. Putting the data display on a
+       //              timeout helps show why Deferreds rock:
+       //
+       //              |       // Deferred style and async func
+       //              |       function renderLotsOfData(data){
+       //              |               var d = new dojo.Deferred();
+       //              |               setTimeout(function(){
+       //              |                       try{
+       //              |                               for(var x in data){
+       //              |                                       renderDataitem(data[x]);
+       //              |                               }
+       //              |                               d.callback(true);
+       //              |                       }catch(e){ 
+       //              |                               d.errback(new Error("rendering failed"));
+       //              |                       }
+       //              |               }, 100);
+       //              |               return d;
+       //              |       }
+       //
+       //              |       // using Deferred style
+       //              |       renderLotsOfData(someDataObj).addErrback(function(){
+       //              |               promptUserToRecover();
+       //              |       });
+       //
+       //              Note that the caller doesn't have to change his code at all to
+       //              handle the asynchronous case.
+
+       this.chain = [];
+       this.id = this._nextId();
+       this.fired = -1;
+       this.paused = 0;
+       this.results = [null, null];
+       this.canceller = canceller;
+       this.silentlyCancelled = false;
+};
+
+dojo.extend(dojo.Deferred, {
+       /*
+       makeCalled: function(){
+               // summary:
+               //              returns a new, empty deferred, which is already in the called
+               //              state. Calling callback() or errback() on this deferred will
+               //              yeild an error and adding new handlers to it will result in
+               //              them being called immediately.
+               var deferred = new dojo.Deferred();
+               deferred.callback();
+               return deferred;
+       },
+
+       toString: function(){
+               var state;
+               if(this.fired == -1){
+                       state = 'unfired';
+               }else{
+                       state = this.fired ? 'success' : 'error';
+               }
+               return 'Deferred(' + this.id + ', ' + state + ')';
+       },
+       */
+
+       _nextId: (function(){
+               var n = 1;
+               return function(){ return n++; };
+       })(),
+
+       cancel: function(){
+               // summary:     
+               //              Cancels a Deferred that has not yet received a value, or is
+               //              waiting on another Deferred as its value.
+               // description:
+               //              If a canceller is defined, the canceller is called. If the
+               //              canceller did not return an error, or there was no canceller,
+               //              then the errback chain is started.
+               var err;
+               if(this.fired == -1){
+                       if(this.canceller){
+                               err = this.canceller(this);
+                       }else{
+                               this.silentlyCancelled = true;
+                       }
+                       if(this.fired == -1){
+                               if(!(err instanceof Error)){
+                                       var res = err;
+                                       err = new Error("Deferred Cancelled");
+                                       err.dojoType = "cancel";
+                                       err.cancelResult = res;
+                               }
+                               this.errback(err);
+                       }
+               }else if(       (this.fired == 0) &&
+                                       (this.results[0] instanceof dojo.Deferred)
+               ){
+                       this.results[0].cancel();
+               }
+       },
+                       
+
+       _resback: function(res){
+               // summary:
+               //              The private primitive that means either callback or errback
+               this.fired = ((res instanceof Error) ? 1 : 0);
+               this.results[this.fired] = res;
+               this._fire();
+       },
+
+       _check: function(){
+               if(this.fired != -1){
+                       if(!this.silentlyCancelled){
+                               throw new Error("already called!");
+                       }
+                       this.silentlyCancelled = false;
+                       return;
+               }
+       },
+
+       callback: function(res){
+               //      summary:        
+               //              Begin the callback sequence with a non-error value.
+               
+               /*
+               callback or errback should only be called once on a given
+               Deferred.
+               */
+               this._check();
+               this._resback(res);
+       },
+
+       errback: function(/*Error*/res){
+               //      summary: 
+               //              Begin the callback sequence with an error result.
+               this._check();
+               if(!(res instanceof Error)){
+                       res = new Error(res);
+               }
+               this._resback(res);
+       },
+
+       addBoth: function(/*Function|Object*/cb, /*String?*/cbfn){
+               //      summary:
+               //              Add the same function as both a callback and an errback as the
+               //              next element on the callback sequence.This is useful for code
+               //              that you want to guarantee to run, e.g. a finalizer.
+               var enclosed = dojo.hitch.apply(dojo, arguments);
+               return this.addCallbacks(enclosed, enclosed);
+       },
+
+       addCallback: function(/*Function|Object*/cb, /*String?*/cbfn /*...*/){
+               //      summary: 
+               //              Add a single callback to the end of the callback sequence.
+               return this.addCallbacks(dojo.hitch.apply(dojo, arguments));
+       },
+
+       addErrback: function(cb, cbfn){
+               //      summary: 
+               //              Add a single callback to the end of the callback sequence.
+               return this.addCallbacks(null, dojo.hitch.apply(dojo, arguments));
+       },
+
+       addCallbacks: function(cb, eb){
+               // summary: 
+               //              Add separate callback and errback to the end of the callback
+               //              sequence.
+               this.chain.push([cb, eb])
+               if(this.fired >= 0){
+                       this._fire();
+               }
+               return this;
+       },
+
+       _fire: function(){
+               // summary: 
+               //              Used internally to exhaust the callback sequence when a result
+               //              is available.
+               var chain = this.chain;
+               var fired = this.fired;
+               var res = this.results[fired];
+               var self = this;
+               var cb = null;
+               while(
+                       (chain.length > 0) &&
+                       (this.paused == 0)
+               ){
+                       // Array
+                       var f = chain.shift()[fired];
+                       if(!f){ continue; }
+                       try{
+                               res = f(res);
+                               fired = ((res instanceof Error) ? 1 : 0);
+                               if(res instanceof dojo.Deferred){
+                                       cb = function(res){
+                                               self._resback(res);
+                                               // inlined from _pause()
+                                               self.paused--;
+                                               if(
+                                                       (self.paused == 0) && 
+                                                       (self.fired >= 0)
+                                               ){
+                                                       self._fire();
+                                               }
+                                       }
+                                       // inlined from _unpause
+                                       this.paused++;
+                               }
+                       }catch(err){
+                               console.debug(err);
+                               fired = 1;
+                               res = err;
+                       }
+               }
+               this.fired = fired;
+               this.results[fired] = res;
+               if((cb)&&(this.paused)){
+                       // this is for "tail recursion" in case the dependent
+                       // deferred is already fired
+                       res.addBoth(cb);
+               }
+       }
+});
+
+}
+
+if(!dojo._hasResource["dojo._base.json"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.json"] = true;
+dojo.provide("dojo._base.json");
+
+dojo.fromJson = function(/*String*/ json){
+       // summary:
+       //              Parses a [JSON](http://json.org) string to return a JavaScript object.
+       // json: 
+       //              a string literal of a JSON item, for instance:
+       //                      `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'`
+
+       return eval("(" + json + ")"); // Object
+}
+
+dojo._escapeString = function(/*String*/str){
+       //summary:
+       //              Adds escape sequences for non-visual characters, double quote and
+       //              backslash and surrounds with double quotes to form a valid string
+       //              literal.
+       return ('"' + str.replace(/(["\\])/g, '\\$1') + '"').
+               replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "\\n").
+               replace(/[\t]/g, "\\t").replace(/[\r]/g, "\\r"); // string
+}
+
+dojo.toJsonIndentStr = "\t";
+dojo.toJson = function(/*Object*/ it, /*Boolean?*/ prettyPrint, /*String?*/ _indentStr){
+       // summary:
+       //              Returns a [JSON](http://json.org) serialization of an object.
+       //
+       // description:
+       //              Returns a [JSON](http://json.org) serialization of an object.
+       //              Note that this doesn't check for infinite recursion, so don't do that!
+       //
+       // it:
+       //              an object to be serialized. Objects may define their own
+       //              serialization via a special "__json__" or "json" function
+       //              property. If a specialized serializer has been defined, it will
+       //              be used as a fallback.
+       //
+       // prettyPrint:
+       //              if true, we indent objects and arrays to make the output prettier.
+       //              The variable dojo.toJsonIndentStr is used as the indent string 
+       //              -- to use something other than the default (tab), 
+       //              change that variable before calling dojo.toJson().
+       //
+       // _indentStr:
+       //              private variable for recursive calls when pretty printing, do not use.
+
+       if(it === undefined){
+               return "undefined";
+       }
+       var objtype = typeof it;
+       if(objtype == "number" || objtype == "boolean"){
+               return it + "";
+       }
+       if(it === null){
+               return "null";
+       }
+       if(dojo.isString(it)){ 
+               return dojo._escapeString(it); 
+       }
+       if(it.nodeType && it.cloneNode){ // isNode
+               return ""; // FIXME: would something like outerHTML be better here?
+       }
+       // recurse
+       var recurse = arguments.callee;
+       // short-circuit for objects that support "json" serialization
+       // if they return "self" then just pass-through...
+       var newObj;
+       _indentStr = _indentStr || "";
+       var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : "";
+       if(typeof it.__json__ == "function"){
+               newObj = it.__json__();
+               if(it !== newObj){
+                       return recurse(newObj, prettyPrint, nextIndent);
+               }
+       }
+       if(typeof it.json == "function"){
+               newObj = it.json();
+               if(it !== newObj){
+                       return recurse(newObj, prettyPrint, nextIndent);
+               }
+       }
+
+       var sep = prettyPrint ? " " : "";
+       var newLine = prettyPrint ? "\n" : "";
+
+       // array
+       if(dojo.isArray(it)){
+               var res = dojo.map(it, function(obj){
+                       var val = recurse(obj, prettyPrint, nextIndent);
+                       if(typeof val != "string"){
+                               val = "undefined";
+                       }
+                       return newLine + nextIndent + val;
+               });
+               return "[" + res.join("," + sep) + newLine + _indentStr + "]";
+       }
+       /*
+       // look in the registry
+       try {
+               window.o = it;
+               newObj = dojo.json.jsonRegistry.match(it);
+               return recurse(newObj, prettyPrint, nextIndent);
+       }catch(e){
+               // console.debug(e);
+       }
+       // it's a function with no adapter, skip it
+       */
+       if(objtype == "function"){
+               return null; // null
+       }
+       // generic object code path
+       var output = [];
+       for(var key in it){
+               var keyStr;
+               if(typeof key == "number"){
+                       keyStr = '"' + key + '"';
+               }else if(typeof key == "string"){
+                       keyStr = dojo._escapeString(key);
+               }else{
+                       // skip non-string or number keys
+                       continue;
+               }
+               val = recurse(it[key], prettyPrint, nextIndent);
+               if(typeof val != "string"){
+                       // skip non-serializable values
+                       continue;
+               }
+               // FIXME: use += on Moz!!
+               //       MOW NOTE: using += is a pain because you have to account for the dangling comma...
+               output.push(newLine + nextIndent + keyStr + ":" + sep + val);
+       }
+       return "{" + output.join("," + sep) + newLine + _indentStr + "}"; // String
+}
+
+}
+
+if(!dojo._hasResource["dojo._base.array"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.array"] = true;
+
+dojo.provide("dojo._base.array");
+
+(function(){
+       var _getParts = function(arr, obj, cb){
+               return [ 
+                       dojo.isString(arr) ? arr.split("") : arr, 
+                       obj || dojo.global,
+                       // FIXME: cache the anonymous functions we create here?
+                       dojo.isString(cb) ? new Function("item", "index", "array", cb) : cb
+               ];
+       };
+
+       dojo.mixin(dojo, {
+               indexOf: function(      /*Array*/               array, 
+                                                       /*Object*/              value,
+                                                       /*Integer?*/    fromIndex,
+                                                       /*Boolean?*/    findLast){
+                       // summary:
+                       //              locates the first index of the provided value in the
+                       //              passed array. If the value is not found, -1 is returned.
+                       // description:
+                       //              For details on this method, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf>
+
+                       var step = 1, end = array.length || 0, i = 0;
+                       if(findLast){
+                               i = end - 1;
+                               step = end = -1;
+                       }
+                       if(fromIndex != undefined){ i = fromIndex; }
+                       if((findLast && i > end) || i < end){
+                               for(; i != end; i += step){
+                                       if(array[i] == value){ return i; }
+                               }
+                       }
+                       return -1;      // Number
+               },
+
+               lastIndexOf: function(/*Array*/array, /*Object*/value, /*Integer?*/fromIndex){
+                       // summary:
+                       //              locates the last index of the provided value in the passed array. 
+                       //              If the value is not found, -1 is returned.
+                       // description:
+                       //              For details on this method, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf>
+                       return dojo.indexOf(array, value, fromIndex, true); // Number
+               },
+
+               forEach: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
+                       // summary:
+                       //              for every item in arr, callback is invoked.  Return values are ignored.
+                       // arr: the array to iterate on.  If a string, operates on individual characters.
+                       // callback: a function is invoked with three arguments: item, index, and array
+                       // thisObject: may be used to scope the call to callback
+                       // description:
+                       //              This function corresponds to the JavaScript 1.6 Array.forEach() method.
+                       //              In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
+                       //              For more details, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach>
+
+                       // match the behavior of the built-in forEach WRT empty arrs
+                       if(!arr || !arr.length){ return; }
+
+                       // FIXME: there are several ways of handilng thisObject. Is
+                       // dojo.global always the default context?
+                       var _p = _getParts(arr, thisObject, callback); arr = _p[0];
+                       for(var i=0,l=_p[0].length; i<l; i++){ 
+                               _p[2].call(_p[1], arr[i], i, arr);
+                       }
+               },
+
+               _everyOrSome: function(/*Boolean*/every, /*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
+                       var _p = _getParts(arr, thisObject, callback); arr = _p[0];
+                       for(var i = 0, l = arr.length; i < l; i++){
+                               var result = !!_p[2].call(_p[1], arr[i], i, arr);
+                               if(every ^ result){
+                                       return result; // Boolean
+                               }
+                       }
+                       return every; // Boolean
+               },
+
+               every: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
+                       // summary:
+                       //              Determines whether or not every item in arr satisfies the
+                       //              condition implemented by callback.
+                       // arr: the array to iterate on.  If a string, operates on individual characters.
+                       // callback: a function is invoked with three arguments: item, index, and array and returns true
+                       //              if the condition is met.
+                       // thisObject: may be used to scope the call to callback
+                       // description:
+                       //              This function corresponds to the JavaScript 1.6 Array.every() method.
+                       //              In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
+                       //              For more details, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every>
+                       // example:
+                       //      |       dojo.every([1, 2, 3, 4], function(item){ return item>1; });
+                       //              returns false
+                       // example:
+                       //      |       dojo.every([1, 2, 3, 4], function(item){ return item>0; });
+                       //              returns true 
+                       return this._everyOrSome(true, arr, callback, thisObject); // Boolean
+               },
+
+               some: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
+                       // summary:
+                       //              Determines whether or not any item in arr satisfies the
+                       //              condition implemented by callback.
+                       // arr: the array to iterate on.  If a string, operates on individual characters.
+                       // callback: a function is invoked with three arguments: item, index, and array and returns true
+                       //              if the condition is met.
+                       // thisObject: may be used to scope the call to callback
+                       // description:
+                       //              This function corresponds to the JavaScript 1.6 Array.some() method.
+                       //              In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
+                       //              For more details, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some>
+                       // example:
+                       //      |       dojo.some([1, 2, 3, 4], function(item){ return item>1; });
+                       //              returns true
+                       // example:
+                       //      |       dojo.some([1, 2, 3, 4], function(item){ return item<1; });
+                       //              returns false
+                       return this._everyOrSome(false, arr, callback, thisObject); // Boolean
+               },
+
+               map: function(/*Array|String*/arr, /*Function|String*/callback, /*Function?*/thisObject){
+                       // summary:
+                       //              applies callback to each element of arr and returns
+                       //              an Array with the results
+                       // arr: the array to iterate on.  If a string, operates on individual characters.
+                       // callback: a function is invoked with three arguments: item, index, and array and returns a value
+                       // thisObject: may be used to scope the call to callback
+                       // description:
+                       //              This function corresponds to the JavaScript 1.6 Array.map() method.
+                       //              In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
+                       //              For more details, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map>
+                       // example:
+                       //      |       dojo.map([1, 2, 3, 4], function(item){ return item+1 });
+                       //              returns [2, 3, 4, 5]
+                       var _p = _getParts(arr, thisObject, callback); arr = _p[0];
+                       var outArr = (arguments[3] ? (new arguments[3]()) : []);
+                       for(var i=0;i<arr.length;++i){
+                               outArr.push(_p[2].call(_p[1], arr[i], i, arr));
+                       }
+                       return outArr; // Array
+               },
+
+               filter: function(/*Array*/arr, /*Function|String*/callback, /*Object?*/thisObject){
+                       // summary:
+                       //              Returns a new Array with those items from arr that match the
+                       //              condition implemented by callback.
+                       // arr: the array to iterate on.  If a string, operates on individual characters.
+                       // callback: a function is invoked with three arguments: item, index, and array and returns true
+                       //              if the condition is met.
+                       // thisObject: may be used to scope the call to callback
+                       // description:
+                       //              This function corresponds to the JavaScript 1.6 Array.filter() method.
+                       //              In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
+                       //              For more details, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter>
+                       // example:
+                       //      |       dojo.filter([1, 2, 3, 4], function(item){ return item>1; });
+                       //              returns [2, 3, 4]
+
+                       var _p = _getParts(arr, thisObject, callback); arr = _p[0];
+                       var outArr = [];
+                       for(var i = 0; i < arr.length; i++){
+                               if(_p[2].call(_p[1], arr[i], i, arr)){
+                                       outArr.push(arr[i]);
+                               }
+                       }
+                       return outArr; // Array
+               }
+       });
+})();
+
+}
+
+if(!dojo._hasResource["dojo._base.Color"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.Color"] = true;
+dojo.provide("dojo._base.Color");
+
+
+
+dojo.Color = function(/*Array|String|Object*/ color){
+       // summary:
+       //              takes a named string, hex string, array of rgb or rgba values,
+       //              an object with r, g, b, and a properties, or another dojo.Color object
+       if(color){ this.setColor(color); }
+};
+
+// FIXME: there's got to be a more space-efficient way to encode or discover these!!  Use hex?
+dojo.Color.named = {
+       black:      [0,0,0],
+       silver:     [192,192,192],
+       gray:       [128,128,128],
+       white:      [255,255,255],
+       maroon:         [128,0,0],
+       red:        [255,0,0],
+       purple:         [128,0,128],
+       fuchsia:        [255,0,255],
+       green:      [0,128,0],
+       lime:       [0,255,0],
+       olive:          [128,128,0],
+       yellow:         [255,255,0],
+       navy:       [0,0,128],
+       blue:       [0,0,255],
+       teal:           [0,128,128],
+       aqua:           [0,255,255]
+};
+
+
+dojo.extend(dojo.Color, {
+       r: 255, g: 255, b: 255, a: 1,
+       _set: function(r, g, b, a){
+               var t = this; t.r = r; t.g = g; t.b = b; t.a = a;
+       },
+       setColor: function(/*Array|String|Object*/ color){
+               // summary:
+               //              takes a named string, hex string, array of rgb or rgba values,
+               //              an object with r, g, b, and a properties, or another dojo.Color object
+               var d = dojo;
+               if(d.isString(color)){
+                       d.colorFromString(color, this);
+               }else if(d.isArray(color)){
+                       d.colorFromArray(color, this);
+               }else{
+                       this._set(color.r, color.g, color.b, color.a);
+                       if(!(color instanceof d.Color)){ this.sanitize(); }
+               }
+               return this;    // dojo.Color
+       },
+       sanitize: function(){
+               // summary:
+               //              makes sure that the object has correct attributes
+               // description: 
+               //              the default implementation does nothing, include dojo.colors to
+               //              augment it to real checks
+               return this;    // dojo.Color
+       },
+       toRgb: function(){
+               // summary: returns 3 component array of rgb values
+               var t = this;
+               return [t.r, t.g, t.b]; // Array
+       },
+       toRgba: function(){
+               // summary: returns a 4 component array of rgba values
+               var t = this;
+               return [t.r, t.g, t.b, t.a];    // Array
+       },
+       toHex: function(){
+               // summary: returns a css color string in hexadecimal representation
+               var arr = dojo.map(["r", "g", "b"], function(x){
+                       var s = this[x].toString(16);
+                       return s.length < 2 ? "0" + s : s;
+               }, this);
+               return "#" + arr.join("");      // String
+       },
+       toCss: function(/*Boolean?*/ includeAlpha){
+               // summary: returns a css color string in rgb(a) representation
+               var t = this, rgb = t.r + ", " + t.g + ", " + t.b;
+               return (includeAlpha ? "rgba(" + rgb + ", " + t.a : "rgb(" + rgb) + ")";        // String
+       },
+       toString: function(){
+               // summary: returns a visual representation of the color
+               return this.toCss(true); // String
+       }
+});
+
+dojo.blendColors = function(
+       /*dojo.Color*/ start, 
+       /*dojo.Color*/ end, 
+       /*Number*/ weight,
+       /*dojo.Color?*/ obj
+){
+       // summary: 
+       //              blend colors end and start with weight from 0 to 1, 0.5 being a 50/50 blend,
+       //              can reuse a previously allocated dojo.Color object for the result
+       var d = dojo, t = obj || new dojo.Color();
+       d.forEach(["r", "g", "b", "a"], function(x){
+               t[x] = start[x] + (end[x] - start[x]) * weight;
+               if(x != "a"){ t[x] = Math.round(t[x]); }
+       });
+       return t.sanitize();    // dojo.Color
+};
+
+dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
+       // summary: get rgb(a) array from css-style color declarations
+       var m = color.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);
+       return m && dojo.colorFromArray(m[1].split(/\s*,\s*/), obj);    // dojo.Color
+};
+
+dojo.colorFromHex = function(/*String*/ color, /*dojo.Color?*/ obj){
+       // summary: converts a hex string with a '#' prefix to a color object.
+       //      Supports 12-bit #rgb shorthand.
+       var d = dojo, t = obj || new d.Color(),
+               bits = (color.length == 4) ? 4 : 8,
+               mask = (1 << bits) - 1;
+       color = Number("0x" + color.substr(1));
+       if(isNaN(color)){
+               return null; // dojo.Color
+       }
+       d.forEach(["b", "g", "r"], function(x){
+               var c = color & mask;
+               color >>= bits;
+               t[x] = bits == 4 ? 17 * c : c;
+       });
+       t.a = 1;
+       return t;       // dojo.Color
+};
+
+dojo.colorFromArray = function(/*Array*/ a, /*dojo.Color?*/ obj){
+       // summary: builds a color from 1, 2, 3, or 4 element array
+       var t = obj || new dojo.Color();
+       t._set(Number(a[0]), Number(a[1]), Number(a[2]), Number(a[3]));
+       if(isNaN(t.a)){ t.a = 1; }
+       return t.sanitize();    // dojo.Color
+};
+
+dojo.colorFromString = function(/*String*/ str, /*dojo.Color?*/ obj){
+       //      summary:
+       //              parses str for a color value.
+       //      description:
+       //              Acceptable input values for str may include arrays of any form
+       //              accepted by dojo.colorFromArray, hex strings such as "#aaaaaa", or
+       //              rgb or rgba strings such as "rgb(133, 200, 16)" or "rgba(10, 10,
+       //              10, 50)"
+       //      returns:
+       //              a dojo.Color object. If obj is passed, it will be the return value.
+       var a = dojo.Color.named[str];
+       return a && dojo.colorFromArray(a, obj) || dojo.colorFromRgb(str, obj) || dojo.colorFromHex(str, obj);
+};
+
+}
+
+if(!dojo._hasResource["dojo._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base"] = true;
+dojo.provide("dojo._base");
+
+
+
+
+
+
+
+
+
+}
+
+if(!dojo._hasResource["dojo._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.window"] = true;
+dojo.provide("dojo._base.window");
+
+dojo._gearsObject = function(){
+       // summary: 
+       //              factory method to get a Google Gears plugin instance to
+       //              expose in the browser runtime environment, if present
+       var factory;
+       var results;
+       
+       var gearsObj = dojo.getObject("google.gears");
+       if(gearsObj){ return gearsObj; } // already defined elsewhere
+       
+       if(typeof GearsFactory != "undefined"){ // Firefox
+               factory = new GearsFactory();
+       }else{
+               if(dojo.isIE){
+                       // IE
+                       try{
+                               factory = new ActiveXObject("Gears.Factory");
+                       }catch(e){
+                               // ok to squelch; there's no gears factory.  move on.
+                       }
+               }else if(navigator.mimeTypes["application/x-googlegears"]){
+                       // Safari?
+                       factory = document.createElement("object");
+                       factory.setAttribute("type", "application/x-googlegears");
+                       factory.setAttribute("width", 0);
+                       factory.setAttribute("height", 0);
+                       factory.style.display = "none";
+                       document.documentElement.appendChild(factory);
+               }
+       }
+
+       // still nothing?
+       if(!factory){ return null; }
+       
+       // define the global objects now; don't overwrite them though if they
+       // were somehow set internally by the Gears plugin, which is on their
+       // dev roadmap for the future
+       dojo.setObject("google.gears.factory", factory);
+       return dojo.getObject("google.gears");
+};
+
+/*=====
+dojo.isGears = {
+       // summary: True if client is using Google Gears
+};
+=====*/
+// see if we have Google Gears installed, and if
+// so, make it available in the runtime environment
+// and in the Google standard 'google.gears' global object
+dojo.isGears = (!!dojo._gearsObject())||0;
+
+/*=====
+dojo.doc = {
+       // summary:
+       //              Alias for the current document. 'dojo.doc' can be modified
+       //              for temporary context shifting. Also see dojo.withDoc().
+       // description:
+       //    Refer to dojo.doc rather
+       //    than referring to 'window.document' to ensure your code runs
+       //    correctly in managed contexts.
+       // example:
+       //      |       n.appendChild(dojo.doc.createElement('div'));
+}
+=====*/
+dojo.doc = window["document"] || null;
+
+dojo.body = function(){
+       // summary:
+       //              Return the body element of the document
+       //              return the body object associated with dojo.doc
+       // example:
+       //      |       dojo.body().appendChild(dojo.doc.createElement('div'));
+
+       // Note: document.body is not defined for a strict xhtml document
+       // Would like to memoize this, but dojo.doc can change vi dojo.withDoc().
+       return dojo.doc.body || dojo.doc.getElementsByTagName("body")[0]; // Node
+}
+
+dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){
+       // summary:
+       //              changes the behavior of many core Dojo functions that deal with
+       //              namespace and DOM lookup, changing them to work in a new global
+       //              context (e.g., an iframe). The varibles dojo.global and dojo.doc
+       //              are modified as a result of calling this function and the result of
+       //              `dojo.body()` likewise differs.
+       dojo.global = globalObject;
+       dojo.doc = globalDocument;
+};
+
+dojo._fireCallback = function(callback, context, cbArguments){
+       if(context && dojo.isString(callback)){
+               callback = context[callback];
+       }
+       return callback.apply(context, cbArguments || [ ]);
+}
+
+dojo.withGlobal = function(    /*Object*/globalObject, 
+                                                       /*Function*/callback, 
+                                                       /*Object?*/thisObject, 
+                                                       /*Array?*/cbArguments){
+       // summary:
+       //              Call callback with globalObject as dojo.global and
+       //              globalObject.document as dojo.doc. If provided, globalObject
+       //              will be executed in the context of object thisObject
+       // description:
+       //              When callback() returns or throws an error, the dojo.global
+       //              and dojo.doc will be restored to its previous state.
+       var rval;
+       var oldGlob = dojo.global;
+       var oldDoc = dojo.doc;
+       try{
+               dojo.setContext(globalObject, globalObject.document);
+               rval = dojo._fireCallback(callback, thisObject, cbArguments);
+       }finally{
+               dojo.setContext(oldGlob, oldDoc);
+       }
+       return rval;
+}
+
+dojo.withDoc = function(       /*Object*/documentObject, 
+                                                       /*Function*/callback, 
+                                                       /*Object?*/thisObject, 
+                                                       /*Array?*/cbArguments){
+       // summary:
+       //              Call callback with documentObject as dojo.doc. If provided,
+       //              callback will be executed in the context of object thisObject
+       // description:
+       //              When callback() returns or throws an error, the dojo.doc will
+       //              be restored to its previous state.
+       var rval;
+       var oldDoc = dojo.doc;
+       try{
+               dojo.doc = documentObject;
+               rval = dojo._fireCallback(callback, thisObject, cbArguments);
+       }finally{
+               dojo.doc = oldDoc;
+       }
+       return rval;
+};
+
+}
+
+if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.event"] = true;
+dojo.provide("dojo._base.event");
+
+
+// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
+
+(function(){
+       // DOM event listener machinery
+       var del = (dojo._event_listener = {
+               add: function(/*DOMNode*/node, /*String*/name, /*Function*/fp){
+                       if(!node){return;} 
+                       name = del._normalizeEventName(name);
+                       fp = del._fixCallback(name, fp);
+                       var oname = name;
+                       if(!dojo.isIE && (name == "mouseenter" || name == "mouseleave")){
+                               var ofp = fp;
+                               //oname = name;
+                               name = (name == "mouseenter") ? "mouseover" : "mouseout";
+                               fp = function(e){
+                                       // thanks ben!
+                                       if(!dojo.isDescendant(e.relatedTarget, node)){
+                                               // e.type = oname; // FIXME: doesn't take? SJM: event.type is generally immutable.
+                                               return ofp.call(this, e); 
+                                       }
+                               }
+                       }
+                       node.addEventListener(name, fp, false);
+                       return fp; /*Handle*/
+               },
+               remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
+                       // summary:
+                       //              clobbers the listener from the node
+                       // node:
+                       //              DOM node to attach the event to
+                       // event:
+                       //              the name of the handler to remove the function from
+                       // handle:
+                       //              the handle returned from add
+                       if (node){
+                               node.removeEventListener(del._normalizeEventName(event), handle, false);
+                       }
+               },
+               _normalizeEventName: function(/*String*/name){
+                       // Generally, name should be lower case, unless it is special
+                       // somehow (e.g. a Mozilla DOM event).
+                       // Remove 'on'.
+                       return name.slice(0,2) =="on" ? name.slice(2) : name;
+               },
+               _fixCallback: function(/*String*/name, fp){
+                       // By default, we only invoke _fixEvent for 'keypress'
+                       // If code is added to _fixEvent for other events, we have
+                       // to revisit this optimization.
+                       // This also applies to _fixEvent overrides for Safari and Opera
+                       // below.
+                       return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); };
+               },
+               _fixEvent: function(evt, sender){
+                       // _fixCallback only attaches us to keypress.
+                       // Switch on evt.type anyway because we might 
+                       // be called directly from dojo.fixEvent.
+                       switch(evt.type){
+                               case "keypress":
+                                       del._setKeyChar(evt);
+                                       break;
+                       }
+                       return evt;
+               },
+               _setKeyChar: function(evt){
+                       evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : '';
+               }
+       });
+
+       // DOM events
+       
+       dojo.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
+               // summary:
+               //              normalizes properties on the event object including event
+               //              bubbling methods, keystroke normalization, and x/y positions
+               // evt: Event
+               //              native event object
+               // sender: DOMNode
+               //              node to treat as "currentTarget"
+               return del._fixEvent(evt, sender);
+       }
+
+       dojo.stopEvent = function(/*Event*/evt){
+               // summary:
+               //              prevents propagation and clobbers the default action of the
+               //              passed event
+               // evt: Event
+               //              The event object. If omitted, window.event is used on IE.
+               evt.preventDefault();
+               evt.stopPropagation();
+               // NOTE: below, this method is overridden for IE
+       }
+
+       // the default listener to use on dontFix nodes, overriden for IE
+       var node_listener = dojo._listener;
+       
+       // Unify connect and event listeners
+       dojo._connect = function(obj, event, context, method, dontFix){
+               // FIXME: need a more strict test
+               var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener);
+               // choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node
+               // we need the third option to provide leak prevention on broken browsers (IE)
+               var lid = !isNode ? 0 : (!dontFix ? 1 : 2), l = [dojo._listener, del, node_listener][lid];
+               // create a listener
+               var h = l.add(obj, event, dojo.hitch(context, method));
+               // formerly, the disconnect package contained "l" directly, but if client code
+               // leaks the disconnect package (by connecting it to a node), referencing "l" 
+               // compounds the problem.
+               // instead we return a listener id, which requires custom _disconnect below.
+               // return disconnect package
+               return [ obj, event, h, lid ];
+       }
+
+       dojo._disconnect = function(obj, event, handle, listener){
+               ([dojo._listener, del, node_listener][listener]).remove(obj, event, handle);
+       }
+
+       // Constants
+
+       // Public: client code should test
+       // keyCode against these named constants, as the
+       // actual codes can vary by browser.
+       dojo.keys = {
+               // summary: definitions for common key values
+               BACKSPACE: 8,
+               TAB: 9,
+               CLEAR: 12,
+               ENTER: 13,
+               SHIFT: 16,
+               CTRL: 17,
+               ALT: 18,
+               PAUSE: 19,
+               CAPS_LOCK: 20,
+               ESCAPE: 27,
+               SPACE: 32,
+               PAGE_UP: 33,
+               PAGE_DOWN: 34,
+               END: 35,
+               HOME: 36,
+               LEFT_ARROW: 37,
+               UP_ARROW: 38,
+               RIGHT_ARROW: 39,
+               DOWN_ARROW: 40,
+               INSERT: 45,
+               DELETE: 46,
+               HELP: 47,
+               LEFT_WINDOW: 91,
+               RIGHT_WINDOW: 92,
+               SELECT: 93,
+               NUMPAD_0: 96,
+               NUMPAD_1: 97,
+               NUMPAD_2: 98,
+               NUMPAD_3: 99,
+               NUMPAD_4: 100,
+               NUMPAD_5: 101,
+               NUMPAD_6: 102,
+               NUMPAD_7: 103,
+               NUMPAD_8: 104,
+               NUMPAD_9: 105,
+               NUMPAD_MULTIPLY: 106,
+               NUMPAD_PLUS: 107,
+               NUMPAD_ENTER: 108,
+               NUMPAD_MINUS: 109,
+               NUMPAD_PERIOD: 110,
+               NUMPAD_DIVIDE: 111,
+               F1: 112,
+               F2: 113,
+               F3: 114,
+               F4: 115,
+               F5: 116,
+               F6: 117,
+               F7: 118,
+               F8: 119,
+               F9: 120,
+               F10: 121,
+               F11: 122,
+               F12: 123,
+               F13: 124,
+               F14: 125,
+               F15: 126,
+               NUM_LOCK: 144,
+               SCROLL_LOCK: 145
+       };
+       
+       // IE event normalization
+       if(dojo.isIE){ 
+               var _trySetKeyCode = function(e, code){
+                       try{
+                               // squelch errors when keyCode is read-only
+                               // (e.g. if keyCode is ctrl or shift)
+                               return (e.keyCode = code);
+                       }catch(e){
+                               return 0;
+                       }
+               }
+
+               // by default, use the standard listener
+               var iel = dojo._listener;
+               // dispatcher tracking property
+               if(!dojo.config._allow_leaks){
+                       // custom listener that handles leak protection for DOM events
+                       node_listener = iel = dojo._ie_listener = {
+                               // support handler indirection: event handler functions are 
+                               // referenced here. Event dispatchers hold only indices.
+                               handlers: [],
+                               // add a listener to an object
+                               add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
+                                       source = source || dojo.global;
+                                       var f = source[method];
+                                       if(!f||!f._listeners){
+                                               var d = dojo._getIeDispatcher();
+                                               // original target function is special
+                                               d.target = f && (ieh.push(f) - 1);
+                                               // dispatcher holds a list of indices into handlers table
+                                               d._listeners = [];
+                                               // redirect source to dispatcher
+                                               f = source[method] = d;
+                                       }
+                                       return f._listeners.push(ieh.push(listener) - 1) ; /*Handle*/
+                               },
+                               // remove a listener from an object
+                               remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
+                                       var f = (source||dojo.global)[method], l = f && f._listeners;
+                                       if(f && l && handle--){
+                                               delete ieh[l[handle]];
+                                               delete l[handle];
+                                       }
+                               }
+                       };
+                       // alias used above
+                       var ieh = iel.handlers;
+               }
+
+               dojo.mixin(del, {
+                       add: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){
+                               if(!node){return;} // undefined
+                               event = del._normalizeEventName(event);
+                               if(event=="onkeypress"){
+                                       // we need to listen to onkeydown to synthesize
+                                       // keypress events that otherwise won't fire
+                                       // on IE
+                                       var kd = node.onkeydown;
+                                       if(!kd || !kd._listeners || !kd._stealthKeydownHandle){
+                                               var h = del.add(node, "onkeydown", del._stealthKeyDown);
+                                               kd = node.onkeydown;
+                                               kd._stealthKeydownHandle = h;
+                                               kd._stealthKeydownRefs = 1;
+                                       }else{
+                                               kd._stealthKeydownRefs++;
+                                       }
+                               }
+                               return iel.add(node, event, del._fixCallback(fp));
+                       },
+                       remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
+                               event = del._normalizeEventName(event);
+                               iel.remove(node, event, handle); 
+                               if(event=="onkeypress"){
+                                       var kd = node.onkeydown;
+                                       if(--kd._stealthKeydownRefs <= 0){
+                                               iel.remove(node, "onkeydown", kd._stealthKeydownHandle);
+                                               delete kd._stealthKeydownHandle;
+                                       }
+                               }
+                       },
+                       _normalizeEventName: function(/*String*/eventName){
+                               // Generally, eventName should be lower case, unless it is
+                               // special somehow (e.g. a Mozilla event)
+                               // ensure 'on'
+                               return eventName.slice(0,2) != "on" ? "on" + eventName : eventName;
+                       },
+                       _nop: function(){},
+                       _fixEvent: function(/*Event*/evt, /*DOMNode*/sender){
+                               // summary:
+                               //              normalizes properties on the event object including event
+                               //              bubbling methods, keystroke normalization, and x/y positions
+                               // evt: native event object
+                               // sender: node to treat as "currentTarget"
+                               if(!evt){
+                                       var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window;
+                                       evt = w.event; 
+                               }
+                               if(!evt){return(evt);}
+                               evt.target = evt.srcElement; 
+                               evt.currentTarget = (sender || evt.srcElement); 
+                               evt.layerX = evt.offsetX;
+                               evt.layerY = evt.offsetY;
+                               // FIXME: scroll position query is duped from dojo.html to
+                               // avoid dependency on that entire module. Now that HTML is in
+                               // Base, we should convert back to something similar there.
+                               var se = evt.srcElement, doc = (se && se.ownerDocument) || document;
+                               // DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used
+                               // here rather than document.body
+                               var docBody = ((dojo.isIE < 6) || (doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
+                               var offset = dojo._getIeDocumentElementOffset();
+                               evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x;
+                               evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y;
+                               if(evt.type == "mouseover"){ 
+                                       evt.relatedTarget = evt.fromElement;
+                               }
+                               if(evt.type == "mouseout"){ 
+                                       evt.relatedTarget = evt.toElement;
+                               }
+                               evt.stopPropagation = del._stopPropagation;
+                               evt.preventDefault = del._preventDefault;
+                               return del._fixKeys(evt);
+                       },
+                       _fixKeys: function(evt){
+                               switch(evt.type){
+                                       case "keypress":
+                                               var c = ("charCode" in evt ? evt.charCode : evt.keyCode);
+                                               if (c==10){
+                                                       // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla
+                                                       c=0;
+                                                       evt.keyCode = 13;
+                                               }else if(c==13||c==27){
+                                                       c=0; // Mozilla considers ENTER and ESC non-printable
+                                               }else if(c==3){
+                                                       c=99; // Mozilla maps CTRL-BREAK to CTRL-c
+                                               }
+                                               // Mozilla sets keyCode to 0 when there is a charCode
+                                               // but that stops the event on IE.
+                                               evt.charCode = c;
+                                               del._setKeyChar(evt);
+                                               break;
+                               }
+                               return evt;
+                       },
+                       // some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE
+                       // we map those virtual key codes to ascii here
+                       // not valid for all (non-US) keyboards, so maybe we shouldn't bother
+                       _punctMap: { 
+                               106:42, 
+                               111:47, 
+                               186:59, 
+                               187:43, 
+                               188:44, 
+                               189:45, 
+                               190:46, 
+                               191:47, 
+                               192:96, 
+                               219:91, 
+                               220:92, 
+                               221:93, 
+                               222:39 
+                       },
+                       _stealthKeyDown: function(evt){
+                               // IE doesn't fire keypress for most non-printable characters.
+                               // other browsers do, we simulate it here.
+                               var kp = evt.currentTarget.onkeypress;
+                               // only works if kp exists and is a dispatcher
+                               if(!kp || !kp._listeners){ return; }
+                               // munge key/charCode
+                               var k=evt.keyCode;
+                               // These are Windows Virtual Key Codes
+                               // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp
+                               var unprintable = (k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);
+                               // synthesize keypress for most unprintables and CTRL-keys
+                               if(unprintable||evt.ctrlKey){
+                                       var c = unprintable ? 0 : k;
+                                       if(evt.ctrlKey){
+                                               if(k==3 || k==13){
+                                                       return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively 
+                                               }else if(c>95 && c<106){ 
+                                                       c -= 48; // map CTRL-[numpad 0-9] to ASCII
+                                               }else if((!evt.shiftKey)&&(c>=65&&c<=90)){ 
+                                                       c += 32; // map CTRL-[A-Z] to lowercase
+                                               }else{ 
+                                                       c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII
+                                               }
+                                       }
+                                       // simulate a keypress event
+                                       var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c});
+                                       kp.call(evt.currentTarget, faux);
+                                       evt.cancelBubble = faux.cancelBubble;
+                                       evt.returnValue = faux.returnValue;
+                                       _trySetKeyCode(evt, faux.keyCode);
+                               }
+                       },
+                       // Called in Event scope
+                       _stopPropagation: function(){
+                               this.cancelBubble = true; 
+                       },
+                       _preventDefault: function(){
+                               // Setting keyCode to 0 is the only way to prevent certain keypresses (namely
+                               // ctrl-combinations that correspond to menu accelerator keys).
+                               // Otoh, it prevents upstream listeners from getting this information
+                               // Try to split the difference here by clobbering keyCode only for ctrl 
+                               // combinations. If you still need to access the key upstream, bubbledKeyCode is
+                               // provided as a workaround.
+                               this.bubbledKeyCode = this.keyCode;
+                               if(this.ctrlKey){_trySetKeyCode(this, 0);}
+                               this.returnValue = false;
+                       }
+               });
+                               
+               // override stopEvent for IE
+               dojo.stopEvent = function(evt){
+                       evt = evt || window.event;
+                       del._stopPropagation.call(evt);
+                       del._preventDefault.call(evt);
+               }
+       }
+
+       del._synthesizeEvent = function(evt, props){
+                       var faux = dojo.mixin({}, evt, props);
+                       del._setKeyChar(faux);
+                       // FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault); 
+                       // but it throws an error when preventDefault is invoked on Safari
+                       // does Event.preventDefault not support "apply" on Safari?
+                       faux.preventDefault = function(){ evt.preventDefault(); }; 
+                       faux.stopPropagation = function(){ evt.stopPropagation(); }; 
+                       return faux;
+       }
+       
+       // Opera event normalization
+       if(dojo.isOpera){
+               dojo.mixin(del, {
+                       _fixEvent: function(evt, sender){
+                               switch(evt.type){
+                                       case "keypress":
+                                               var c = evt.which;
+                                               if(c==3){
+                                                       c=99; // Mozilla maps CTRL-BREAK to CTRL-c
+                                               }
+                                               // can't trap some keys at all, like INSERT and DELETE
+                                               // there is no differentiating info between DELETE and ".", or INSERT and "-"
+                                               c = ((c<41)&&(!evt.shiftKey) ? 0 : c);
+                                               if((evt.ctrlKey)&&(!evt.shiftKey)&&(c>=65)&&(c<=90)){
+                                                       // lowercase CTRL-[A-Z] keys
+                                                       c += 32;
+                                               }
+                                               return del._synthesizeEvent(evt, { charCode: c });
+                               }
+                               return evt;
+                       }
+               });
+       }
+
+       // Safari event normalization
+       if(dojo.isSafari){
+               dojo.mixin(del, {
+                       _fixEvent: function(evt, sender){
+                               switch(evt.type){
+                                       case "keypress":
+                                               var c = evt.charCode, s = evt.shiftKey, k = evt.keyCode;
+                                               // FIXME: This is a hack, suggest we rethink keyboard strategy.
+                                               // Arrow and page keys have 0 "keyCode" in keypress events.on Safari for Windows
+                                               k = k || identifierMap[evt.keyIdentifier] || 0;
+                                               if(evt.keyIdentifier=="Enter"){
+                                                       c = 0; // differentiate Enter from CTRL-m (both code 13)
+                                               }else if((evt.ctrlKey)&&(c>0)&&(c<27)){
+                                                       c += 96; // map CTRL-[A-Z] codes to ASCII
+                                               } else if (c==dojo.keys.SHIFT_TAB) {
+                                                       c = dojo.keys.TAB; // morph SHIFT_TAB into TAB + shiftKey: true
+                                                       s = true;
+                                               } else {
+                                                       c = (c>=32 && c<63232 ? c : 0); // avoid generating keyChar for non-printables
+                                               }
+                                               return del._synthesizeEvent(evt, {charCode: c, shiftKey: s, keyCode: k});
+                               }
+                               return evt;
+                       }
+               });
+               
+               dojo.mixin(dojo.keys, {
+                       SHIFT_TAB: 25,
+                       UP_ARROW: 63232,
+                       DOWN_ARROW: 63233,
+                       LEFT_ARROW: 63234,
+                       RIGHT_ARROW: 63235,
+                       F1: 63236,
+                       F2: 63237,
+                       F3: 63238,
+                       F4: 63239,
+                       F5: 63240,
+                       F6: 63241,
+                       F7: 63242,
+                       F8: 63243,
+                       F9: 63244,
+                       F10: 63245,
+                       F11: 63246,
+                       F12: 63247,
+                       PAUSE: 63250,
+                       DELETE: 63272,
+                       HOME: 63273,
+                       END: 63275,
+                       PAGE_UP: 63276,
+                       PAGE_DOWN: 63277,
+                       INSERT: 63302,
+                       PRINT_SCREEN: 63248,
+                       SCROLL_LOCK: 63249,
+                       NUM_LOCK: 63289
+               });
+               var dk = dojo.keys, identifierMap = { "Up": dk.UP_ARROW, "Down": dk.DOWN_ARROW, "Left": dk.LEFT_ARROW, "Right": dk.RIGHT_ARROW, "PageUp": dk.PAGE_UP, "PageDown": dk.PAGE_DOWN }; 
+       }
+})();
+
+if(dojo.isIE){
+       // keep this out of the closure
+       // closing over 'iel' or 'ieh' b0rks leak prevention
+       // ls[i] is an index into the master handler array
+       dojo._ieDispatcher = function(args, sender){
+               var ap=Array.prototype, h=dojo._ie_listener.handlers, c=args.callee, ls=c._listeners, t=h[c.target];
+               // return value comes from original target function
+               var r = t && t.apply(sender, args);
+               // invoke listeners after target function
+               for(var i in ls){
+                       if(!(i in ap)){
+                               h[ls[i]].apply(sender, args);
+                       }
+               }
+               return r;
+       }
+       dojo._getIeDispatcher = function(){
+               // ensure the returned function closes over nothing
+               return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); // function
+       }
+       // keep this out of the closure to reduce RAM allocation
+       dojo._event_listener._fixCallback = function(fp){
+               var f = dojo._event_listener._fixEvent;
+               return function(e){ return fp.call(this, f(e, this)); };
+       }
+}
+
+}
+
+if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.html"] = true;
+
+dojo.provide("dojo._base.html");
+
+// FIXME: need to add unit tests for all the semi-public methods
+
+try{
+       document.execCommand("BackgroundImageCache", false, true);
+}catch(e){
+       // sane browsers don't have cache "issues"
+}
+
+// =============================
+// DOM Functions
+// =============================
+
+/*=====
+dojo.byId = function(id, doc){
+       //      summary:
+       //              Returns DOM node with matching `id` attribute or `null` 
+       //              if not found, similar to "$" function in another library.
+       //              If `id` is a DomNode, this function is a no-op.
+       //
+       //      id: String|DOMNode
+       //              A string to match an HTML id attribute or a reference to a DOM Node
+       //
+       //      doc: Document?
+       //              Document to work in. Defaults to the current value of
+       //              dojo.doc.  Can be used to retrieve
+       //              node references from other documents.
+=====*/
+if(dojo.isIE || dojo.isOpera){
+       dojo.byId = function(id, doc){
+               if(dojo.isString(id)){
+                       var _d = doc || dojo.doc;
+                       var te = _d.getElementById(id);
+                       // attributes.id.value is better than just id in case the 
+                       // user has a name=id inside a form
+                       if(te && te.attributes.id.value == id){
+                               return te;
+                       }else{
+                               var eles = _d.all[id];
+                               if(!eles || !eles.length){ return eles; }
+                               // if more than 1, choose first with the correct id
+                               var i=0;
+                               while((te=eles[i++])){
+                                       if(te.attributes.id.value == id){ return te; }
+                               }
+                       }
+               }else{
+                       return id; // DomNode
+               }
+       }
+}else{
+       dojo.byId = function(id, doc){
+               return dojo.isString(id) ? (doc || dojo.doc).getElementById(id) : id; // DomNode
+       }
+}
+/*=====
+}
+=====*/
+
+(function(){
+       /*
+       dojo.createElement = function(obj, parent, position){
+               // TODO: need to finish this!
+       }
+       */
+
+       var d = dojo;
+
+       var _destroyContainer = null;
+       dojo.addOnUnload(function(){
+               _destroyContainer=null; //prevent IE leak
+       });
+       dojo._destroyElement = function(/*String||DomNode*/node){
+               // summary:
+               //              removes node from its parent, clobbers it and all of its
+               //              children.
+               //      node:
+               //              the element to be destroyed, either as an ID or a reference
+
+               node = d.byId(node);
+               try{
+                       if(!_destroyContainer){
+                               _destroyContainer = document.createElement("div");
+                       }
+                       _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node);
+                       // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature
+                       _destroyContainer.innerHTML = ""; 
+               }catch(e){
+                       /* squelch */
+               }
+       };
+
+       dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){
+               //      summary:
+               //              Returns true if node is a descendant of ancestor
+               //      node: id or node reference to test
+               //      ancestor: id or node reference of potential parent to test against
+               try{
+                       node = d.byId(node);
+                       ancestor = d.byId(ancestor);
+                       while(node){
+                               if(node === ancestor){
+                                       return true; // Boolean
+                               }
+                               node = node.parentNode;
+                       }
+               }catch(e){ /* squelch, return false */ }
+               return false; // Boolean
+       };
+
+       dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){
+               //      summary: enable or disable selection on a node
+               //      node:
+               //              id or reference to node
+               //      selectable:
+               node = d.byId(node);
+               if(d.isMozilla){
+                       node.style.MozUserSelect = selectable ? "" : "none";
+               }else if(d.isKhtml){
+                       node.style.KhtmlUserSelect = selectable ? "auto" : "none";
+               }else if(d.isIE){
+                       node.unselectable = selectable ? "" : "on";
+                       d.query("*", node).forEach(function(descendant){
+                               descendant.unselectable = selectable ? "" : "on";
+                       });
+               }
+               //FIXME: else?  Opera?
+       };
+
+       var _insertBefore = function(/*Node*/node, /*Node*/ref){
+               ref.parentNode.insertBefore(node, ref);
+               return true;    //      boolean
+       }
+
+       var _insertAfter = function(/*Node*/node, /*Node*/ref){
+               //      summary:
+               //              Try to insert node after ref
+               var pn = ref.parentNode;
+               if(ref == pn.lastChild){
+                       pn.appendChild(node);
+               }else{
+                       return _insertBefore(node, ref.nextSibling);    //      boolean
+               }
+               return true;    //      boolean
+       }
+
+       dojo.place = function(/*String|DomNode*/node, /*String|DomNode*/refNode, /*String|Number*/position){
+               //      summary:
+               //              Attempt to insert node into the DOM, choosing from various positioning options.
+               //              Returns true if successful, false otherwise.
+               //      node: 
+               //              id or node reference to place relative to refNode
+               //      refNode: 
+               //              id or node reference to use as basis for placement
+               //      position:
+               //              string noting the position of node relative to refNode or a
+               //              number indicating the location in the childNodes collection of
+               //              refNode. Accepted string values are:
+               //
+               //              * before
+               //              * after
+               //              * first
+               //              * last
+               //
+               //              "first" and "last" indicate positions as children of refNode.
+
+               // FIXME: need to write tests for this!!!!
+               if(!node || !refNode || position === undefined){ 
+                       return false;   //      boolean 
+               }
+               node = d.byId(node);
+               refNode = d.byId(refNode);
+               if(typeof position == "number"){
+                       var cn = refNode.childNodes;
+                       if((position == 0 && cn.length == 0) ||
+                               cn.length == position){
+                               refNode.appendChild(node); return true;
+                       }
+                       if(position == 0){
+                               return _insertBefore(node, refNode.firstChild);
+                       }
+                       return _insertAfter(node, cn[position-1]);
+               }
+               switch(position.toLowerCase()){
+                       case "before":
+                               return _insertBefore(node, refNode);    //      boolean
+                       case "after":
+                               return _insertAfter(node, refNode);             //      boolean
+                       case "first":
+                               if(refNode.firstChild){
+                                       return _insertBefore(node, refNode.firstChild); //      boolean
+                               }
+                               // else fallthrough...
+                       default: // aka: last
+                               refNode.appendChild(node);
+                               return true;    //      boolean
+               }
+       }
+
+       // Box functions will assume this model.
+       // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
+       // Can be set to change behavior of box setters.
+       
+       // can be either:
+       //      "border-box"
+       //      "content-box" (default)
+       dojo.boxModel = "content-box";
+       
+       // We punt per-node box mode testing completely.
+       // If anybody cares, we can provide an additional (optional) unit 
+       // that overrides existing code to include per-node box sensitivity.
+
+       // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
+       // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
+       // IIRC, earlier versions of Opera did in fact use border-box.
+       // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
+
+       if(d.isIE /*|| dojo.isOpera*/){
+               var _dcm = document.compatMode;
+               // client code may have to adjust if compatMode varies across iframes
+               d.boxModel = _dcm == "BackCompat" || _dcm == "QuirksMode" || d.isIE<6 ? "border-box" : "content-box"; // FIXME: remove IE < 6 support?
+       }
+
+       // =============================
+       // Style Functions
+       // =============================
+       
+       // getComputedStyle drives most of the style code.
+       // Wherever possible, reuse the returned object.
+       //
+       // API functions below that need to access computed styles accept an 
+       // optional computedStyle parameter.
+       // If this parameter is omitted, the functions will call getComputedStyle themselves.
+       // This way, calling code can access computedStyle once, and then pass the reference to 
+       // multiple API functions. 
+
+/*=====
+       dojo.getComputedStyle = function(node){
+               //      summary:
+               //              Returns a "computed style" object.
+               //
+               //      description:
+               //              Gets a "computed style" object which can be used to gather
+               //              information about the current state of the rendered node. 
+               //
+               //              Note that this may behave differently on different browsers.
+               //              Values may have different formats and value encodings across
+               //              browsers.
+               //
+               //              Note also that this method is expensive.  Wherever possible,
+               //              reuse the returned object.
+               //
+               //              Use the dojo.style() method for more consistent (pixelized)
+               //              return values.
+               //
+               //      node: DOMNode
+               //              A reference to a DOM node. Does NOT support taking an
+               //              ID string for speed reasons.
+               //      example:
+               //      |       dojo.getComputedStyle(dojo.byId('foo')).borderWidth;
+               return; // CSS2Properties
+       }
+=====*/
+
+       var gcs, dv = document.defaultView;
+       if(d.isSafari){
+               gcs = function(/*DomNode*/node){
+                       var s = dv.getComputedStyle(node, null);
+                       if(!s && node.style){ 
+                               node.style.display = ""; 
+                               s = dv.getComputedStyle(node, null);
+                       }
+                       return s || {};
+               }; 
+       }else if(d.isIE){
+               gcs = function(node){
+                       return node.currentStyle;
+               };
+       }else{
+               gcs = function(node){
+                       return dv.getComputedStyle(node, null);
+               };
+       }
+       dojo.getComputedStyle = gcs;
+
+       if(!d.isIE){
+               dojo._toPixelValue = function(element, value){
+                       // style values can be floats, client code may want
+                       // to round for integer pixels.
+                       return parseFloat(value) || 0; 
+               }
+       }else{
+               dojo._toPixelValue = function(element, avalue){
+                       if(!avalue){ return 0; }
+                       // on IE7, medium is usually 4 pixels
+                       if(avalue=="medium"){ return 4; }
+                       // style values can be floats, client code may
+                       // want to round this value for integer pixels.
+                       if(avalue.slice && (avalue.slice(-2)=='px')){ return parseFloat(avalue); }
+                       with(element){
+                               var sLeft = style.left;
+                               var rsLeft = runtimeStyle.left;
+                               runtimeStyle.left = currentStyle.left;
+                               try{
+                                       // 'avalue' may be incompatible with style.left, which can cause IE to throw
+                                       // this has been observed for border widths using "thin", "medium", "thick" constants
+                                       // those particular constants could be trapped by a lookup
+                                       // but perhaps there are more
+                                       style.left = avalue;
+                                       avalue = style.pixelLeft;
+                               }catch(e){
+                                       avalue = 0;
+                               }
+                               style.left = sLeft;
+                               runtimeStyle.left = rsLeft;
+                       }
+                       return avalue;
+               }
+       }
+       var px = d._toPixelValue;
+
+       // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
+       /*=====
+       dojo._getOpacity = function(node){
+                       //      summary:
+                       //              Returns the current opacity of the passed node as a
+                       //              floating-point value between 0 and 1.
+                       //      node: DomNode
+                       //              a reference to a DOM node. Does NOT support taking an
+                       //              ID string for speed reasons.
+                       //      return: Number between 0 and 1
+       }
+       =====*/
+
+       dojo._getOpacity = d.isIE ? function(node){
+               try{
+                       return node.filters.alpha.opacity / 100; // Number
+               }catch(e){
+                       return 1; // Number
+               }
+       } : function(node){
+               return gcs(node).opacity;
+       };
+
+       /*=====
+       dojo._setOpacity = function(node, opacity){
+                       //      summary:
+                       //              set the opacity of the passed node portably. Returns the
+                       //              new opacity of the node.
+                       //      node: DOMNode
+                       //              a reference to a DOM node. Does NOT support taking an
+                       //              ID string for performance reasons.
+                       //      opacity: Number
+                       //              A Number between 0 and 1. 0 specifies transparent.
+                       //      return: Number between 0 and 1
+       }
+       =====*/
+
+       dojo._setOpacity = d.isIE ? function(/*DomNode*/node, /*Number*/opacity){
+               if(opacity == 1){
+                       // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so remove it altogether (bug #2661)
+                       var filterRE = /FILTER:[^;]*;?/i;
+                       node.style.cssText = node.style.cssText.replace(filterRE, "");
+                       if(node.nodeName.toLowerCase() == "tr"){
+                               d.query("> td", node).forEach(function(i){
+                                       i.style.cssText = i.style.cssText.replace(filterRE, "");
+                               });
+                       }
+               }else{
+                       var o = "Alpha(Opacity="+ opacity * 100 +")";
+                       node.style.filter = o;
+               }
+               if(node.nodeName.toLowerCase() == "tr"){
+                       d.query("> td", node).forEach(function(i){
+                               i.style.filter = o;
+                       });
+               }
+               return opacity;
+       } : function(node, opacity){
+               return node.style.opacity = opacity;
+       };
+
+       var _pixelNamesCache = {
+               left: true, top: true
+       };
+       var _pixelRegExp = /margin|padding|width|height|max|min|offset/;  // |border
+       var _toStyleValue = function(node, type, value){
+               type = type.toLowerCase();
+               if(d.isIE && value == "auto"){
+                       if(type == "height"){ return node.offsetHeight; }
+                       if(type == "width"){ return node.offsetWidth; }
+               }
+               if(!(type in _pixelNamesCache)){
+                       //      if(dojo.isOpera && type == "cssText"){
+                       //              FIXME: add workaround for #2855 here
+                       //      }
+                       _pixelNamesCache[type] = _pixelRegExp.test(type);
+               }
+               return _pixelNamesCache[type] ? px(node, value) : value;
+       }
+
+       var _floatStyle = d.isIE ? "styleFloat" : "cssFloat";
+       var _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle };
+       
+       // public API
+       
+       dojo.style = function(  /*DomNode|String*/ node, 
+                                                       /*String?|Object?*/ style, 
+                                                       /*String?*/ value){
+               //      summary:
+               //              Accesses styles on a node. If 2 arguments are
+               //              passed, acts as a getter. If 3 arguments are passed, acts
+               //              as a setter.
+               //      node:
+               //              id or reference to node to get/set style for
+               //      style:
+               //              the style property to set in DOM-accessor format
+               //              ("borderWidth", not "border-width") or an object with key/value
+               //              pairs suitable for setting each property.
+               //      value:
+               //              If passed, sets value on the node for style, handling
+               //              cross-browser concerns.
+               //      example:
+               //              Passing only an ID or node returns the computed style object of
+               //              the node:
+               //      |       dojo.style("thinger");
+               //      example:
+               //              Passing a node and a style property returns the current
+               //              normalized, computed value for that property:
+               //      |       dojo.style("thinger", "opacity"); // 1 by default
+               //
+               //      example:
+               //              Passing a node, a style property, and a value changes the
+               //              current display of the node and returns the new computed value
+               //      |       dojo.style("thinger", "opacity", 0.5); // == 0.5
+               //
+               //      example:
+               //              Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
+               //      |       dojo.style("thinger", {
+               //      |               "opacity": 0.5,
+               //      |               "border": "3px solid black",
+               //      |               "height": 300
+               //      |       });
+               //
+               //      example:
+               //              When the CSS style property is hyphenated, the JavaScript property is camelCased.
+               //              font-size becomes fontSize, and so on.
+               //      |       dojo.style("thinger",{
+               //      |               fontSize:"14pt",
+               //      |               letterSpacing:"1.2em"
+               //      |       });
+               //
+               //      example:
+               //              dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
+               //              dojo.style() on every element of the list. See: dojo.query and dojo.NodeList
+               //      |       dojo.query(".someClassName").style("visibility","hidden");
+               //      |       // or
+               //      |       dojo.query("#baz > div").style({
+               //      |               opacity:0.75,
+               //      |               fontSize:"13pt"
+               //      |       });
+
+               var n = d.byId(node), args = arguments.length, op = (style=="opacity");
+               style = _floatAliases[style] || style;
+               if(args == 3){
+                       return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/
+               }
+               if(args == 2 && op){
+                       return d._getOpacity(n);
+               }
+               var s = gcs(n);
+               if(args == 2 && !d.isString(style)){
+                       for(var x in style){
+                               d.style(node, x, style[x]);
+                       }
+                       return s;
+               }
+               return (args == 1) ? s : _toStyleValue(n, style, s[style]); /* CSS2Properties||String||Number */
+       }
+
+       // =============================
+       // Box Functions
+       // =============================
+
+       dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){
+               //      summary:
+               //              Returns object with special values specifically useful for node
+               //              fitting.
+               //
+               //              * l/t = left/top padding (respectively)
+               //              * w = the total of the left and right padding 
+               //              * h = the total of the top and bottom padding
+               //
+               //              If 'node' has position, l/t forms the origin for child nodes. 
+               //              The w/h are used for calculating boxes.
+               //              Normally application code will not need to invoke this
+               //              directly, and will use the ...box... functions instead.
+               var 
+                       s = computedStyle||gcs(n), 
+                       l = px(n, s.paddingLeft), 
+                       t = px(n, s.paddingTop);
+               return { 
+                       l: l,
+                       t: t,
+                       w: l+px(n, s.paddingRight),
+                       h: t+px(n, s.paddingBottom)
+               };
+       }
+
+       dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
+               //      summary:
+               //              returns an object with properties useful for noting the border
+               //              dimensions.
+               //
+               //              * l/t = the sum of left/top border (respectively)
+               //              * w = the sum of the left and right border
+               //              * h = the sum of the top and bottom border
+               //
+               //              The w/h are used for calculating boxes.
+               //              Normally application code will not need to invoke this
+               //              directly, and will use the ...box... functions instead.
+               var 
+                       ne = "none",
+                       s = computedStyle||gcs(n), 
+                       bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
+                       bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
+               return { 
+                       l: bl,
+                       t: bt,
+                       w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),
+                       h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)
+               };
+       }
+
+       dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
+               //      summary:
+               //              returns object with properties useful for box fitting with
+               //              regards to padding.
+               //
+               //              * l/t = the sum of left/top padding and left/top border (respectively)
+               //              * w = the sum of the left and right padding and border
+               //              * h = the sum of the top and bottom padding and border
+               //
+               //              The w/h are used for calculating boxes.
+               //              Normally application code will not need to invoke this
+               //              directly, and will use the ...box... functions instead.
+               var 
+                       s = computedStyle||gcs(n), 
+                       p = d._getPadExtents(n, s),
+                       b = d._getBorderExtents(n, s);
+               return { 
+                       l: p.l + b.l,
+                       t: p.t + b.t,
+                       w: p.w + b.w,
+                       h: p.h + b.h
+               };
+       }
+
+       dojo._getMarginExtents = function(n, computedStyle){
+               //      summary:
+               //              returns object with properties useful for box fitting with
+               //              regards to box margins (i.e., the outer-box).
+               //
+               //              * l/t = marginLeft, marginTop, respectively
+               //              * w = total width, margin inclusive
+               //              * h = total height, margin inclusive
+               //
+               //              The w/h are used for calculating boxes.
+               //              Normally application code will not need to invoke this
+               //              directly, and will use the ...box... functions instead.
+               var 
+                       s = computedStyle||gcs(n), 
+                       l = px(n, s.marginLeft),
+                       t = px(n, s.marginTop),
+                       r = px(n, s.marginRight),
+                       b = px(n, s.marginBottom);
+               if(d.isSafari && (s.position != "absolute")){
+                       // FIXME: Safari's version of the computed right margin
+                       // is the space between our right edge and the right edge 
+                       // of our offsetParent. 
+                       // What we are looking for is the actual margin value as 
+                       // determined by CSS.
+                       // Hack solution is to assume left/right margins are the same.
+                       r = l;
+               }
+               return { 
+                       l: l,
+                       t: t,
+                       w: l+r,
+                       h: t+b
+               };
+       }
+
+       // Box getters work in any box context because offsetWidth/clientWidth
+       // are invariant wrt box context
+       //
+       // They do *not* work for display: inline objects that have padding styles
+       // because the user agent ignores padding (it's bogus styling in any case)
+       //
+       // Be careful with IMGs because they are inline or block depending on 
+       // browser and browser mode.
+
+       // Although it would be easier to read, there are not separate versions of 
+       // _getMarginBox for each browser because:
+       // 1. the branching is not expensive
+       // 2. factoring the shared code wastes cycles (function call overhead)
+       // 3. duplicating the shared code wastes bytes
+       
+       dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){
+               // summary:
+               //              returns an object that encodes the width, height, left and top
+               //              positions of the node's margin box.
+               var s = computedStyle||gcs(node), me = d._getMarginExtents(node, s);
+               var     l = node.offsetLeft - me.l,     t = node.offsetTop - me.t;
+               if(d.isMoz){
+                       // Mozilla:
+                       // If offsetParent has a computed overflow != visible, the offsetLeft is decreased
+                       // by the parent's border.
+                       // We don't want to compute the parent's style, so instead we examine node's
+                       // computed left/top which is more stable.
+                       var sl = parseFloat(s.left), st = parseFloat(s.top);
+                       if(!isNaN(sl) && !isNaN(st)){
+                               l = sl, t = st;
+                       }else{
+                               // If child's computed left/top are not parseable as a number (e.g. "auto"), we
+                               // have no choice but to examine the parent's computed style.
+                               var p = node.parentNode;
+                               if(p && p.style){
+                                       var pcs = gcs(p);
+                                       if(pcs.overflow != "visible"){
+                                               var be = d._getBorderExtents(p, pcs);
+                                               l += be.l, t += be.t;
+                                       }
+                               }
+                       }
+               }else if(d.isOpera){
+                       // On Opera, offsetLeft includes the parent's border
+                       var p = node.parentNode;
+                       if(p){
+                               var be = d._getBorderExtents(p);
+                               l -= be.l, t -= be.t;
+                       }
+               }
+               return { 
+                       l: l, 
+                       t: t, 
+                       w: node.offsetWidth + me.w, 
+                       h: node.offsetHeight + me.h 
+               };
+       }
+       
+       dojo._getContentBox = function(node, computedStyle){
+               // summary:
+               //              Returns an object that encodes the width, height, left and top
+               //              positions of the node's content box, irrespective of the
+               //              current box model.
+
+               // clientWidth/Height are important since the automatically account for scrollbars
+               // fallback to offsetWidth/Height for special cases (see #3378)
+               var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), be=d._getBorderExtents(node, s), w=node.clientWidth, h;
+               if(!w){
+                       w=node.offsetWidth, h=node.offsetHeight;
+               }else{
+                       h=node.clientHeight, be.w = be.h = 0; 
+               }
+               // On Opera, offsetLeft includes the parent's border
+               if(d.isOpera){ pe.l += be.l; pe.t += be.t; };
+               return { 
+                       l: pe.l, 
+                       t: pe.t, 
+                       w: w - pe.w - be.w, 
+                       h: h - pe.h - be.h
+               };
+       }
+
+       dojo._getBorderBox = function(node, computedStyle){
+               var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), cb=d._getContentBox(node, s);
+               return { 
+                       l: cb.l - pe.l, 
+                       t: cb.t - pe.t, 
+                       w: cb.w + pe.w, 
+                       h: cb.h + pe.h
+               };
+       }
+
+       // Box setters depend on box context because interpretation of width/height styles
+       // vary wrt box context.
+       //
+       // The value of dojo.boxModel is used to determine box context.
+       // dojo.boxModel can be set directly to change behavior.
+       //
+       // Beware of display: inline objects that have padding styles
+       // because the user agent ignores padding (it's a bogus setup anyway)
+       //
+       // Be careful with IMGs because they are inline or block depending on 
+       // browser and browser mode.
+       // 
+       // Elements other than DIV may have special quirks, like built-in
+       // margins or padding, or values not detectable via computedStyle.
+       // In particular, margins on TABLE do not seems to appear 
+       // at all in computedStyle on Mozilla.
+       
+       dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){
+               //      summary:
+               //              sets width/height/left/top in the current (native) box-model
+               //              dimentions. Uses the unit passed in u.
+               //      node: DOM Node reference. Id string not supported for performance reasons.
+               //      l: optional. left offset from parent.
+               //      t: optional. top offset from parent.
+               //      w: optional. width in current box model.
+               //      h: optional. width in current box model.
+               //      u: optional. unit measure to use for other measures. Defaults to "px".
+               u = u || "px";
+               var s = node.style;
+               if(!isNaN(l)){ s.left = l+u; }
+               if(!isNaN(t)){ s.top = t+u; }
+               if(w>=0){ s.width = w+u; }
+               if(h>=0){ s.height = h+u; }
+       }
+
+       dojo._usesBorderBox = function(/*DomNode*/node){
+               //      summary: 
+               //              True if the node uses border-box layout.
+
+               // We could test the computed style of node to see if a particular box
+               // has been specified, but there are details and we choose not to bother.
+               var n = node.tagName;
+               // For whatever reason, TABLE and BUTTON are always border-box by default.
+               // If you have assigned a different box to either one via CSS then
+               // box functions will break.
+               return d.boxModel=="border-box" || n=="TABLE" || n=="BUTTON"; // boolean
+       }
+
+       dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){
+               //      summary:
+               //              Sets the size of the node's contents, irrespective of margins,
+               //              padding, or borders.
+               if(d._usesBorderBox(node)){
+                       var pb = d._getPadBorderExtents(node, computedStyle);
+                       if(widthPx >= 0){ widthPx += pb.w; }
+                       if(heightPx >= 0){ heightPx += pb.h; }
+               }
+               d._setBox(node, NaN, NaN, widthPx, heightPx);
+       }
+
+       dojo._setMarginBox = function(/*DomNode*/node,  /*Number?*/leftPx, /*Number?*/topPx, 
+                                                                                                       /*Number?*/widthPx, /*Number?*/heightPx, 
+                                                                                                       /*Object*/computedStyle){
+               //      summary:
+               //              sets the size of the node's margin box and placement
+               //              (left/top), irrespective of box model. Think of it as a
+               //              passthrough to dojo._setBox that handles box-model vagaries for
+               //              you.
+
+               var s = computedStyle||gcs(node);
+               // Some elements have special padding, margin, and box-model settings. 
+               // To use box functions you may need to set padding, margin explicitly.
+               // Controlling box-model is harder, in a pinch you might set dojo.boxModel.
+               var bb=d._usesBorderBox(node),
+                               pb=bb ? _nilExtents : d._getPadBorderExtents(node, s),
+                               mb=d._getMarginExtents(node, s);
+               if(widthPx>=0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); }
+               if(heightPx>=0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); }
+               d._setBox(node, leftPx, topPx, widthPx, heightPx);
+       }
+       
+       var _nilExtents = { l:0, t:0, w:0, h:0 };
+
+       // public API
+       
+       dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){
+               //      summary:
+               //              Getter/setter for the margin-box of node.
+               //      description: 
+               //              Returns an object in the expected format of box (regardless
+               //              if box is passed). The object might look like:
+               //                      `{ l: 50, t: 200, w: 300: h: 150 }`
+               //              for a node offset from its parent 50px to the left, 200px from
+               //              the top with a margin width of 300px and a margin-height of
+               //              150px.
+               //      node:
+               //              id or reference to DOM Node to get/set box for
+               //      box:
+               //              If passed, denotes that dojo.marginBox() should
+               //              update/set the margin box for node. Box is an object in the
+               //              above format. All properties are optional if passed.
+               var n=d.byId(node), s=gcs(n), b=box;
+               return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object
+       }
+
+       dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){
+               //      summary:
+               //              Getter/setter for the content-box of node.
+               //      description:
+               //              Returns an object in the expected format of box (regardless if box is passed).
+               //              The object might look like:
+               //                      `{ l: 50, t: 200, w: 300: h: 150 }`
+               //              for a node offset from its parent 50px to the left, 200px from
+               //              the top with a content width of 300px and a content-height of
+               //              150px. Note that the content box may have a much larger border
+               //              or margin box, depending on the box model currently in use and
+               //              CSS values set/inherited for node.
+               //      node:
+               //              id or reference to DOM Node to get/set box for
+               //      box:
+               //              If passed, denotes that dojo.contentBox() should
+               //              update/set the content box for node. Box is an object in the
+               //              above format. All properties are optional if passed.
+               var n=dojo.byId(node), s=gcs(n), b=box;
+               return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object
+       }
+       
+       // =============================
+       // Positioning 
+       // =============================
+       
+       var _sumAncestorProperties = function(node, prop){
+               if(!(node = (node||0).parentNode)){return 0};
+               var val, retVal = 0, _b = d.body();
+               while(node && node.style){
+                       if(gcs(node).position == "fixed"){
+                               return 0;
+                       }
+                       val = node[prop];
+                       if(val){
+                               retVal += val - 0;
+                               // opera and khtml #body & #html has the same values, we only
+                               // need one value
+                               if(node == _b){ break; }
+                       }
+                       node = node.parentNode;
+               }
+               return retVal;  //      integer
+       }
+
+       dojo._docScroll = function(){
+               var 
+                       _b = d.body(),
+                       _w = d.global,
+                       de = d.doc.documentElement;
+               return {
+                       y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0),
+                       x: (_w.pageXOffset || d._fixIeBiDiScrollLeft(de.scrollLeft) || _b.scrollLeft || 0)
+               };
+       };
+       
+       dojo._isBodyLtr = function(){
+               //FIXME: could check html and body tags directly instead of computed style?  need to ignore case, accept empty values
+               return !("_bodyLtr" in d) ? 
+                       d._bodyLtr = gcs(d.body()).direction == "ltr" :
+                       d._bodyLtr; // Boolean 
+       }
+       
+       dojo._getIeDocumentElementOffset = function(){
+               // summary
+               // The following values in IE contain an offset:
+               //     event.clientX 
+               //     event.clientY 
+               //     node.getBoundingClientRect().left
+               //     node.getBoundingClientRect().top
+               // But other position related values do not contain this offset, such as
+               // node.offsetLeft, node.offsetTop, node.style.left and node.style.top.
+               // The offset is always (2, 2) in LTR direction. When the body is in RTL
+               // direction, the offset counts the width of left scroll bar's width.
+               // This function computes the actual offset.
+
+               //NOTE: assumes we're being called in an IE browser
+
+               var de = d.doc.documentElement;
+               //FIXME: use this instead?                      var de = d.compatMode == "BackCompat" ? d.body : d.documentElement;
+
+               return (d.isIE >= 7) ?
+                       {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}
+               :
+                       // IE 6.0
+                       {x: d._isBodyLtr() || window.parent == window ?
+                               de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft, 
+                               y: de.clientTop}; // Object
+       };
+       
+       dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){
+               // In RTL direction, scrollLeft should be a negative value, but IE 
+               // returns a positive one. All codes using documentElement.scrollLeft
+               // must call this function to fix this error, otherwise the position
+               // will offset to right when there is a horizontal scrollbar.
+               var dd = d.doc;
+               if(d.isIE && !dojo._isBodyLtr()){
+                       var de = dd.compatMode == "BackCompat" ? dd.body : dd.documentElement;
+                       return scrollLeft + de.clientWidth - de.scrollWidth; // Integer
+               }
+               return scrollLeft; // Integer
+       }
+
+       dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll){
+               //      summary:
+               //              Gets the position of the passed element relative to
+               //              the viewport (if includeScroll==false), or relative to the
+               //              document root (if includeScroll==true).
+               //
+               //              Returns an object of the form:
+               //                      { x: 100, y: 300 }
+               //              if includeScroll is passed, the x and y values will include any
+               //              document offsets that may affect the position relative to the
+               //              viewport.
+
+               // FIXME: need to decide in the brave-new-world if we're going to be
+               // margin-box or border-box.
+               var ownerDocument = node.ownerDocument;
+               var ret = {
+                       x: 0,
+                       y: 0
+               };
+
+               // targetBoxType == "border-box"
+               var db = d.body();
+               if(d.isIE || (d.isFF >= 3)){
+                       var client = node.getBoundingClientRect();
+                       var offset = (d.isIE) ? d._getIeDocumentElementOffset() : { x: 0, y: 0};
+                       ret.x = client.left - offset.x;
+                       ret.y = client.top - offset.y;
+               }else if(ownerDocument["getBoxObjectFor"]){
+                       // mozilla
+                       var bo = ownerDocument.getBoxObjectFor(node),
+                               b = d._getBorderExtents(node);
+                       ret.x = bo.x - b.l - _sumAncestorProperties(node, "scrollLeft");
+                       ret.y = bo.y - b.t - _sumAncestorProperties(node, "scrollTop");
+               }else{
+                       if(node["offsetParent"]){
+                               var endNode;
+                               // in Safari, if the node is an absolutely positioned child of
+                               // the body and the body has a margin the offset of the child
+                               // and the body contain the body's margins, so we need to end
+                               // at the body
+                               // FIXME: getting contrary results to the above in latest WebKit.
+                               if(d.isSafari &&
+                                       //(node.style.getPropertyValue("position") == "absolute") &&
+                                       (gcs(node).position == "absolute") &&
+                                       (node.parentNode == db)){
+                                       endNode = db;
+                               }else{
+                                       endNode = db.parentNode;
+                               }
+                               if(node.parentNode != db){
+                                       var nd = node;
+                                       if(d.isOpera){ nd = db; }
+                                       ret.x -= _sumAncestorProperties(nd, "scrollLeft");
+                                       ret.y -= _sumAncestorProperties(nd, "scrollTop");
+                               }
+                               var curnode = node;
+                               do{
+                                       var n = curnode.offsetLeft;
+                                       //FIXME: ugly hack to workaround the submenu in 
+                                       //popupmenu2 does not shown up correctly in opera. 
+                                       //Someone have a better workaround?
+                                       if(!d.isOpera || n > 0){
+                                               ret.x += isNaN(n) ? 0 : n;
+                                       }
+                                       var t = curnode.offsetTop;
+                                       ret.y += isNaN(t) ? 0 : t;
+                                       if(d.isSafari && curnode != node){
+                                               var cs = gcs(curnode);
+                                               ret.x += px(curnode, cs.borderLeftWidth);
+                                               ret.y += px(curnode, cs.borderTopWidth);
+                                       }
+                                       curnode = curnode.offsetParent;
+                               }while((curnode != endNode) && curnode);
+                       }else if(node.x && node.y){
+                               ret.x += isNaN(node.x) ? 0 : node.x;
+                               ret.y += isNaN(node.y) ? 0 : node.y;
+                       }
+               }
+               // account for document scrolling
+               // if offsetParent is used, ret value already includes scroll position
+               // so we may have to actually remove that value if !includeScroll
+               if(includeScroll){
+                       var scroll = d._docScroll();
+                       ret.y += scroll.y;
+                       ret.x += scroll.x;
+               }
+
+               return ret; // object
+       }
+
+       // FIXME: need a setter for coords or a moveTo!!
+       dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
+               //      summary:
+               //              Returns an object that measures margin box width/height and
+               //              absolute positioning data from dojo._abs().
+               //
+               //      description:
+               //              Returns an object that measures margin box width/height and
+               //              absolute positioning data from dojo._abs().
+               //              Return value will be in the form:
+               //                      `{ l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }`
+               //              Does not act as a setter. If includeScroll is passed, the x and
+               //              y params are affected as one would expect in dojo._abs().
+               var n=d.byId(node), s=gcs(n), mb=d._getMarginBox(n, s);
+               var abs = d._abs(n, includeScroll);
+               mb.x = abs.x;
+               mb.y = abs.y;
+               return mb;
+       }
+
+       // =============================
+       // Element attribute Functions
+       // =============================
+
+       var _fixAttrName = function(/*String*/name){
+               switch(name.toLowerCase()){
+                       case "tabindex":
+                               // Internet Explorer will only set or remove tabindex
+                               // if it is spelled "tabIndex"
+                               // console.debug((dojo.isIE && dojo.isIE < 8)? "tabIndex" : "tabindex");
+                               return (d.isIE && d.isIE < 8) ? "tabIndex" : "tabindex";
+                       default:
+                               return name;
+               }
+       }
+
+       // non-deprecated HTML4 attributes with default values
+       // http://www.w3.org/TR/html401/index/attributes.html
+       // FF and Safari will return the default values if you
+       // access the attributes via a property but not
+       // via getAttribute()
+       var _attrProps = {
+               colspan: "colSpan",
+               enctype: "enctype",
+               frameborder: "frameborder",
+               method: "method",
+               rowspan: "rowSpan",
+               scrolling: "scrolling",
+               shape: "shape",
+               span: "span",
+               type: "type",
+               valuetype: "valueType"
+       }
+
+       dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){
+               //      summary:
+               //              Returns true if the requested attribute is specified on the
+               //              given element, and false otherwise.
+               //      node:
+               //              id or reference to the element to check
+               //      name:
+               //              the name of the attribute
+               //      returns:
+               //              true if the requested attribute is specified on the
+               //              given element, and false otherwise
+               var attr = d.byId(node).getAttributeNode(_fixAttrName(name));
+               return attr ? attr.specified : false; // Boolean
+       }
+
+       var _evtHdlrMap = {
+               
+       }
+
+       var _ctr = 0;
+       var _attrId = dojo._scopeName + "attrid";
+
+       dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){
+               //      summary:
+               //              Gets or sets an attribute on an HTML element.
+               //      description:
+               //              Handles normalized getting and setting of attributes on DOM
+               //              Nodes. If 2 arguments are passed, and a the second argumnt is a
+               //              string, acts as a getter.
+               //      
+               //              If a third argument is passed, or if the second argumnt is a
+               //              map of attributes, acts as a setter.
+               //
+               //              When passing functions as values, note that they will not be
+               //              directly assigned to slots on the node, but rather the default
+               //              behavior will be removed and the new behavior will be added
+               //              using `dojo.connect()`, meaning that event handler properties
+               //              will be normalized and that some caveats with regards to
+               //              non-standard behaviors for onsubmit apply. Namely that you
+               //              should cancel form submission using `dojo.stopEvent()` on the
+               //              passed event object instead of returning a boolean value from
+               //              the handler itself.
+               //      node:
+               //              id or reference to the element to get or set the attribute on
+               //      name:
+               //              the name of the attribute to get or set.
+               //      value:
+               //              The value to set for the attribute
+               //      returns:
+               //              when used as a getter, the value of the requested attribute
+               //              or null if that attribute does not have a specified or
+               //              default value;
+               //
+               //              when user as a setter, undefined
+               //      example:
+               //      |       // get the current value of the "foo" attribute on a node
+               //      |       dojo.attr(dojo.byId("nodeId"), "foo");
+               //      |       
+               //      |       // we can just pass the id:
+               //      |       dojo.attr("nodeId", "foo");
+               //      |
+               //      |       // use attr() to set the tab index
+               //      |       dojo.attr("nodeId", "tabindex", 3);
+               //      |
+               //      |       // set multiple values at once, including event handlers:
+               //      |       dojo.attr("formId", {
+               //      |               "foo": "bar",
+               //      |               "tabindex": -1,
+               //      |               "method": "POST",
+               //      |               "onsubmit": function(e){
+               //      |                       // stop submitting the form. Note that the IE behavior
+               //      |                       // of returning true or false will have no effect here
+               //      |                       // since our handler is connect()ed to the built-in
+               //      |                       // onsubmit behavior and so we need to use
+               //      |                       // dojo.stopEvent() to ensure that the submission
+               //      |                       // doesn't proceed.
+               //      |                       dojo.stopEvent(e);
+               //      |
+               //      |                       // submit the form with Ajax
+               //      |                       dojo.xhrPost({ form: "formId" });
+               //      |               }
+               //      |       });
+
+               var args = arguments.length;
+               if(args == 2 && !d.isString(name)){
+                       for(var x in name){ d.attr(node, x, name[x]); }
+                       return;
+               }
+               node = d.byId(node);
+               name = _fixAttrName(name);
+               if(args == 3){
+                       if(d.isFunction(value)){
+                               // clobber if we can
+                               var attrId = d.attr(node, _attrId);
+                               if(!attrId){
+                                       attrId = _ctr++;
+                                       d.attr(node, _attrId, attrId);
+                               }
+                               if(!_evtHdlrMap[attrId]){
+                                       _evtHdlrMap[attrId] = {};
+                               }
+                               var h = _evtHdlrMap[attrId][name];
+                               if(h){
+                                       d.disconnect(h);
+                               }else{
+                                       try{
+                                               delete node[name];
+                                       }catch(e){}
+                               }
+
+                               // ensure that event objects are normalized, etc.
+                               _evtHdlrMap[attrId][name] = d.connect(node, name, value);
+
+                       }else if(typeof value == "boolean"){ // e.g. onsubmit, disabled
+                               // if a function, we should normalize the event object here!!!
+                               node[name] = value;
+                       }else{
+                               node.setAttribute(name, value);
+                       }
+                       return;
+               }else{
+                       // should we access this attribute via a property or
+                       // via getAttribute()?
+                       var prop = _attrProps[name.toLowerCase()];
+                       if(prop){
+                               return node[prop];
+                       }else{
+                               var value = node[name];
+                               return (typeof value == 'boolean' || typeof value == 'function') ? value : (d.hasAttr(node, name) ? node.getAttribute(name) : null);
+                       }
+               }
+       }
+
+       dojo.removeAttr = function(/*DomNode|String*/node, /*String*/name){
+               //      summary:
+               //              Removes an attribute from an HTML element.
+               //      node:
+               //              id or reference to the element to remove the attribute from
+               //      name:
+               //              the name of the attribute to remove
+               d.byId(node).removeAttribute(_fixAttrName(name));
+       }
+})();
+
+// =============================
+// (CSS) Class Functions
+// =============================
+
+dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){
+       //      summary:
+       //              Returns whether or not the specified classes are a portion of the
+       //              class list currently applied to the node. 
+       return ((" "+dojo.byId(node).className+" ").indexOf(" "+classStr+" ") >= 0);  // Boolean
+};
+
+dojo.addClass = function(/*DomNode|String*/node, /*String*/classStr){
+       //      summary:
+       //              Adds the specified classes to the end of the class list on the
+       //              passed node.
+       node = dojo.byId(node);
+       var cls = node.className;
+       if((" "+cls+" ").indexOf(" "+classStr+" ") < 0){
+               node.className = cls + (cls ? ' ' : '') + classStr;
+       }
+};
+
+dojo.removeClass = function(/*DomNode|String*/node, /*String*/classStr){
+       // summary: Removes the specified classes from node.
+       node = dojo.byId(node);
+       var t = dojo.trim((" " + node.className + " ").replace(" " + classStr + " ", " "));
+       if(node.className != t){ node.className = t; }
+};
+
+dojo.toggleClass = function(/*DomNode|String*/node, /*String*/classStr, /*Boolean?*/condition){
+       //      summary:        
+       //              Adds a class to node if not present, or removes if present.
+       //              Pass a boolean condition if you want to explicitly add or remove.
+       //      condition:
+       //              If passed, true means to add the class, false means to remove.
+       if(condition === undefined){
+               condition = !dojo.hasClass(node, classStr);
+       }
+       dojo[condition ? "addClass" : "removeClass"](node, classStr);
+};
+
+}
+
+if(!dojo._hasResource["dojo._base.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.NodeList"] = true;
+dojo.provide("dojo._base.NodeList");
+
+
+
+(function(){
+
+       var d = dojo;
+
+       var tnl = function(arr){
+               // decorate an array to make it look like a NodeList
+               arr.constructor = dojo.NodeList;
+               dojo._mixin(arr, dojo.NodeList.prototype);
+               return arr;
+       }
+
+       var _mapIntoDojo = function(func, alwaysThis){
+               // returns a function which, when executed in the scope of its caller,
+               // applies the passed arguments to a particular dojo.* function (named
+               // in func) and aggregates the returns. if alwaysThis is true, it
+               // always returns the scope object and not the collected returns from
+               // the Dojo method
+               return function(){
+                       var _a = arguments;
+                       var aa = d._toArray(_a, 0, [null]);
+                       var s = this.map(function(i){
+                               aa[0] = i;
+                               return d[func].apply(d, aa);
+                       });
+                       return (alwaysThis || ( (_a.length > 1) || !d.isString(_a[0]) )) ? this : s; // String||dojo.NodeList
+               }
+       };
+
+       dojo.NodeList = function(){
+               //      summary:
+               //              dojo.NodeList is as subclass of Array which adds syntactic 
+               //              sugar for chaining, common iteration operations, animation, 
+               //              and node manipulation. NodeLists are most often returned as
+               //              the result of dojo.query() calls.
+               //      example:
+               //              create a node list from a node
+               //              |       new dojo.NodeList(dojo.byId("foo"));
+
+               return tnl(Array.apply(null, arguments));
+       }
+
+       dojo.NodeList._wrap = tnl;
+
+       dojo.extend(dojo.NodeList, {
+               // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods
+
+               // FIXME: handle return values for #3244
+               //              http://trac.dojotoolkit.org/ticket/3244
+               
+               // FIXME:
+               //              need to wrap or implement:
+               //                      join (perhaps w/ innerHTML/outerHTML overload for toString() of items?)
+               //                      reduce
+               //                      reduceRight
+
+               slice: function(/*===== begin, end =====*/){
+                       // summary:
+                       //              Returns a new NodeList, maintaining this one in place
+                       // description:
+                       //              This method behaves exactly like the Array.slice method
+                       //              with the caveat that it returns a dojo.NodeList and not a
+                       //              raw Array. For more details, see:
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice
+                       // begin: Integer
+                       //              Can be a positive or negative integer, with positive
+                       //              integers noting the offset to begin at, and negative
+                       //              integers denoting an offset from the end (i.e., to the left
+                       //              of the end)
+                       // end: Integer?
+                       //              Optional parameter to describe what position relative to
+                       //              the NodeList's zero index to end the slice at. Like begin,
+                       //              can be positive or negative.
+                       var a = dojo._toArray(arguments);
+                       return tnl(a.slice.apply(this, a));
+               },
+
+               splice: function(/*===== index, howmany, item =====*/){
+                       // summary:
+                       //              Returns a new NodeList, manipulating this NodeList based on
+                       //              the arguments passed, potentially splicing in new elements
+                       //              at an offset, optionally deleting elements
+                       // description:
+                       //              This method behaves exactly like the Array.splice method
+                       //              with the caveat that it returns a dojo.NodeList and not a
+                       //              raw Array. For more details, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice>
+                       // index: Integer
+                       //              begin can be a positive or negative integer, with positive
+                       //              integers noting the offset to begin at, and negative
+                       //              integers denoting an offset from the end (i.e., to the left
+                       //              of the end)
+                       // howmany: Integer?
+                       //              Optional parameter to describe what position relative to
+                       //              the NodeList's zero index to end the slice at. Like begin,
+                       //              can be positive or negative.
+                       // item: Object...?
+                       //              Any number of optional parameters may be passed in to be
+                       //              spliced into the NodeList
+                       // returns:
+                       //              dojo.NodeList
+                       var a = dojo._toArray(arguments);
+                       return tnl(a.splice.apply(this, a));
+               },
+
+               concat: function(/*===== item =====*/){
+                       // summary:
+                       //              Returns a new NodeList comprised of items in this NodeList
+                       //              as well as items passed in as parameters
+                       // description:
+                       //              This method behaves exactly like the Array.concat method
+                       //              with the caveat that it returns a dojo.NodeList and not a
+                       //              raw Array. For more details, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat>
+                       // item: Object...?
+                       //              Any number of optional parameters may be passed in to be
+                       //              spliced into the NodeList
+                       // returns:
+                       //              dojo.NodeList
+                       var a = dojo._toArray(arguments, 0, [this]);
+                       return tnl(a.concat.apply([], a));
+               },
+               
+               indexOf: function(/*Object*/ value, /*Integer?*/ fromIndex){
+                       //      summary:
+                       //              see dojo.indexOf(). The primary difference is that the acted-on 
+                       //              array is implicitly this NodeList
+                       // value:
+                       //              The value to search for.
+                       // fromIndex:
+                       //              The loction to start searching from. Optional. Defaults to 0.
+                       //      description:
+                       //              For more details on the behavior of indexOf, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf>
+                       //      returns:
+                       //              Positive Integer or 0 for a match, -1 of not found.
+                       return d.indexOf(this, value, fromIndex); // Integer
+               },
+
+               lastIndexOf: function(/*===== value, fromIndex =====*/){
+                       // summary:
+                       //              see dojo.lastIndexOf(). The primary difference is that the
+                       //              acted-on array is implicitly this NodeList
+                       //      description:
+                       //              For more details on the behavior of lastIndexOf, see:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf>
+                       // value: Object
+                       //              The value to search for.
+                       // fromIndex: Integer?
+                       //              The loction to start searching from. Optional. Defaults to 0.
+                       // returns:
+                       //              Positive Integer or 0 for a match, -1 of not found.
+                       return d.lastIndexOf.apply(d, d._toArray(arguments, 0, [this])); // Integer
+               },
+
+               every: function(/*Function*/callback, /*Object?*/thisObject){
+                       //      summary:
+                       //              see `dojo.every()` and:
+                       //                      <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every>
+                       //              Takes the same structure of arguments and returns as
+                       //              dojo.every() with the caveat that the passed array is
+                       //              implicitly this NodeList
+                       return d.every(this, callback, thisObject); // Boolean
+               },
+
+               some: function(/*Function*/callback, /*Object?*/thisObject){
+                       //      summary:
+                       //              see dojo.some() and:
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
+                       //              Takes the same structure of arguments and returns as
+                       //              dojo.some() with the caveat that the passed array is
+                       //              implicitly this NodeList
+                       return d.some(this, callback, thisObject); // Boolean
+               },
+
+               map: function(/*Function*/ func, /*Function?*/ obj){
+                       //      summary:
+                       //              see dojo.map(). The primary difference is that the acted-on
+                       //              array is implicitly this NodeList and the return is a
+                       //              dojo.NodeList (a subclass of Array)
+
+                       return d.map(this, func, obj, d.NodeList); // dojo.NodeList
+               },
+
+               forEach: function(callback, thisObj){
+                       //      summary:
+                       //              see dojo.forEach(). The primary difference is that the acted-on 
+                       //              array is implicitly this NodeList
+
+                       d.forEach(this, callback, thisObj);
+                       // non-standard return to allow easier chaining
+                       return this; // dojo.NodeList 
+               },
+
+               // custom methods
+               
+               coords: function(){
+                       //      summary:
+                       //              Returns the box objects all elements in a node list as
+                       //              an Array (*not* a NodeList)
+                       
+                       return d.map(this, d.coords); // Array
+               },
+
+               /*=====
+               attr: function(property, value){
+                       //      summary:
+                       //              gets or sets the DOM attribute for every element in the
+                       //              NodeList
+                       //      property: String
+                       //              the attribute to get/set
+                       //      value: String?
+                       //              optional. The value to set the property to
+                       //      return:
+                       //              if no value is passed, the result is an array of attribute values
+                       //              If a value is passed, the return is this NodeList
+               },
+
+               style: function(property, value){
+                       //      summary:
+                       //              gets or sets the CSS property for every element in the NodeList
+                       //      property: String
+                       //              the CSS property to get/set, in JavaScript notation
+                       //              ("lineHieght" instead of "line-height") 
+                       //      value: String?
+                       //              optional. The value to set the property to
+                       //      return:
+                       //              if no value is passed, the result is an array of strings.
+                       //              If a value is passed, the return is this NodeList
+               },
+
+               addClass: function(className){
+                       //      summary:
+                       //              adds the specified class to every node in the list
+                       //      className: String
+                       //              the CSS class to add
+                       //      return:
+                       //              dojo.NodeList, this list
+               },
+
+               removeClass: function(className){
+                       //      summary:
+                       //              removes the specified class from every node in the list
+                       //      className: String
+                       //              the CSS class to add
+                       //      return:
+                       //              dojo.NodeList, this list
+               },
+
+               toggleClass: function(className, condition){
+                       //      summary:
+                       //              Adds a class to node if not present, or removes if present.
+                       //              Pass a boolean condition if you want to explicitly add or remove.
+                       //      condition: Boolean?
+                       //              If passed, true means to add the class, false means to remove.
+                       //      className: String
+                       //              the CSS class to add
+                       //      return: dojo.NodeList
+                       //              this list
+               },
+
+               connect: function(methodName, objOrFunc, funcName){
+                       //      summary:
+                       //              attach event handlers to every item of the NodeList. Uses dojo.connect()
+                       //              so event properties are normalized
+                       //      methodName: String
+                       //              the name of the method to attach to. For DOM events, this should be
+                       //              the lower-case name of the event
+                       //      objOrFunc: Object|Function|String
+                       //              if 2 arguments are passed (methodName, objOrFunc), objOrFunc should
+                       //              reference a function or be the name of the function in the global
+                       //              namespace to attach. If 3 arguments are provided
+                       //              (methodName, objOrFunc, funcName), objOrFunc must be the scope to 
+                       //              locate the bound function in
+                       //      funcName: String?
+                       //              optional. A string naming the function in objOrFunc to bind to the
+                       //              event. May also be a function reference.
+                       //      example:
+                       //              add an onclick handler to every button on the page
+                       //              |       dojo.query("div:nth-child(odd)").connect("onclick", function(e){
+                       //              |               console.debug("clicked!");
+                       //              |       });
+                       // example:
+                       //              attach foo.bar() to every odd div's onmouseover
+                       //              |       dojo.query("div:nth-child(odd)").connect("onmouseover", foo, "bar");
+               },
+               =====*/
+               attr: _mapIntoDojo("attr"),
+               style: _mapIntoDojo("style"),
+               addClass: _mapIntoDojo("addClass", true),
+               removeClass: _mapIntoDojo("removeClass", true),
+               toggleClass: _mapIntoDojo("toggleClass", true),
+               connect: _mapIntoDojo("connect", true),
+
+               // FIXME: connectPublisher()? connectRunOnce()?
+
+               place: function(/*String||Node*/ queryOrNode, /*String*/ position){
+                       //      summary:
+                       //              places elements of this node list relative to the first element matched
+                       //              by queryOrNode. Returns the original NodeList.
+                       //      queryOrNode:
+                       //              may be a string representing any valid CSS3 selector or a DOM node.
+                       //              In the selector case, only the first matching element will be used 
+                       //              for relative positioning.
+                       //      position:
+                       //              can be one of:
+                       //                      * "last"||"end" (default)
+                       //                      * "first||"start"
+                       //                      * "before"
+                       //                      * "after"
+                       //              or an offset in the childNodes property
+                       var item = d.query(queryOrNode)[0];
+                       return this.forEach(function(i){ d.place(i, item, (position||"last")); }); // dojo.NodeList
+               },
+
+               orphan: function(/*String?*/ simpleFilter){
+                       //      summary:
+                       //              removes elements in this list that match the simple
+                       //              filter from their parents and returns them as a new
+                       //              NodeList.
+                       //      simpleFilter:
+                       //              single-expression CSS filter
+                       //      return:
+                       //              `dojo.NodeList` the orpahned elements 
+                       var orphans = simpleFilter ? d._filterQueryResult(this, simpleFilter) : this;
+                       orphans.forEach(function(item){
+                               if(item.parentNode){
+                                       item.parentNode.removeChild(item);
+                               }
+                       });
+                       return orphans; // dojo.NodeList
+               },
+
+               adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
+                       //      summary:
+                       //              places any/all elements in queryOrListOrNode at a
+                       //              position relative to the first element in this list.
+                       //              Returns a dojo.NodeList of the adopted elements.
+                       //      queryOrListOrNode:
+                       //              a DOM node or a query string or a query result.
+                       //              Represents the nodes to be adopted relative to the
+                       //              first element of this NodeList.
+                       //      position:
+                       //              can be one of:
+                       //                      * "last"||"end" (default)
+                       //                      * "first||"start"
+                       //                      * "before"
+                       //                      * "after"
+                       //              or an offset in the childNodes property
+                       var item = this[0];
+                       return d.query(queryOrListOrNode).forEach(function(ai){ d.place(ai, item, position || "last"); }); // dojo.NodeList
+               },
+
+               // FIXME: do we need this?
+               query: function(/*String*/ queryStr){
+                       //      summary:
+                       //              Returns a new, flattened NodeList. Elements of the new list
+                       //              satisfy the passed query but use elements of the
+                       //              current NodeList as query roots.
+
+                       if(!queryStr){ return this; }
+
+                       // FIXME: probably slow
+                       // FIXME: use map?
+                       var ret = d.NodeList();
+                       this.forEach(function(item){
+                               d.query(queryStr, item).forEach(function(subItem){
+                                       if(subItem !== undefined){
+                                               ret.push(subItem);
+                                       }
+                               });
+                       });
+                       return ret; // dojo.NodeList
+               },
+
+               filter: function(/*String*/ simpleQuery){
+                       //      summary:
+                       //              "masks" the built-in javascript filter() method to support
+                       //              passing a simple string filter in addition to supporting
+                       //              filtering function objects.
+                       //      example:
+                       //              "regular" JS filter syntax as exposed in dojo.filter:
+                       //              |       dojo.query("*").filter(function(item){
+                       //              |               // highlight every paragraph
+                       //              |               return (item.nodeName == "p");
+                       //              |       }).styles("backgroundColor", "yellow");
+                       // example:
+                       //              the same filtering using a CSS selector
+                       //              |       dojo.query("*").filter("p").styles("backgroundColor", "yellow");
+
+                       var items = this;
+                       var _a = arguments;
+                       var r = d.NodeList();
+                       var rp = function(t){ 
+                               if(t !== undefined){
+                                       r.push(t); 
+                               }
+                       }
+                       if(d.isString(simpleQuery)){
+                               items = d._filterQueryResult(this, _a[0]);
+                               if(_a.length == 1){
+                                       // if we only got a string query, pass back the filtered results
+                                       return items; // dojo.NodeList
+                               }
+                               // if we got a callback, run it over the filtered items
+                               _a.shift();
+                       }
+                       // handle the (callback, [thisObject]) case
+                       d.forEach(d.filter(items, _a[0], _a[1]), rp);
+                       return r; // dojo.NodeList
+               },
+               
+               /*
+               // FIXME: should this be "copyTo" and include parenting info?
+               clone: function(){
+                       // summary:
+                       //              creates node clones of each element of this list
+                       //              and returns a new list containing the clones
+               },
+               */
+
+               addContent: function(/*String*/ content, /*String||Integer?*/ position){
+                       //      summary:
+                       //              add a node or some HTML as a string to every item in the list. 
+                       //              Returns the original list.
+                       //      description:
+                       //              a copy of the HTML content is added to each item in the
+                       //              list, with an optional position argument. If no position
+                       //              argument is provided, the content is appended to the end of
+                       //              each item.
+                       //      content:
+                       //              the HTML in string format to add at position to every item
+                       //      position:
+                       //              can be one of:
+                       //                      * "last"||"end" (default)
+                       //                      * "first||"start"
+                       //                      * "before"
+                       //                      * "after"
+                       //              or an offset in the childNodes property
+                       //      example:
+                       //              appends content to the end if the position is ommitted
+                       //      |       dojo.query("h3 > p").addContent("hey there!");
+                       //      example:
+                       //              add something to the front of each element that has a "thinger" property:
+                       //      |       dojo.query("[thinger]").addContent("...", "first");
+                       //      example:
+                       //              adds a header before each element of the list
+                       //      |       dojo.query(".note").addContent("<h4>NOTE:</h4>", "before");
+                       var ta = d.doc.createElement("span");
+                       if(d.isString(content)){
+                               ta.innerHTML = content;
+                       }else{
+                               ta.appendChild(content);
+                       }
+                       if(position === undefined){
+                               position = "last";
+                       }
+                       var ct = (position == "first" || position == "after") ? "lastChild" : "firstChild";
+                       this.forEach(function(item){
+                               var tn = ta.cloneNode(true);
+                               while(tn[ct]){
+                                       d.place(tn[ct], item, position);
+                               }
+                       });
+                       return this; // dojo.NodeList
+               },
+
+               empty: function(){
+                       //      summary:
+                       //              clears all content from each node in the list
+                       return this.forEach("item.innerHTML='';"); // dojo.NodeList
+
+                       // FIXME: should we be checking for and/or disposing of widgets below these nodes?
+               },
+               
+               instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){
+                       //      summary:
+                       //              Create a new instance of a specified class, using the
+                       //              specified properties and each node in the nodeList as a
+                       //              srcNodeRef
+                       //
+                       var c = d.isFunction(declaredClass) ? declaredClass : d.getObject(declaredClass);
+                       return this.forEach(function(i){
+                               new c(properties||{},i);
+                       }) // dojo.NodeList
+               }
+
+       });
+
+       // syntactic sugar for DOM events
+       d.forEach([
+               "blur", "focus", "click", "keydown", "keypress", "keyup", "mousedown",
+               "mouseenter", "mouseleave", "mousemove", "mouseout", "mouseover",
+               "mouseup"
+               ], function(evt){
+                       var _oe = "on"+evt;
+                       dojo.NodeList.prototype[_oe] = function(a, b){
+                               return this.connect(_oe, a, b);
+                       }
+                               // FIXME: should these events trigger publishes?
+                               /*
+                               return (a ? this.connect(_oe, a, b) : 
+                                                       this.forEach(function(n){  
+                                                               // FIXME:
+                                                               //              listeners get buried by
+                                                               //              addEventListener and can't be dug back
+                                                               //              out to be triggered externally.
+                                                               // see:
+                                                               //              http://developer.mozilla.org/en/docs/DOM:element
+
+                                                               console.debug(n, evt, _oe);
+
+                                                               // FIXME: need synthetic event support!
+                                                               var _e = { target: n, faux: true, type: evt };
+                                                               // dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt });
+                                                               try{ n[evt](_e); }catch(e){ console.debug(e); }
+                                                               try{ n[_oe](_e); }catch(e){ console.debug(e); }
+                                                       })
+                               );
+                       }
+                       */
+               }
+       );
+
+})();
+
+}
+
+if(!dojo._hasResource["dojo._base.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.query"] = true;
+dojo.provide("dojo._base.query");
+
+
+/*
+       dojo.query() architectural overview:
+
+               dojo.query is a relatively full-featured CSS3 query library. It is
+               designed to take any valid CSS3 selector and return the nodes matching
+               the selector. To do this quickly, it processes queries in several
+               steps, applying caching where profitable.
+               
+               The steps (roughly in reverse order of the way they appear in the code):
+                       1.) check to see if we already have a "query dispatcher"
+                               - if so, use that with the given parameterization. Skip to step 4.
+                       2.) attempt to determine which branch to dispatch the query to:
+                               - JS (optimized DOM iteration)
+                               - xpath (for browsers that support it and where it's fast)
+                               - native (not available in any browser yet)
+                       3.) tokenize and convert to executable "query dispatcher"
+                               - this is where the lion's share of the complexity in the
+                                 system lies. In the DOM version, the query dispatcher is
+                                 assembled as a chain of "yes/no" test functions pertaining to
+                                 a section of a simple query statement (".blah:nth-child(odd)"
+                                 but not "div div", which is 2 simple statements). Individual
+                                 statement dispatchers are cached (to prevent re-definition)
+                                 as are entire dispatch chains (to make re-execution of the
+                                 same query fast)
+                               - in the xpath path, tokenization yeilds a concatenation of
+                                 parameterized xpath selectors. As with the DOM version, both
+                                 simple selector blocks and overall evaluators are cached to
+                                 prevent re-defintion
+                       4.) the resulting query dispatcher is called in the passed scope (by default the top-level document)
+                               - for DOM queries, this results in a recursive, top-down
+                                 evaluation of nodes based on each simple query section
+                               - xpath queries can, thankfully, be executed in one shot
+                       5.) matched nodes are pruned to ensure they are unique
+*/
+
+;(function(){
+       // define everything in a closure for compressability reasons. "d" is an
+       // alias to "dojo" since it's so frequently used. This seems a
+       // transformation that the build system could perform on a per-file basis.
+
+       ////////////////////////////////////////////////////////////////////////
+       // Utility code
+       ////////////////////////////////////////////////////////////////////////
+
+       var d = dojo;
+       var childNodesName = dojo.isIE ? "children" : "childNodes";
+       var caseSensitive = false;
+
+       var getQueryParts = function(query){
+               // summary: state machine for query tokenization
+               if(">~+".indexOf(query.charAt(query.length-1)) >= 0){
+                       query += " *"
+               }
+               query += " "; // ensure that we terminate the state machine
+
+               var ts = function(s, e){
+                       return d.trim(query.slice(s, e));
+               }
+
+               // the overall data graph of the full query, as represented by queryPart objects
+               var qparts = []; 
+               // state keeping vars
+               var inBrackets = -1;
+               var inParens = -1;
+               var inMatchFor = -1;
+               var inPseudo = -1;
+               var inClass = -1;
+               var inId = -1;
+               var inTag = -1;
+               var lc = ""; // the last character
+               var cc = ""; // the current character
+               var pStart;
+               // iteration vars
+               var x = 0; // index in the query
+               var ql = query.length;
+               var currentPart = null; // data structure representing the entire clause
+               var _cp = null; // the current pseudo or attr matcher
+
+               var endTag = function(){
+                       if(inTag >= 0){
+                               var tv = (inTag == x) ? null : ts(inTag, x).toLowerCase();
+                               currentPart[ (">~+".indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
+                               inTag = -1;
+                       }
+               }
+
+               var endId = function(){
+                       if(inId >= 0){
+                               currentPart.id = ts(inId, x).replace(/\\/g, "");
+                               inId = -1;
+                       }
+               }
+
+               var endClass = function(){
+                       if(inClass >= 0){
+                               currentPart.classes.push(ts(inClass+1, x).replace(/\\/g, ""));
+                               inClass = -1;
+                       }
+               }
+
+               var endAll = function(){
+                       endId(); endTag(); endClass();
+               }
+
+               for(; lc=cc, cc=query.charAt(x),x<ql; x++){
+                       if(lc == "\\"){ continue; }
+                       if(!currentPart){
+                               // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
+                               pStart = x;
+                               currentPart = {
+                                       query: null,
+                                       pseudos: [],
+                                       attrs: [],
+                                       classes: [],
+                                       tag: null,
+                                       oper: null,
+                                       id: null
+                               };
+                               inTag = x;
+                       }
+
+                       if(inBrackets >= 0){
+                               // look for a the close first
+                               if(cc == "]"){
+                                       if(!_cp.attr){
+                                               _cp.attr = ts(inBrackets+1, x);
+                                       }else{
+                                               _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
+                                       }
+                                       var cmf = _cp.matchFor;
+                                       if(cmf){
+                                               if(     (cmf.charAt(0) == '"') || (cmf.charAt(0)  == "'") ){
+                                                       _cp.matchFor = cmf.substring(1, cmf.length-1);
+                                               }
+                                       }
+                                       currentPart.attrs.push(_cp);
+                                       _cp = null; // necessaray?
+                                       inBrackets = inMatchFor = -1;
+                               }else if(cc == "="){
+                                       var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
+                                       _cp.type = addToCc+cc;
+                                       _cp.attr = ts(inBrackets+1, x-addToCc.length);
+                                       inMatchFor = x+1;
+                               }
+                               // now look for other clause parts
+                       }else if(inParens >= 0){
+                               if(cc == ")"){
+                                       if(inPseudo >= 0){
+                                               _cp.value = ts(inParens+1, x);
+                                       }
+                                       inPseudo = inParens = -1;
+                               }
+                       }else if(cc == "#"){
+                               endAll();
+                               inId = x+1;
+                       }else if(cc == "."){
+                               endAll();
+                               inClass = x;
+                       }else if(cc == ":"){
+                               endAll();
+                               inPseudo = x;
+                       }else if(cc == "["){
+                               endAll();
+                               inBrackets = x;
+                               _cp = {
+                                       /*=====
+                                       attr: null, type: null, matchFor: null
+                                       =====*/
+                               };
+                       }else if(cc == "("){
+                               if(inPseudo >= 0){
+                                       _cp = { 
+                                               name: ts(inPseudo+1, x), 
+                                               value: null
+                                       }
+                                       currentPart.pseudos.push(_cp);
+                               }
+                               inParens = x;
+                       }else if(cc == " " && lc != cc){
+                               // note that we expect the string to be " " terminated
+                               endAll();
+                               if(inPseudo >= 0){
+                                       currentPart.pseudos.push({ name: ts(inPseudo+1, x) });
+                               }
+                               currentPart.hasLoops = (        
+                                               currentPart.pseudos.length || 
+                                               currentPart.attrs.length || 
+                                               currentPart.classes.length      );
+                               currentPart.query = ts(pStart, x);
+                               currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
+                               qparts.push(currentPart);
+                               currentPart = null;
+                       }
+               }
+               return qparts;
+       };
+       
+
+       ////////////////////////////////////////////////////////////////////////
+       // XPath query code
+       ////////////////////////////////////////////////////////////////////////
+
+       // this array is a lookup used to generate an attribute matching function.
+       // There is a similar lookup/generator list for the DOM branch with similar
+       // calling semantics.
+       var xPathAttrs = {
+               "*=": function(attr, value){
+                       return "[contains(@"+attr+", '"+ value +"')]";
+               },
+               "^=": function(attr, value){
+                       return "[starts-with(@"+attr+", '"+ value +"')]";
+               },
+               "$=": function(attr, value){
+                       return "[substring(@"+attr+", string-length(@"+attr+")-"+(value.length-1)+")='"+value+"']";
+               },
+               "~=": function(attr, value){
+                       return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
+               },
+               "|=": function(attr, value){
+                       return "[contains(concat(' ',@"+attr+",' '), ' "+ value +"-')]";
+               },
+               "=": function(attr, value){
+                       return "[@"+attr+"='"+ value +"']";
+               }
+       };
+
+       // takes a list of attribute searches, the overall query, a function to
+       // generate a default matcher, and a closure-bound method for providing a
+       // matching function that generates whatever type of yes/no distinguisher
+       // the query method needs. The method is a bit tortured and hard to read
+       // because it needs to be used in both the XPath and DOM branches.
+       var handleAttrs = function(     attrList, 
+                                                               query, 
+                                                               getDefault, 
+                                                               handleMatch){
+               d.forEach(query.attrs, function(attr){
+                       var matcher;
+                       // type, attr, matchFor
+                       if(attr.type && attrList[attr.type]){
+                               matcher = attrList[attr.type](attr.attr, attr.matchFor);
+                       }else if(attr.attr.length){
+                               matcher = getDefault(attr.attr);
+                       }
+                       if(matcher){ handleMatch(matcher); }
+               });
+       }
+
+       var buildPath = function(query){
+               var xpath = ".";
+               var qparts = getQueryParts(d.trim(query));
+               while(qparts.length){
+                       var tqp = qparts.shift();
+                       var prefix;
+                       var postfix = "";
+                       if(tqp.oper == ">"){
+                               prefix = "/";
+                               // prefix = "/child::*";
+                               tqp = qparts.shift();
+                       }else if(tqp.oper == "~"){
+                               prefix = "/following-sibling::"; // get element following siblings
+                               tqp = qparts.shift();
+                       }else if(tqp.oper == "+"){
+                               // FIXME: 
+                               //              fails when selecting subsequent siblings by node type
+                               //              because the position() checks the position in the list
+                               //              of matching elements and not the localized siblings
+                               prefix = "/following-sibling::";
+                               postfix = "[position()=1]";
+                               tqp = qparts.shift();
+                       }else{
+                               prefix = "//";
+                               // prefix = "/descendant::*"
+                       }
+
+                       // get the tag name (if any)
+
+                       xpath += prefix + tqp.tag + postfix;
+                       
+                       // check to see if it's got an id. Needs to come first in xpath.
+                       if(tqp.id){
+                               xpath += "[@id='"+tqp.id+"'][1]";
+                       }
+
+                       d.forEach(tqp.classes, function(cn){
+                               var cnl = cn.length;
+                               var padding = " ";
+                               if(cn.charAt(cnl-1) == "*"){
+                                       padding = ""; cn = cn.substr(0, cnl-1);
+                               }
+                               xpath += 
+                                       "[contains(concat(' ',@class,' '), ' "+
+                                       cn + padding + "')]";
+                       });
+
+                       handleAttrs(xPathAttrs, tqp, 
+                               function(condition){
+                                               return "[@"+condition+"]";
+                               },
+                               function(matcher){
+                                       xpath += matcher;
+                               }
+                       );
+
+                       // FIXME: need to implement pseudo-class checks!!
+               };
+               return xpath;
+       };
+
+       var _xpathFuncCache = {};
+       var getXPathFunc = function(path){
+               if(_xpathFuncCache[path]){
+                       return _xpathFuncCache[path];
+               }
+
+               var doc = d.doc;
+               // don't need to memoize. The closure scope handles it for us.
+               var xpath = buildPath(path);
+
+               var tf = function(parent){
+                       // XPath query strings are memoized.
+                       var ret = [];
+                       var xpathResult;
+                       try{
+                               xpathResult = doc.evaluate(xpath, parent, null, 
+                                                                                               // XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
+                                                                                               XPathResult.ANY_TYPE, null);
+                       }catch(e){
+                               console.debug("failure in exprssion:", xpath, "under:", parent);
+                               console.debug(e);
+                       }
+                       var result = xpathResult.iterateNext();
+                       while(result){
+                               ret.push(result);
+                               result = xpathResult.iterateNext();
+                       }
+                       return ret;
+               }
+               return _xpathFuncCache[path] = tf;
+       };
+
+       /*
+       d.xPathMatch = function(query){
+               // XPath based DOM query system. Handles a small subset of CSS
+               // selectors, subset is identical to the non-XPath version of this
+               // function. 
+
+               return getXPathFunc(query)();
+       }
+       */
+
+       ////////////////////////////////////////////////////////////////////////
+       // DOM query code
+       ////////////////////////////////////////////////////////////////////////
+
+       var _filtersCache = {};
+       var _simpleFiltersCache = {};
+
+       // the basic building block of the yes/no chaining system. agree(f1, f2)
+       // generates a new function which returns the boolean results of both of
+       // the passed functions to a single logical-anded result.
+       var agree = function(first, second){
+               if(!first){ return second; }
+               if(!second){ return first; }
+
+               return function(){
+                       return first.apply(window, arguments) && second.apply(window, arguments);
+               }
+       }
+
+       var _childElements = function(root){
+               var ret = [];
+               var te, x=0, tret = root[childNodesName];
+               while(te=tret[x++]){
+                       if(te.nodeType == 1){ ret.push(te); }
+               }
+               return ret;
+       }
+
+       var _nextSiblings = function(root, single){
+               var ret = [];
+               var te = root;
+               while(te = te.nextSibling){
+                       if(te.nodeType == 1){
+                               ret.push(te);
+                               if(single){ break; }
+                       }
+               }
+               return ret;
+       }
+
+       var _filterDown = function(element, queryParts, matchArr, idx){
+               // NOTE:
+               //              in the fast path! this function is called recursively and for
+               //              every run of a query.
+               var nidx = idx+1;
+               var isFinal = (queryParts.length == nidx);
+               var tqp = queryParts[idx];
+
+               // see if we can constrain our next level to direct children
+               if(tqp.oper){
+                       var ecn = (tqp.oper == ">") ? 
+                               _childElements(element) :
+                               _nextSiblings(element, (tqp.oper == "+"));
+
+                       if(!ecn || !ecn.length){
+                               return;
+                       }
+                       nidx++;
+                       isFinal = (queryParts.length == nidx);
+                       // kinda janky, too much array alloc
+                       var tf = getFilterFunc(queryParts[idx+1]);
+                       // for(var x=ecn.length-1, te; x>=0, te=ecn[x]; x--){
+                       for(var x=0, ecnl=ecn.length, te; x<ecnl, te=ecn[x]; x++){
+                               if(tf(te)){
+                                       if(isFinal){
+                                               matchArr.push(te);
+                                       }else{
+                                               _filterDown(te, queryParts, matchArr, nidx);
+                                       }
+                               }
+                               /*
+                               if(x==0){
+                                       break;
+                               }
+                               */
+                       }
+               }
+
+               // otherwise, keep going down, unless we'er at the end
+               var candidates = getElementsFunc(tqp)(element);
+               if(isFinal){
+                       while(candidates.length){
+                               matchArr.push(candidates.shift());
+                       }
+                       /*
+                       candidates.unshift(0, matchArr.length-1);
+                       matchArr.splice.apply(matchArr, candidates);
+                       */
+               }else{
+                       // if we're not yet at the bottom, keep going!
+                       while(candidates.length){
+                               _filterDown(candidates.shift(), queryParts, matchArr, nidx);
+                       }
+               }
+       }
+
+       var filterDown = function(elements, queryParts){
+               var ret = [];
+
+               // for every root, get the elements that match the descendant selector
+               // for(var x=elements.length-1, te; x>=0, te=elements[x]; x--){
+               var x = elements.length - 1, te;
+               while(te = elements[x--]){
+                       _filterDown(te, queryParts, ret, 0);
+               }
+               return ret;
+       }
+
+       var getFilterFunc = function(q){
+               // note: query can't have spaces!
+               if(_filtersCache[q.query]){
+                       return _filtersCache[q.query];
+               }
+               var ff = null;
+
+               // does it have a tagName component?
+               if(q.tag){
+                       if(q.tag == "*"){
+                               ff = agree(ff, 
+                                       function(elem){
+                                               return (elem.nodeType == 1);
+                                       }
+                               );
+                       }else{
+                               // tag name match
+                               ff = agree(ff, 
+                                       function(elem){
+                                               return (
+                                                       (elem.nodeType == 1) &&
+                                                       (q.tag == elem.tagName.toLowerCase())
+                                               );
+                                               // return isTn;
+                                       }
+                               );
+                       }
+               }
+
+               // does the node have an ID?
+               if(q.id){
+                       ff = agree(ff, 
+                               function(elem){
+                                       return (
+                                               (elem.nodeType == 1) &&
+                                               (elem.id == q.id)
+                                       );
+                               }
+                       );
+               }
+
+               if(q.hasLoops){
+                       // if we have other query param parts, make sure we add them to the
+                       // filter chain
+                       ff = agree(ff, getSimpleFilterFunc(q));
+               }
+
+               return _filtersCache[q.query] = ff;
+       }
+
+       var getNodeIndex = function(node){
+               // NOTE: 
+               //              we could have a more accurate caching mechanism by invalidating
+               //              caches after the query has finished, but I think that'd lead to
+               //              significantly more cache churn than the cache would provide
+               //              value for in the common case. Generally, we're more
+               //              conservative (and therefore, more accurate) than jQuery and
+               //              DomQuery WRT node node indexes, but there may be corner cases
+               //              in which we fall down.  How much we care about them is TBD.
+
+               var pn = node.parentNode;
+               var pnc = pn.childNodes;
+
+               // check to see if we can trust the cache. If not, re-key the whole
+               // thing and return our node match from that.
+
+               var nidx = -1;
+               var child = pn.firstChild;
+               if(!child){
+                       return nidx;
+               }
+
+               var ci = node["__cachedIndex"];
+               var cl = pn["__cachedLength"];
+
+               // only handle cache building if we've gone out of sync
+               if(((typeof cl == "number")&&(cl != pnc.length))||(typeof ci != "number")){
+                       // rip though the whole set, building cache indexes as we go
+                       pn["__cachedLength"] = pnc.length;
+                       var idx = 1;
+                       do{
+                               // we only assign indexes for nodes with nodeType == 1, as per:
+                               //              http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
+                               // only elements are counted in the search order, and they
+                               // begin at 1 for the first child's index
+
+                               if(child === node){
+                                       nidx = idx;
+                               }
+                               if(child.nodeType == 1){
+                                       child["__cachedIndex"] = idx;
+                                       idx++;
+                               }
+                               child = child.nextSibling;
+                       }while(child);
+               }else{
+                       // NOTE: 
+                       //              could be incorrect in some cases (node swaps involving the
+                       //              passed node, etc.), but we ignore those due to the relative
+                       //              unlikelihood of that occuring
+                       nidx = ci;
+               }
+               return nidx;
+       }
+
+       var firedCount = 0;
+
+       var blank = "";
+       var _getAttr = function(elem, attr){
+               if(attr == "class"){
+                       return elem.className || blank;
+               }
+               if(attr == "for"){
+                       return elem.htmlFor || blank;
+               }
+               return elem.getAttribute(attr, 2) || blank;
+       }
+
+       var attrs = {
+               "*=": function(attr, value){
+                       return function(elem){
+                               // E[foo*="bar"]
+                               //              an E element whose "foo" attribute value contains
+                               //              the substring "bar"
+                               return (_getAttr(elem, attr).indexOf(value)>=0);
+                       }
+               },
+               "^=": function(attr, value){
+                       // E[foo^="bar"]
+                       //              an E element whose "foo" attribute value begins exactly
+                       //              with the string "bar"
+                       return function(elem){
+                               return (_getAttr(elem, attr).indexOf(value)==0);
+                       }
+               },
+               "$=": function(attr, value){
+                       // E[foo$="bar"]        
+                       //              an E element whose "foo" attribute value ends exactly
+                       //              with the string "bar"
+                       var tval = " "+value;
+                       return function(elem){
+                               var ea = " "+_getAttr(elem, attr);
+                               return (ea.lastIndexOf(value)==(ea.length-value.length));
+                       }
+               },
+               "~=": function(attr, value){
+                       // E[foo~="bar"]        
+                       //              an E element whose "foo" attribute value is a list of
+                       //              space-separated values, one of which is exactly equal
+                       //              to "bar"
+
+                       // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
+                       var tval = " "+value+" ";
+                       return function(elem){
+                               var ea = " "+_getAttr(elem, attr)+" ";
+                               return (ea.indexOf(tval)>=0);
+                       }
+               },
+               "|=": function(attr, value){
+                       // E[hreflang|="en"]
+                       //              an E element whose "hreflang" attribute has a
+                       //              hyphen-separated list of values beginning (from the
+                       //              left) with "en"
+                       var valueDash = " "+value+"-";
+                       return function(elem){
+                               var ea = " "+(elem.getAttribute(attr, 2) || "");
+                               return (
+                                       (ea == value) ||
+                                       (ea.indexOf(valueDash)==0)
+                               );
+                       }
+               },
+               "=": function(attr, value){
+                       return function(elem){
+                               return (_getAttr(elem, attr) == value);
+                       }
+               }
+       };
+
+       var pseudos = {
+               "first-child": function(name, condition){
+                       return function(elem){
+                               if(elem.nodeType != 1){ return false; }
+                               // check to see if any of the previous siblings are elements
+                               var fc = elem.previousSibling;
+                               while(fc && (fc.nodeType != 1)){
+                                       fc = fc.previousSibling;
+                               }
+                               return (!fc);
+                       }
+               },
+               "last-child": function(name, condition){
+                       return function(elem){
+                               if(elem.nodeType != 1){ return false; }
+                               // check to see if any of the next siblings are elements
+                               var nc = elem.nextSibling;
+                               while(nc && (nc.nodeType != 1)){
+                                       nc = nc.nextSibling;
+                               }
+                               return (!nc);
+                       }
+               },
+               "empty": function(name, condition){
+                       return function(elem){
+                               // DomQuery and jQuery get this wrong, oddly enough.
+                               // The CSS 3 selectors spec is pretty explicit about
+                               // it, too.
+                               var cn = elem.childNodes;
+                               var cnl = elem.childNodes.length;
+                               // if(!cnl){ return true; }
+                               for(var x=cnl-1; x >= 0; x--){
+                                       var nt = cn[x].nodeType;
+                                       if((nt == 1)||(nt == 3)){ return false; }
+                               }
+                               return true;
+                       }
+               },
+               "contains": function(name, condition){
+                       return function(elem){
+                               // FIXME: I dislike this version of "contains", as
+                               // whimsical attribute could set it off. An inner-text
+                               // based version might be more accurate, but since
+                               // jQuery and DomQuery also potentially get this wrong,
+                               // I'm leaving it for now.
+                               return (elem.innerHTML.indexOf(condition) >= 0);
+                       }
+               },
+               "not": function(name, condition){
+                       var ntf = getFilterFunc(getQueryParts(condition)[0]);
+                       return function(elem){
+                               return (!ntf(elem));
+                       }
+               },
+               "nth-child": function(name, condition){
+                       var pi = parseInt;
+                       if(condition == "odd"){
+                               return function(elem){
+                                       return (
+                                               ((getNodeIndex(elem)) % 2) == 1
+                                       );
+                               }
+                       }else if((condition == "2n")||
+                               (condition == "even")){
+                               return function(elem){
+                                       return ((getNodeIndex(elem) % 2) == 0);
+                               }
+                       }else if(condition.indexOf("0n+") == 0){
+                               var ncount = pi(condition.substr(3));
+                               return function(elem){
+                                       return (elem.parentNode[childNodesName][ncount-1] === elem);
+                               }
+                       }else if(       (condition.indexOf("n+") > 0) &&
+                                               (condition.length > 3) ){
+                               var tparts = condition.split("n+", 2);
+                               var pred = pi(tparts[0]);
+                               var idx = pi(tparts[1]);
+                               return function(elem){
+                                       return ((getNodeIndex(elem) % pred) == idx);
+                               }
+                       }else if(condition.indexOf("n") == -1){
+                               var ncount = pi(condition);
+                               return function(elem){
+                                       return (getNodeIndex(elem) == ncount);
+                               }
+                       }
+               }
+       };
+
+       var defaultGetter = (d.isIE) ? function(cond){
+               var clc = cond.toLowerCase();
+               return function(elem){
+                       return elem[cond]||elem[clc];
+               }
+       } : function(cond){
+               return function(elem){
+                       return (elem && elem.getAttribute && elem.hasAttribute(cond));
+               }
+       };
+
+       var getSimpleFilterFunc = function(query){
+
+               var fcHit = (_simpleFiltersCache[query.query]||_filtersCache[query.query]);
+               if(fcHit){ return fcHit; }
+
+               var ff = null;
+
+               // the only case where we'll need the tag name is if we came from an ID query
+               if(query.id){ // do we have an ID component?
+                       if(query.tag != "*"){
+                               ff = agree(ff, function(elem){
+                                       return (elem.tagName.toLowerCase() == query.tag);
+                               });
+                       }
+               }
+
+               // if there's a class in our query, generate a match function for it
+               d.forEach(query.classes, function(cname, idx, arr){
+                       // get the class name
+                       var isWildcard = cname.charAt(cname.length-1) == "*";
+                       if(isWildcard){
+                               cname = cname.substr(0, cname.length-1);
+                       }
+                       // I dislike the regex thing, even if memozied in a cache, but it's VERY short
+                       var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
+                       ff = agree(ff, function(elem){
+                               return re.test(elem.className);
+                       });
+                       ff.count = idx;
+               });
+
+               d.forEach(query.pseudos, function(pseudo){
+                       if(pseudos[pseudo.name]){
+                               ff = agree(ff, pseudos[pseudo.name](pseudo.name, pseudo.value));
+                       }
+               });
+
+               handleAttrs(attrs, query, defaultGetter,
+                       function(tmatcher){ ff = agree(ff, tmatcher); }
+               );
+               if(!ff){
+                       ff = function(){ return true; };
+               }
+               return _simpleFiltersCache[query.query] = ff;
+       }
+
+       var _getElementsFuncCache = { };
+
+       var getElementsFunc = function(query, root){
+               var fHit = _getElementsFuncCache[query.query];
+               if(fHit){ return fHit; }
+
+               // NOTE: this function is in the fast path! not memoized!!!
+
+               // the query doesn't contain any spaces, so there's only so many
+               // things it could be
+
+               if(query.id && !query.hasLoops && !query.tag){
+                       // ID-only query. Easy.
+                       return _getElementsFuncCache[query.query] = function(root){
+                               // FIXME: if root != document, check for parenting!
+                               return [ d.byId(query.id) ];
+                       }
+               }
+
+               var filterFunc = getSimpleFilterFunc(query);
+
+               var retFunc;
+               if(query.tag && query.id && !query.hasLoops){
+                       // we got a filtered ID search (e.g., "h4#thinger")
+                       retFunc = function(root){
+                               var te = d.byId(query.id);
+                               if(filterFunc(te)){
+                                       return [ te ];
+                               }
+                       }
+               }else{
+                       var tret;
+
+                       if(!query.hasLoops){
+                               // it's just a plain-ol elements-by-tag-name query from the root
+                               retFunc = function(root){
+                                       var ret = [];
+                                       var te, x=0, tret = root.getElementsByTagName(query.tag);
+                                       while(te=tret[x++]){
+                                               ret.push(te);
+                                       }
+                                       return ret;
+                               }
+                       }else{
+                               retFunc = function(root){
+                                       var ret = [];
+                                       var te, x=0, tret = root.getElementsByTagName(query.tag);
+                                       while(te=tret[x++]){
+                                               if(filterFunc(te)){
+                                                       ret.push(te);
+                                               }
+                                       }
+                                       return ret;
+                               }
+                       }
+               }
+               return _getElementsFuncCache[query.query] = retFunc;
+       }
+
+       var _partsCache = {};
+
+       ////////////////////////////////////////////////////////////////////////
+       // the query runner
+       ////////////////////////////////////////////////////////////////////////
+
+       // this is the second level of spliting, from full-length queries (e.g.,
+       // "div.foo .bar") into simple query expressions (e.g., ["div.foo",
+       // ".bar"])
+       var _queryFuncCache = {
+               "*": d.isIE ? 
+                       function(root){ 
+                                       return root.all;
+                       } : 
+                       function(root){
+                                return root.getElementsByTagName("*");
+                       },
+               "~": _nextSiblings,
+               "+": function(root){ return _nextSiblings(root, true); },
+               ">": _childElements
+       };
+
+       var getStepQueryFunc = function(query){
+               // if it's trivial, get a fast-path dispatcher
+               var qparts = getQueryParts(d.trim(query));
+               // if(query[query.length-1] == ">"){ query += " *"; }
+               if(qparts.length == 1){
+                       var tt = getElementsFunc(qparts[0]);
+                       tt.nozip = true;
+                       return tt;
+               }
+
+               // otherwise, break it up and return a runner that iterates over the parts recursively
+               var sqf = function(root){
+                       var localQueryParts = qparts.slice(0); // clone the src arr
+                       var candidates;
+                       if(localQueryParts[0].oper == ">"){ // FIXME: what if it's + or ~?
+                               candidates = [ root ];
+                               // root = document;
+                       }else{
+                               candidates = getElementsFunc(localQueryParts.shift())(root);
+                       }
+                       return filterDown(candidates, localQueryParts);
+               }
+               return sqf;
+       }
+
+       // a specialized method that implements our primoridal "query optimizer".
+       // This allows us to dispatch queries to the fastest subsystem we can get.
+       var _getQueryFunc = (
+               // NOTE: 
+               //              XPath on the Webkit nighlies is slower than it's DOM iteration
+               //              for most test cases
+               // FIXME: 
+               //              we should try to capture some runtime speed data for each query
+               //              function to determine on the fly if we should stick w/ the
+               //              potentially optimized variant or if we should try something
+               //              new.
+               (document["evaluate"] && !d.isSafari) ? 
+               function(query){
+                       // has xpath support that's faster than DOM
+                       var qparts = query.split(" ");
+                       // can we handle it?
+                       if(     (document["evaluate"])&&
+                               (query.indexOf(":") == -1)&&
+                               (query.indexOf("+") == -1) // skip direct sibling matches. See line ~344
+                       ){
+                               // dojo.debug(query);
+                               // should we handle it?
+
+                               // kind of a lame heuristic, but it works
+                               if(     
+                                       // a "div div div" style query
+                                       ((qparts.length > 2)&&(query.indexOf(">") == -1))||
+                                       // or something else with moderate complexity. kinda janky
+                                       (qparts.length > 3)||
+                                       (query.indexOf("[")>=0)||
+                                       // or if it's a ".thinger" query
+                                       ((1 == qparts.length)&&(0 <= query.indexOf(".")))
+
+                               ){
+                                       // use get and cache a xpath runner for this selector
+                                       return getXPathFunc(query);
+                               }
+                       }
+
+                       // fallthrough
+                       return getStepQueryFunc(query);
+               } : getStepQueryFunc
+       );
+       // uncomment to disable XPath for testing and tuning the DOM path
+       // _getQueryFunc = getStepQueryFunc;
+
+       // FIXME: we've got problems w/ the NodeList query()/filter() functions if we go XPath for everything
+
+       // uncomment to disable DOM queries for testing and tuning XPath
+       // _getQueryFunc = getXPathFunc;
+
+       // this is the primary caching for full-query results. The query dispatcher
+       // functions are generated here and then pickled for hash lookup in the
+       // future
+       var getQueryFunc = function(query){
+               // return a cached version if one is available
+               var qcz = query.charAt(0);
+               if(d.doc["querySelectorAll"] && 
+                       ( (!d.isSafari) || (d.isSafari > 3.1) ) && // see #5832
+                       // as per CSS 3, we can't currently start w/ combinator:
+                       //              http://www.w3.org/TR/css3-selectors/#w3cselgrammar
+                       (">+~".indexOf(qcz) == -1)
+               ){
+                       return function(root){
+                               var r = root.querySelectorAll(query);
+                               r.nozip = true; // skip expensive duplication checks and just wrap in a NodeList
+                               return r;
+                       };
+               }
+               if(_queryFuncCache[query]){ return _queryFuncCache[query]; }
+               if(0 > query.indexOf(",")){
+                       // if it's not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
+                       return _queryFuncCache[query] = _getQueryFunc(query);
+               }else{
+                       // if it's a complex query, break it up into it's constituent parts
+                       // and return a dispatcher that will merge the parts when run
+
+                       // var parts = query.split(", ");
+                       var parts = query.split(/\s*,\s*/);
+                       var tf = function(root){
+                               var pindex = 0; // avoid array alloc for every invocation
+                               var ret = [];
+                               var tp;
+                               while(tp = parts[pindex++]){
+                                       ret = ret.concat(_getQueryFunc(tp, tp.indexOf(" "))(root));
+                               }
+                               return ret;
+                       }
+                       // ...cache and return
+                       return _queryFuncCache[query] = tf;
+               }
+       }
+
+       // FIXME: 
+       //              Dean's Base2 uses a system whereby queries themselves note if
+       //              they'll need duplicate filtering. We need to get on that plan!!
+
+       // attempt to efficiently determine if an item in a list is a dupe,
+       // returning a list of "uniques", hopefully in doucment order
+       var _zipIdx = 0;
+       var _zip = function(arr){
+               if(arr && arr.nozip){ return d.NodeList._wrap(arr); }
+               var ret = new d.NodeList();
+               if(!arr){ return ret; }
+               if(arr[0]){
+                       ret.push(arr[0]);
+               }
+               if(arr.length < 2){ return ret; }
+               _zipIdx++;
+               arr[0]["_zipIdx"] = _zipIdx;
+               for(var x=1, te; te = arr[x]; x++){
+                       if(arr[x]["_zipIdx"] != _zipIdx){ 
+                               ret.push(te);
+                       }
+                       te["_zipIdx"] = _zipIdx;
+               }
+               // FIXME: should we consider stripping these properties?
+               return ret;
+       }
+
+       // the main executor
+       d.query = function(/*String*/ query, /*String|DOMNode?*/ root){
+               //      summary:
+               //              Returns nodes which match the given CSS3 selector, searching the
+               //              entire document by default but optionally taking a node to scope
+               //              the search by. Returns an instance of dojo.NodeList.
+               //      description:
+               //              dojo.query() is the swiss army knife of DOM node manipulation in
+               //              Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
+               //              "$" function, dojo.query provides robust, high-performance
+               //              CSS-based node selector support with the option of scoping searches
+               //              to a particular sub-tree of a document.
+               //
+               //              Supported Selectors:
+               //              --------------------
+               //
+               //              dojo.query() supports a rich set of CSS3 selectors, including:
+               //
+               //                      * class selectors (e.g., `.foo`)
+               //                      * node type selectors like `span`
+               //                      * ` ` descendant selectors
+               //                      * `>` child element selectors 
+               //                      * `#foo` style ID selectors
+               //                      * `*` universal selector
+               //                      * `~`, the immediately preceeded-by sibling selector
+               //                      * `+`, the preceeded-by sibling selector
+               //                      * attribute queries:
+               //                      |       * `[foo]` attribute presence selector
+               //                      |       * `[foo='bar']` attribute value exact match
+               //                      |       * `[foo~='bar']` attribute value list item match
+               //                      |       * `[foo^='bar']` attribute start match
+               //                      |       * `[foo$='bar']` attribute end match
+               //                      |       * `[foo*='bar']` attribute substring match
+               //                      * `:first-child`, `:last-child` positional selectors
+               //                      * `:empty` content emtpy selector
+               //                      * `:empty` content emtpy selector
+               //                      * `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
+               //                      * `:nth-child(even)`, `:nth-child(odd)` positional selectors
+               //                      * `:not(...)` negation pseudo selectors
+               //
+               //              Any legal combination of these selectors will work with
+               //              `dojo.query()`, including compound selectors ("," delimited).
+               //              Very complex and useful searches can be constructed with this
+               //              palette of selectors and when combined with functions for
+               //              maniplation presented by dojo.NodeList, many types of DOM
+               //              manipulation operations become very straightforward.
+               //              
+               //              Unsupported Selectors:
+               //              ----------------------
+               //
+               //              While dojo.query handles many CSS3 selectors, some fall outside of
+               //              what's resaonable for a programmatic node querying engine to
+               //              handle. Currently unsupported selectors include:
+               //              
+               //                      * namespace-differentiated selectors of any form
+               //                      * all `::` pseduo-element selectors
+               //                      * certain pseduo-selectors which don't get a lot of day-to-day use:
+               //                      |       * `:root`, `:lang()`, `:target`, `:focus`
+               //                      * all visual and state selectors:
+               //                      |       * `:root`, `:active`, `:hover`, `:visisted`, `:link`,
+               //                                `:enabled`, `:disabled`, `:checked`
+               //                      * `:*-of-type` pseudo selectors
+               //              
+               //              dojo.query and XML Documents:
+               //              -----------------------------
+               //              
+               //              `dojo.query` currently only supports searching XML documents
+               //              whose tags and attributes are 100% lower-case. This is a known
+               //              limitation and will [be addressed soon](http://trac.dojotoolkit.org/ticket/3866)
+               //              Non-selector Queries:
+               //              ---------------------
+               //
+               //              If something other than a String is passed for the query,
+               //              `dojo.query` will return a new `dojo.NodeList` constructed from
+               //              that parameter alone and all further processing will stop. This
+               //              means that if you have a reference to a node or NodeList, you
+               //              can quickly construct a new NodeList from the original by
+               //              calling `dojo.query(node)` or `dojo.query(list)`.
+               //
+               //      query:
+               //              The CSS3 expression to match against. For details on the syntax of
+               //              CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
+               //      root:
+               //              A DOMNode (or node id) to scope the search from. Optional.
+               //      returns: dojo.NodeList
+               //              An instance of `dojo.NodeList`. Many methods are available on
+               //              NodeLists for searching, iterating, manipulating, and handling
+               //              events on the matched nodes in the returned list.
+               //      example:
+               //              search the entire document for elements with the class "foo":
+               //      |       dojo.query(".foo");
+               //              these elements will match:
+               //      |       <span class="foo"></span>
+               //      |       <span class="foo bar"></span>
+               //      |       <p class="thud foo"></p>
+               //      example:
+               //              search the entire document for elements with the classes "foo" *and* "bar":
+               //      |       dojo.query(".foo.bar");
+               //              these elements will match:
+               //      |       <span class="foo bar"></span>
+               //              while these will not:
+               //      |       <span class="foo"></span>
+               //      |       <p class="thud foo"></p>
+               //      example:
+               //              find `<span>` elements which are descendants of paragraphs and
+               //              which have a "highlighted" class:
+               //      |       dojo.query("p span.highlighted");
+               //              the innermost span in this fragment matches:
+               //      |       <p class="foo">
+               //      |               <span>...
+               //      |                       <span class="highlighted foo bar">...</span>
+               //      |               </span>
+               //      |       </p>
+               //      example:
+               //              set an "odd" class on all odd table rows inside of the table
+               //              `#tabular_data`, using the `>` (direct child) selector to avoid
+               //              affecting any nested tables:
+               //      |       dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
+               //      example:
+               //              remove all elements with the class "error" from the document
+               //              and store them in a list:
+               //      |       var errors = dojo.query(".error").orphan();
+               //      example:
+               //              add an onclick handler to every submit button in the document
+               //              which causes the form to be sent via Ajax instead:
+               //      |       dojo.query("input[type='submit']").onclick(function(e){
+               //      |               dojo.stopEvent(e); // prevent sending the form
+               //      |               var btn = e.target;
+               //      |               dojo.xhrPost({
+               //      |                       form: btn.form,
+               //      |                       load: function(data){
+               //      |                               // replace the form with the response
+               //      |                               var div = dojo.doc.createElement("div");
+               //      |                               dojo.place(div, btn.form, "after");
+               //      |                               div.innerHTML = data;
+               //      |                               dojo.style(btn.form, "display", "none");
+               //      |                       }
+               //      |               });
+               //      |       });
+
+
+               // NOTE: elementsById is not currently supported
+               // NOTE: ignores xpath-ish queries for now
+
+               if(query.constructor == d.NodeList){
+                       return query;
+               }
+               if(!d.isString(query)){
+                       return new d.NodeList(query); // dojo.NodeList
+               }
+               if(d.isString(root)){
+                       root = d.byId(root);
+               }
+
+               return _zip(getQueryFunc(query)(root||d.doc)); // dojo.NodeList
+       }
+
+       /*
+       // exposing this was a mistake
+       d.query.attrs = attrs;
+       */
+       // exposing this because new pseudo matches are only executed through the
+       // DOM query path (never through the xpath optimizing branch)
+       d.query.pseudos = pseudos;
+
+       // one-off function for filtering a NodeList based on a simple selector
+       d._filterQueryResult = function(nodeList, simpleFilter){
+               var tnl = new d.NodeList();
+               var ff = (simpleFilter) ? getFilterFunc(getQueryParts(simpleFilter)[0]) : function(){ return true; };
+               for(var x=0, te; te = nodeList[x]; x++){
+                       if(ff(te)){ tnl.push(te); }
+               }
+               return tnl;
+       }
+})();
+
+}
+
+if(!dojo._hasResource["dojo._base.xhr"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.xhr"] = true;
+dojo.provide("dojo._base.xhr");
+
+
+
+
+
+(function(){
+       var _d = dojo;
+       function setValue(/*Object*/obj, /*String*/name, /*String*/value){
+               //summary:
+               //              For the nameed property in object, set the value. If a value
+               //              already exists and it is a string, convert the value to be an
+               //              array of values.
+               var val = obj[name];
+               if(_d.isString(val)){
+                       obj[name] = [val, value];
+               }else if(_d.isArray(val)){
+                       val.push(value);
+               }else{
+                       obj[name] = value;
+               }
+       }
+
+       dojo.formToObject = function(/*DOMNode||String*/ formNode){
+               // summary:
+               //              dojo.formToObject returns the values encoded in an HTML form as
+               //              string properties in an object which it then returns. Disabled form
+               //              elements, buttons, and other non-value form elements are skipped.
+               //              Multi-select elements are returned as an array of string values.
+               // description:
+               //              This form:
+               //
+               //              |       <form id="test_form">
+               //              |               <input type="text" name="blah" value="blah">
+               //              |               <input type="text" name="no_value" value="blah" disabled>
+               //              |               <input type="button" name="no_value2" value="blah">
+               //              |               <select type="select" multiple name="multi" size="5">
+               //              |                       <option value="blah">blah</option>
+               //              |                       <option value="thud" selected>thud</option>
+               //              |                       <option value="thonk" selected>thonk</option>
+               //              |               </select>
+               //              |       </form>
+               //
+               //              yields this object structure as the result of a call to
+               //              formToObject():
+               //
+               //              |       { 
+               //              |               blah: "blah",
+               //              |               multi: [
+               //              |                       "thud",
+               //              |                       "thonk"
+               //              |               ]
+               //              |       };
+       
+               var ret = {};
+               var iq = "input:not([type=file]):not([type=submit]):not([type=image]):not([type=reset]):not([type=button]), select, textarea";
+               _d.query(iq, formNode).filter(function(node){
+                       return !node.disabled && node.name;
+               }).forEach(function(item){
+                       var _in = item.name;
+                       var type = (item.type||"").toLowerCase();
+                       if(type == "radio" || type == "checkbox"){
+                               if(item.checked){ setValue(ret, _in, item.value); }
+                       }else if(item.multiple){
+                               ret[_in] = [];
+                               _d.query("option", item).forEach(function(opt){
+                                       if(opt.selected){
+                                               setValue(ret, _in, opt.value);
+                                       }
+                               });
+                       }else{ 
+                               setValue(ret, _in, item.value);
+                               if(type == "image"){
+                                       ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0;
+                               }
+                       }
+               });
+               return ret; // Object
+       }
+
+       dojo.objectToQuery = function(/*Object*/ map){
+               //      summary:
+               //              takes a name/value mapping object and returns a string representing
+               //              a URL-encoded version of that object.
+               //      example:
+               //              this object:
+               //
+               //              |       { 
+               //              |               blah: "blah",
+               //              |               multi: [
+               //              |                       "thud",
+               //              |                       "thonk"
+               //              |               ]
+               //              |       };
+               //
+               //      yields the following query string:
+               //      
+               //      |       "blah=blah&multi=thud&multi=thonk"
+
+               // FIXME: need to implement encodeAscii!!
+               var enc = encodeURIComponent;
+               var pairs = [];
+               var backstop = {};
+               for(var name in map){
+                       var value = map[name];
+                       if(value != backstop[name]){
+                               var assign = enc(name) + "=";
+                               if(_d.isArray(value)){
+                                       for(var i=0; i < value.length; i++){
+                                               pairs.push(assign + enc(value[i]));
+                                       }
+                               }else{
+                                       pairs.push(assign + enc(value));
+                               }
+                       }
+               }
+               return pairs.join("&"); // String
+       }
+
+       dojo.formToQuery = function(/*DOMNode||String*/ formNode){
+               // summary:
+               //              Returns a URL-encoded string representing the form passed as either a
+               //              node or string ID identifying the form to serialize
+               return _d.objectToQuery(_d.formToObject(formNode)); // String
+       }
+
+       dojo.formToJson = function(/*DOMNode||String*/ formNode, /*Boolean?*/prettyPrint){
+               // summary:
+               //              return a serialized JSON string from a form node or string
+               //              ID identifying the form to serialize
+               return _d.toJson(_d.formToObject(formNode), prettyPrint); // String
+       }
+
+       dojo.queryToObject = function(/*String*/ str){
+               // summary:
+               //              returns an object representing a de-serialized query section of a
+               //              URL. Query keys with multiple values are returned in an array.
+               // description:
+               //              This string:
+               //
+               //      |               "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&"
+               //              
+               //              results in this object structure:
+               //
+               //      |               {
+               //      |                       foo: [ "bar", "baz" ],
+               //      |                       thinger: " spaces =blah",
+               //      |                       zonk: "blarg"
+               //      |               }
+               //      
+               //              Note that spaces and other urlencoded entities are correctly
+               //              handled.
+
+               // FIXME: should we grab the URL string if we're not passed one?
+               var ret = {};
+               var qp = str.split("&");
+               var dec = decodeURIComponent;
+               _d.forEach(qp, function(item){
+                       if(item.length){
+                               var parts = item.split("=");
+                               var name = dec(parts.shift());
+                               var val = dec(parts.join("="));
+                               if(_d.isString(ret[name])){
+                                       ret[name] = [ret[name]];
+                               }
+                               if(_d.isArray(ret[name])){
+                                       ret[name].push(val);
+                               }else{
+                                       ret[name] = val;
+                               }
+                       }
+               });
+               return ret; // Object
+       }
+
+       /*
+               from refactor.txt:
+
+               all bind() replacement APIs take the following argument structure:
+
+                       {
+                               url: "blah.html",
+
+                               // all below are optional, but must be supported in some form by
+                               // every IO API
+                               timeout: 1000, // milliseconds
+                               handleAs: "text", // replaces the always-wrong "mimetype"
+                               content: { 
+                                       key: "value"
+                               },
+
+                               // browser-specific, MAY be unsupported
+                               sync: true, // defaults to false
+                               form: dojo.byId("someForm") 
+                       }
+       */
+
+       // need to block async callbacks from snatching this thread as the result
+       // of an async callback might call another sync XHR, this hangs khtml forever
+       // must checked by watchInFlight()
+
+       dojo._blockAsync = false;
+
+       dojo._contentHandlers = {
+               "text": function(xhr){ return xhr.responseText; },
+               "json": function(xhr){
+                       if(!dojo.config.usePlainJson){
+                               console.warn("Consider using mimetype:text/json-comment-filtered"
+                                       + " to avoid potential security issues with JSON endpoints"
+                                       + " (use djConfig.usePlainJson=true to turn off this message)");
+                       }
+                       return (xhr.status == 204) ? undefined : _d.fromJson(xhr.responseText);
+               },
+               "json-comment-filtered": function(xhr){ 
+                       // NOTE: we provide the json-comment-filtered option as one solution to
+                       // the "JavaScript Hijacking" issue noted by Fortify and others. It is
+                       // not appropriate for all circumstances.
+
+                       var value = xhr.responseText;
+                       var cStartIdx = value.indexOf("\/*");
+                       var cEndIdx = value.lastIndexOf("*\/");
+                       if(cStartIdx == -1 || cEndIdx == -1){
+                               throw new Error("JSON was not comment filtered");
+                       }
+                       return (xhr.status == 204) ? undefined :
+                               _d.fromJson(value.substring(cStartIdx+2, cEndIdx));
+               },
+               "javascript": function(xhr){ 
+                       // FIXME: try Moz and IE specific eval variants?
+                       return _d.eval(xhr.responseText);
+               },
+               "xml": function(xhr){ 
+                       var result = xhr.responseXML;
+                       if(_d.isIE && (!result || window.location.protocol == "file:")){
+                               _d.forEach(["MSXML2", "Microsoft", "MSXML", "MSXML3"], function(prefix){
+                                       try{
+                                               var dom = new ActiveXObject(prefix + ".XMLDOM");
+                                               dom.async = false;
+                                               dom.loadXML(xhr.responseText);
+                                               result = dom;
+                                       }catch(e){ /* Not available. Squelch and try next one. */ }
+                               });
+                       }
+                       return result; // DOMDocument
+               }
+       };
+
+       dojo._contentHandlers["json-comment-optional"] = function(xhr){
+               var handlers = _d._contentHandlers;
+               try{
+                       return handlers["json-comment-filtered"](xhr);
+               }catch(e){
+                       return handlers["json"](xhr);
+               }
+       };
+
+       /*=====
+       dojo.__IoArgs = function(){
+               //      url: String
+               //              URL to server endpoint.
+               //      content: Object?
+               //              Contains properties with string values. These
+               //              properties will be serialized as name1=value2 and
+               //              passed in the request.
+               //      timeout: Integer?
+               //              Milliseconds to wait for the response. If this time
+               //              passes, the then error callbacks are called.
+               //      form: DOMNode?
+               //              DOM node for a form. Used to extract the form values
+               //              and send to the server.
+               //      preventCache: Boolean?
+               //              Default is false. If true, then a
+               //              "dojo.preventCache" parameter is sent in the request
+               //              with a value that changes with each request
+               //              (timestamp). Useful only with GET-type requests.
+               //      handleAs: String?
+               //              Acceptable values depend on the type of IO
+               //              transport (see specific IO calls for more information).
+               //      load: Function?
+               //              function(response, ioArgs){}. response is an Object, ioArgs
+               //              is of type dojo.__IoCallbackArgs. The load function will be
+               //              called on a successful response.
+               //      error: Function?
+               //              function(response, ioArgs){}. response is an Object, ioArgs
+               //              is of type dojo.__IoCallbackArgs. The error function will
+               //              be called in an error case. 
+               //      handle: Function?
+               //              function(response, ioArgs){}. response is an Object, ioArgs
+               //              is of type dojo.__IoCallbackArgs. The handle function will
+               //              be called in either the successful or error case.  For
+               //              the load, error and handle functions, the ioArgs object
+               //              will contain the following properties: 
+               this.url = url;
+               this.content = content;
+               this.timeout = timeout;
+               this.form = form;
+               this.preventCache = preventCache;
+               this.handleAs = handleAs;
+               this.load = load;
+               this.error = error;
+               this.handle = handle;
+       }
+       =====*/
+
+       /*=====
+       dojo.__IoCallbackArgs = function(args, xhr, url, query, handleAs, id, canDelete, json){
+               //      args: Object
+               //              the original object argument to the IO call.
+               //      xhr: XMLHttpRequest
+               //              For XMLHttpRequest calls only, the
+               //              XMLHttpRequest object that was used for the
+               //              request.
+               //      url: String
+               //              The final URL used for the call. Many times it
+               //              will be different than the original args.url
+               //              value.
+               //      query: String
+               //              For non-GET requests, the
+               //              name1=value1&name2=value2 parameters sent up in
+               //              the request.
+               //      handleAs: String
+               //              The final indicator on how the response will be
+               //              handled.
+               //      id: String
+               //              For dojo.io.script calls only, the internal
+               //              script ID used for the request.
+               //      canDelete: Boolean
+               //              For dojo.io.script calls only, indicates
+               //              whether the script tag that represents the
+               //              request can be deleted after callbacks have
+               //              been called. Used internally to know when
+               //              cleanup can happen on JSONP-type requests.
+               //      json: Object
+               //              For dojo.io.script calls only: holds the JSON
+               //              response for JSONP-type requests. Used
+               //              internally to hold on to the JSON responses.
+               //              You should not need to access it directly --
+               //              the same object should be passed to the success
+               //              callbacks directly.
+               this.args = args;
+               this.xhr = xhr;
+               this.url = url;
+               this.query = query;
+               this.handleAs = handleAs;
+               this.id = id;
+               this.canDelete = canDelete;
+               this.json = json;
+       }
+       =====*/
+
+
+
+       dojo._ioSetArgs = function(/*dojo.__IoArgs*/args,
+                       /*Function*/canceller,
+                       /*Function*/okHandler,
+                       /*Function*/errHandler){
+               //      summary: 
+               //              sets up the Deferred and ioArgs property on the Deferred so it
+               //              can be used in an io call.
+               //      args:
+               //              The args object passed into the public io call. Recognized properties on
+               //              the args object are:
+               //      canceller:
+               //              The canceller function used for the Deferred object. The function
+               //              will receive one argument, the Deferred object that is related to the
+               //              canceller.
+               //      okHandler:
+               //              The first OK callback to be registered with Deferred. It has the opportunity
+               //              to transform the OK response. It will receive one argument -- the Deferred
+               //              object returned from this function.
+               //      errHandler:
+               //              The first error callback to be registered with Deferred. It has the opportunity
+               //              to do cleanup on an error. It will receive two arguments: error (the 
+               //              Error object) and dfd, the Deferred object returned from this function.
+
+               var ioArgs = {args: args, url: args.url};
+
+               //Get values from form if requestd.
+               var formObject = null;
+               if(args.form){ 
+                       var form = _d.byId(args.form);
+                       //IE requires going through getAttributeNode instead of just getAttribute in some form cases, 
+                       //so use it for all.  See #2844
+                       var actnNode = form.getAttributeNode("action");
+                       ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null); 
+                       formObject = _d.formToObject(form);
+               }
+
+               // set up the query params
+               var miArgs = [{}];
+       
+               if(formObject){
+                       // potentially over-ride url-provided params w/ form values
+                       miArgs.push(formObject);
+               }
+               if(args.content){
+                       // stuff in content over-rides what's set by form
+                       miArgs.push(args.content);
+               }
+               if(args.preventCache){
+                       miArgs.push({"dojo.preventCache": new Date().valueOf()});
+               }
+               ioArgs.query = _d.objectToQuery(_d.mixin.apply(null, miArgs));
+       
+               // .. and the real work of getting the deferred in order, etc.
+               ioArgs.handleAs = args.handleAs || "text";
+               var d = new _d.Deferred(canceller);
+               d.addCallbacks(okHandler, function(error){
+                       return errHandler(error, d);
+               });
+
+               //Support specifying load, error and handle callback functions from the args.
+               //For those callbacks, the "this" object will be the args object.
+               //The callbacks will get the deferred result value as the
+               //first argument and the ioArgs object as the second argument.
+               var ld = args.load;
+               if(ld && _d.isFunction(ld)){
+                       d.addCallback(function(value){
+                               return ld.call(args, value, ioArgs);
+                       });
+               }
+               var err = args.error;
+               if(err && _d.isFunction(err)){
+                       d.addErrback(function(value){
+                               return err.call(args, value, ioArgs);
+                       });
+               }
+               var handle = args.handle;
+               if(handle && _d.isFunction(handle)){
+                       d.addBoth(function(value){
+                               return handle.call(args, value, ioArgs);
+                       });
+               }
+               
+               d.ioArgs = ioArgs;
+       
+               // FIXME: need to wire up the xhr object's abort method to something
+               // analagous in the Deferred
+               return d;
+       }
+
+       var _deferredCancel = function(/*Deferred*/dfd){
+               //summary: canceller function for dojo._ioSetArgs call.
+               
+               dfd.canceled = true;
+               var xhr = dfd.ioArgs.xhr;
+               var _at = typeof xhr.abort;
+               if(_at == "function" || _at == "unknown"){
+                       xhr.abort();
+               }
+               var err = new Error("xhr cancelled");
+               err.dojoType = "cancel";
+               return err;
+       }
+       var _deferredOk = function(/*Deferred*/dfd){
+               //summary: okHandler function for dojo._ioSetArgs call.
+
+               return _d._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);
+       }
+       var _deferError = function(/*Error*/error, /*Deferred*/dfd){
+               //summary: errHandler function for dojo._ioSetArgs call.
+               
+               // console.debug("xhr error in:", dfd.ioArgs.xhr);
+               console.debug(error);
+               return error;
+       }
+
+       var _makeXhrDeferred = function(/*dojo.__XhrArgs*/args){
+               //summary: makes the Deferred object for this xhr request.
+               var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError);
+               //Pass the args to _xhrObj, to allow xhr iframe proxy interceptions.
+               dfd.ioArgs.xhr = _d._xhrObj(dfd.ioArgs.args);
+               return dfd;
+       }
+
+       // avoid setting a timer per request. It degrades performance on IE
+       // something fierece if we don't use unified loops.
+       var _inFlightIntvl = null;
+       var _inFlight = [];
+       var _watchInFlight = function(){
+               //summary: 
+               //              internal method that checks each inflight XMLHttpRequest to see
+               //              if it has completed or if the timeout situation applies.
+               
+               var now = (new Date()).getTime();
+               // make sure sync calls stay thread safe, if this callback is called
+               // during a sync call and this results in another sync call before the
+               // first sync call ends the browser hangs
+               if(!_d._blockAsync){
+                       // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating
+                       // note: the second clause is an assigment on purpose, lint may complain
+                       for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){
+                               var dfd = tif.dfd;
+                               try{
+                                       if(!dfd || dfd.canceled || !tif.validCheck(dfd)){
+                                               _inFlight.splice(i--, 1); 
+                                       }else if(tif.ioCheck(dfd)){
+                                               _inFlight.splice(i--, 1);
+                                               tif.resHandle(dfd);
+                                       }else if(dfd.startTime){
+                                               //did we timeout?
+                                               if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){
+                                                       _inFlight.splice(i--, 1);
+                                                       var err = new Error("timeout exceeded");
+                                                       err.dojoType = "timeout";
+                                                       dfd.errback(err);
+                                                       //Cancel the request so the io module can do appropriate cleanup.
+                                                       dfd.cancel();
+                                               }
+                                       }
+                               }catch(e){
+                                       // FIXME: make sure we errback! (fixed?  remove console.debug?)
+                                       console.debug(e);
+                                       dfd.errback(new Error("_watchInFlightError!"));
+                               }
+                       }
+               }
+
+               if(!_inFlight.length){
+                       clearInterval(_inFlightIntvl);
+                       _inFlightIntvl = null;
+                       return;
+               }
+
+       }
+
+       dojo._ioCancelAll = function(){
+               //summary: Cancels all pending IO requests, regardless of IO type
+               //(xhr, script, iframe).
+               try{
+                       _d.forEach(_inFlight, function(i){
+                               i.dfd.cancel();
+                       });
+               }catch(e){/*squelch*/}
+       }
+
+       //Automatically call cancel all io calls on unload
+       //in IE for trac issue #2357.
+       if(_d.isIE){
+               _d.addOnUnload(_d._ioCancelAll);
+       }
+
+       _d._ioWatch = function(/*Deferred*/dfd,
+               /*Function*/validCheck,
+               /*Function*/ioCheck,
+               /*Function*/resHandle){
+               //summary: watches the io request represented by dfd to see if it completes.
+               //dfd:
+               //              The Deferred object to watch.
+               //validCheck:
+               //              Function used to check if the IO request is still valid. Gets the dfd
+               //              object as its only argument.
+               //ioCheck:
+               //              Function used to check if basic IO call worked. Gets the dfd
+               //              object as its only argument.
+               //resHandle:
+               //              Function used to process response. Gets the dfd
+               //              object as its only argument.
+               if(dfd.ioArgs.args.timeout){
+                       dfd.startTime = (new Date()).getTime();
+               }
+               _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle});
+               if(!_inFlightIntvl){
+                       _inFlightIntvl = setInterval(_watchInFlight, 50);
+               }
+               _watchInFlight(); // handle sync requests
+       }
+
+       var _defaultContentType = "application/x-www-form-urlencoded";
+
+       var _validCheck = function(/*Deferred*/dfd){
+               return dfd.ioArgs.xhr.readyState; //boolean
+       }
+       var _ioCheck = function(/*Deferred*/dfd){
+               return 4 == dfd.ioArgs.xhr.readyState; //boolean
+       }
+       var _resHandle = function(/*Deferred*/dfd){
+               var xhr = dfd.ioArgs.xhr;
+               if(_d._isDocumentOk(xhr)){
+                       dfd.callback(dfd);
+               }else{
+                       var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status);
+                       err.status = xhr.status;
+                       err.responseText = xhr.responseText;
+                       dfd.errback(err);
+               }
+       }
+
+       var _doIt = function(/*String*/type, /*Deferred*/dfd){
+               // IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open).
+               // workaround for IE6's apply() "issues"
+               var ioArgs = dfd.ioArgs;
+               var args = ioArgs.args;
+               var xhr = ioArgs.xhr;
+               xhr.open(type, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined);
+               if(args.headers){
+                       for(var hdr in args.headers){
+                               if(hdr.toLowerCase() === "content-type" && !args.contentType){
+                                       args.contentType = args.headers[hdr];
+                               }else{
+                                       xhr.setRequestHeader(hdr, args.headers[hdr]);
+                               }
+                       }
+               }
+               // FIXME: is this appropriate for all content types?
+               xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType);
+               if(!args.headers || !args.headers["X-Requested-With"]){
+                       xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+               }
+               // FIXME: set other headers here!
+               try{
+                       xhr.send(ioArgs.query);
+               }catch(e){
+                       dfd.cancel();
+               }
+               _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle);
+               xhr = null;
+               return dfd; //Deferred
+       }
+
+       dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){
+               //summary: Adds query params discovered by the io deferred construction to the URL.
+               //Only use this for operations which are fundamentally GET-type operations.
+               if(ioArgs.query.length){
+                       ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query;
+                       ioArgs.query = null;
+               }               
+       }
+
+       /*=====
+       dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, {
+               constructor: function(){
+                       //      summary:
+                       //              In addition to the properties listed for the dojo._IoArgs type,
+                       //              the following properties are allowed for dojo.xhr* methods.
+                       //      handleAs: String?
+                       //              Acceptable values are: text (default), json, json-comment-optional,
+                       //              json-comment-filtered, javascript, xml
+                       //      sync: Boolean?
+                       //              false is default. Indicates whether the request should
+                       //              be a synchronous (blocking) request.
+                       //      headers: Object?
+                       //              Additional HTTP headers to send in the request.
+                       this.handleAs = handleAs;
+                       this.sync = sync;
+                       this.headers = headers;
+               }
+       });
+       =====*/
+
+       dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){
+               //      summary: 
+               //              Sends an HTTP request with the given method. If the request has an 
+               //              HTTP body, then pass true for hasBody. The method argument should be uppercase.
+               //              Also look at dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts
+               //              for those HTTP methods. There are also methods for "raw" PUT and POST methods
+               //              via dojo.rawXhrPut() and dojo.rawXhrPost() respectively.
+               var dfd = _makeXhrDeferred(args);
+               if(!hasBody){
+                       _d._ioAddQueryToUrl(dfd.ioArgs);
+               }
+               return _doIt(method, dfd); // dojo.Deferred
+       }
+
+       dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){
+               //      summary: 
+               //              Sends an HTTP GET request to the server.
+               return _d.xhr("GET", args); //dojo.Deferred
+       }
+
+       dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){
+               //summary: 
+               //              Sends an HTTP POST request to the server.
+               return _d.xhr("POST", args, true); // dojo.Deferred
+       }
+
+       dojo.rawXhrPost = function(/*dojo.__XhrArgs*/ args){
+               //      summary:
+               //              Sends an HTTP POST request to the server. In addtion to the properties
+               //              listed for the dojo.__XhrArgs type, the following property is allowed:
+               //      postData:
+               //              String. The raw data to send in the body of the POST request.
+               var dfd = _makeXhrDeferred(args);
+               dfd.ioArgs.query = args.postData;
+               return _doIt("POST", dfd); // dojo.Deferred
+       }
+
+       dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){
+               //      summary:
+               //              Sends an HTTP PUT request to the server.
+               return _d.xhr("PUT", args, true); // dojo.Deferred
+       }
+
+       dojo.rawXhrPut = function(/*dojo.__XhrArgs*/ args){
+               //      summary:
+               //              Sends an HTTP PUT request to the server. In addtion to the properties
+               //              listed for the dojo.__XhrArgs type, the following property is allowed:
+               //      putData:
+               //              String. The raw data to send in the body of the PUT request.
+               var dfd = _makeXhrDeferred(args);
+               var ioArgs = dfd.ioArgs;
+               if(args.putData){
+                       ioArgs.query = args.putData;
+                       args.putData = null;
+               }
+               return _doIt("PUT", dfd); // dojo.Deferred
+       }
+
+       dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){
+               //      summary:
+               //              Sends an HTTP DELETE request to the server.
+               return _d.xhr("DELETE", args); //dojo.Deferred
+       }
+
+       /*
+       dojo.wrapForm = function(formNode){
+               //summary:
+               //              A replacement for FormBind, but not implemented yet.
+
+               // FIXME: need to think harder about what extensions to this we might
+               // want. What should we allow folks to do w/ this? What events to
+               // set/send?
+               throw new Error("dojo.wrapForm not yet implemented");
+       }
+       */
+})();
+
+}
+
+if(!dojo._hasResource["dojo._base.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.fx"] = true;
+dojo.provide("dojo._base.fx");
+
+
+
+
+
+
+/*
+       Animation losely package based on Dan Pupius' work, contributed under CLA: 
+               http://pupius.co.uk/js/Toolkit.Drawing.js
+*/
+(function(){ 
+
+       var d = dojo;
+       
+       dojo._Line = function(/*int*/ start, /*int*/ end){
+               //      summary:
+               //              dojo._Line is the object used to generate values from a start value
+               //              to an end value
+               //      start: int
+               //              Beginning value for range
+               //      end: int
+               //              Ending value for range
+               this.start = start;
+               this.end = end;
+               this.getValue = function(/*float*/ n){
+                       //      summary: returns the point on the line
+                       //      n: a floating point number greater than 0 and less than 1
+                       return ((this.end - this.start) * n) + this.start; // Decimal
+               }
+       }
+       
+       d.declare("dojo._Animation", null, {
+               //      summary
+               //              A generic animation class that fires callbacks into its handlers
+               //              object at various states. Nearly all dojo animation functions
+               //              return an instance of this method, usually without calling the
+               //              .play() method beforehand. Therefore, you will likely need to
+               //              call .play() on instances of dojo._Animation when one is
+               //              returned.
+               constructor: function(/*Object*/ args){
+                       d.mixin(this, args);
+                       if(d.isArray(this.curve)){
+                               /* curve: Array
+                                       pId: a */
+                               this.curve = new d._Line(this.curve[0], this.curve[1]);
+                       }
+               },
+               
+               // duration: Integer
+               //      The time in milliseonds the animation will take to run
+               duration: 350,
+       
+       /*=====
+               // curve: dojo._Line||Array
+               //      A two element array of start and end values, or a dojo._Line instance to be
+               //      used in the Animation. 
+               curve: null,
+       
+               // easing: Function
+               //      A Function to adjust the acceleration (or deceleration) of the progress 
+               //      across a dojo._Line
+               easing: null,
+       =====*/
+       
+               // repeat: Integer
+               //      The number of times to loop the animation
+               repeat: 0,
+       
+               // rate: Integer
+               //      the time in milliseconds to wait before advancing to next frame 
+               //      (used as a fps timer: rate/1000 = fps)
+               rate: 10 /* 100 fps */,
+       
+       /*===== 
+               // delay: Integer
+               //      The time in milliseconds to wait before starting animation after it has been .play()'ed
+               delay: null,
+       
+               // events
+               //
+               // beforeBegin: Event
+               //      Synthetic event fired before a dojo._Animation begins playing (synchronous)
+               beforeBegin: null,
+       
+               // onBegin: Event
+               //      Synthetic event fired as a dojo._Animation begins playing (useful?)
+               onBegin: null,
+       
+               // onAnimate: Event
+               //      Synthetic event fired at each interval of a dojo._Animation
+               onAnimate: null,
+       
+               // onEnd: Event
+               //      Synthetic event fired after the final frame of a dojo._Animation
+               onEnd: null,
+       
+               // onPlay: Event
+               //      Synthetic event fired any time a dojo._Animation is play()'ed
+               onPlay: null,
+       
+               // onPause: Event
+               //      Synthetic event fired when a dojo._Animation is paused
+               onPause: null,
+       
+               // onStop: Event
+               //      Synthetic event fires when a dojo._Animation is stopped
+               onStop: null,
+       
+       =====*/
+       
+               _percent: 0,
+               _startRepeatCount: 0,
+       
+               _fire: function(/*Event*/ evt, /*Array?*/ args){
+                       //      summary:
+                       //              Convenience function.  Fire event "evt" and pass it the
+                       //              arguments specified in "args".
+                       //      evt:
+                       //              The event to fire.
+                       //      args:
+                       //              The arguments to pass to the event.
+                       try{
+                               if(this[evt]){
+                                       this[evt].apply(this, args||[]);
+                               }
+                       }catch(e){
+                               // squelch and log because we shouldn't allow exceptions in
+                               // synthetic event handlers to cause the internal timer to run
+                               // amuck, potentially pegging the CPU. I'm not a fan of this
+                               // squelch, but hopefully logging will make it clear what's
+                               // going on
+                               console.error("exception in animation handler for:", evt);
+                               console.error(e);
+                       }
+                       return this; // dojo._Animation
+               },
+       
+               play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
+                       // summary:
+                       //              Start the animation.
+                       // delay:
+                       //              How many milliseconds to delay before starting.
+                       // gotoStart:
+                       //              If true, starts the animation from the beginning; otherwise,
+                       //              starts it from its current position.
+                       var _t = this;
+                       if(gotoStart){
+                               _t._stopTimer();
+                               _t._active = _t._paused = false;
+                               _t._percent = 0;
+                       }else if(_t._active && !_t._paused){
+                               return _t; // dojo._Animation
+                       }
+       
+                       _t._fire("beforeBegin");
+       
+                       var de = delay||_t.delay;
+                       var _p = dojo.hitch(_t, "_play", gotoStart);
+                       if(de > 0){
+                               setTimeout(_p, de);
+                               return _t; // dojo._Animation
+                       }
+                       _p();
+                       return _t;
+               },
+       
+               _play: function(gotoStart){
+                       var _t = this;
+                       _t._startTime = new Date().valueOf();
+                       if(_t._paused){
+                               _t._startTime -= _t.duration * _t._percent;
+                       }
+                       _t._endTime = _t._startTime + _t.duration;
+       
+                       _t._active = true;
+                       _t._paused = false;
+       
+                       var value = _t.curve.getValue(_t._percent);
+                       if(!_t._percent){
+                               if(!_t._startRepeatCount){
+                                       _t._startRepeatCount = _t.repeat;
+                               }
+                               _t._fire("onBegin", [value]);
+                       }
+       
+                       _t._fire("onPlay", [value]);
+       
+                       _t._cycle();
+                       return _t; // dojo._Animation
+               },
+       
+               pause: function(){
+                       // summary: Pauses a running animation.
+                       this._stopTimer();
+                       if(!this._active){ return this; /*dojo._Animation*/ }
+                       this._paused = true;
+                       this._fire("onPause", [this.curve.getValue(this._percent)]);
+                       return this; // dojo._Animation
+               },
+       
+               gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){
+                       //      summary:
+                       //              Sets the progress of the animation.
+                       //      percent:
+                       //              A percentage in decimal notation (between and including 0.0 and 1.0).
+                       //      andPlay:
+                       //              If true, play the animation after setting the progress.
+                       this._stopTimer();
+                       this._active = this._paused = true;
+                       this._percent = percent;
+                       if(andPlay){ this.play(); }
+                       return this; // dojo._Animation
+               },
+       
+               stop: function(/*boolean?*/ gotoEnd){
+                       // summary: Stops a running animation.
+                       // gotoEnd: If true, the animation will end.
+                       if(!this._timer){ return this; /* dojo._Animation */ }
+                       this._stopTimer();
+                       if(gotoEnd){
+                               this._percent = 1;
+                       }
+                       this._fire("onStop", [this.curve.getValue(this._percent)]);
+                       this._active = this._paused = false;
+                       return this; // dojo._Animation
+               },
+       
+               status: function(){
+                       // summary: Returns a string token representation of the status of
+                       //                      the animation, one of: "paused", "playing", "stopped"
+                       if(this._active){
+                               return this._paused ? "paused" : "playing"; // String
+                       }
+                       return "stopped"; // String
+               },
+       
+               _cycle: function(){
+                       var _t = this;
+                       if(_t._active){
+                               var curr = new Date().valueOf();
+                               var step = (curr - _t._startTime) / (_t._endTime - _t._startTime);
+       
+                               if(step >= 1){
+                                       step = 1;
+                               }
+                               _t._percent = step;
+       
+                               // Perform easing
+                               if(_t.easing){
+                                       step = _t.easing(step);
+                               }
+       
+                               _t._fire("onAnimate", [_t.curve.getValue(step)]);
+       
+                               if(_t._percent < 1){
+                                       _t._startTimer();
+                               }else{
+                                       _t._active = false;
+       
+                                       if(_t.repeat > 0){
+                                               _t.repeat--;
+                                               _t.play(null, true);
+                                       }else if(_t.repeat == -1){
+                                               _t.play(null, true);
+                                       }else{
+                                               if(_t._startRepeatCount){
+                                                       _t.repeat = _t._startRepeatCount;
+                                                       _t._startRepeatCount = 0;
+                                               }
+                                       }
+                                       _t._percent = 0;
+                                       _t._fire("onEnd");
+                                       _t._stopTimer();
+                               }
+                       }
+                       return _t; // dojo._Animation
+               }
+       });
+
+       var ctr = 0;
+       var _globalTimerList = [];
+       var runner = {
+               run: function(){ }
+       };
+       var timer = null;
+       dojo._Animation.prototype._startTimer = function(){
+               // this._timer = setTimeout(dojo.hitch(this, "_cycle"), this.rate);
+               if(!this._timer){
+                       this._timer = d.connect(runner, "run", this, "_cycle");
+                       ctr++;
+               }
+               if(!timer){
+                       timer = setInterval(d.hitch(runner, "run"), this.rate);
+               }
+       };
+
+       dojo._Animation.prototype._stopTimer = function(){
+               if(this._timer){
+                       d.disconnect(this._timer);
+                       this._timer = null;
+                       ctr--;
+               }
+               if(ctr <= 0){
+                       clearInterval(timer);
+                       timer = null;
+                       ctr = 0;
+               }
+       };
+
+       var _makeFadeable = (d.isIE) ? function(node){
+               // only set the zoom if the "tickle" value would be the same as the
+               // default
+               var ns = node.style;
+               if(!ns.zoom.length && d.style(node, "zoom") == "normal"){
+                       // make sure the node "hasLayout"
+                       // NOTE: this has been tested with larger and smaller user-set text
+                       // sizes and works fine
+                       ns.zoom = "1";
+                       // node.style.zoom = "normal";
+               }
+               // don't set the width to auto if it didn't already cascade that way.
+               // We don't want to f anyones designs
+               if(!ns.width.length && d.style(node, "width") == "auto"){
+                       ns.width = "auto";
+               }
+       } : function(){};
+
+       dojo._fade = function(/*Object*/ args){
+               //      summary: 
+               //              Returns an animation that will fade the node defined by
+               //              args.node from the start to end values passed (args.start
+               //              args.end) (end is mandatory, start is optional)
+
+               args.node = d.byId(args.node);
+               var fArgs = d.mixin({ properties: {} }, args);
+               var props = (fArgs.properties.opacity = {});
+               props.start = !("start" in fArgs) ?
+                       function(){ 
+                               return Number(d.style(fArgs.node, "opacity")); 
+                       } : fArgs.start;
+               props.end = fArgs.end;
+
+               var anim = d.animateProperty(fArgs);
+               d.connect(anim, "beforeBegin", d.partial(_makeFadeable, fArgs.node));
+
+               return anim; // dojo._Animation
+       }
+
+       /*=====
+       dojo.__FadeArgs = function(node, duration, easing){
+               //      node: DOMNode|String
+               //              The node referenced in the animation
+               //      duration: Integer?
+               //              Duration of the animation in milliseconds.
+               //      easing: Function?
+               //              An easing function.
+               this.node = node;
+               this.duration = duration;
+               this.easing = easing;
+       }
+       =====*/
+
+       dojo.fadeIn = function(/*dojo.__FadeArgs*/ args){
+               // summary: 
+               //              Returns an animation that will fade node defined in 'args' from
+               //              its current opacity to fully opaque.
+               return d._fade(d.mixin({ end: 1 }, args)); // dojo._Animation
+       }
+
+       dojo.fadeOut = function(/*dojo.__FadeArgs*/  args){
+               // summary: 
+               //              Returns an animation that will fade node defined in 'args'
+               //              from its current opacity to fully transparent.
+               return d._fade(d.mixin({ end: 0 }, args)); // dojo._Animation
+       }
+
+       dojo._defaultEasing = function(/*Decimal?*/ n){
+               // summary: The default easing function for dojo._Animation(s)
+               return 0.5 + ((Math.sin((n + 1.5) * Math.PI))/2);
+       }
+
+       var PropLine = function(properties){
+               // PropLine is an internal class which is used to model the values of
+               // an a group of CSS properties across an animation lifecycle. In
+               // particular, the "getValue" function handles getting interpolated
+               // values between start and end for a particular CSS value.
+               this._properties = properties;
+               for(var p in properties){
+                       var prop = properties[p];
+                       if(prop.start instanceof d.Color){
+                               // create a reusable temp color object to keep intermediate results
+                               prop.tempColor = new d.Color();
+                       }
+               }
+               this.getValue = function(r){
+                       var ret = {};
+                       for(var p in this._properties){
+                               var prop = this._properties[p];
+                               var start = prop.start;
+                               if(start instanceof d.Color){
+                                       ret[p] = d.blendColors(start, prop.end, r, prop.tempColor).toCss();
+                               }else if(!d.isArray(start)){
+                                       ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units||"px" : "");
+                               }
+                       }
+                       return ret;
+               }
+       }
+
+       /*=====
+       dojo.declare("dojo.__AnimArgs", [dojo.__FadeArgs], {
+               // Properties: Object?
+               //      A hash map of style properties to Objects describing the transition,
+               //      such as the properties of dojo._Line with an additional 'unit' property
+               properties: {}
+               
+               //TODOC: add event callbacks
+       });
+       =====*/
+
+       dojo.animateProperty = function(/*dojo.__AnimArgs*/ args){
+               //      summary: 
+               //              Returns an animation that will transition the properties of
+               //              node defined in 'args' depending how they are defined in
+               //              'args.properties'
+               //
+               // description:
+               //              dojo.animateProperty is the foundation of most dojo.fx
+               //              animations. It takes an object of "properties" corresponding to
+               //              style properties, and animates them in parallel over a set
+               //              duration.
+               //      
+               //      example:
+               //              A simple animation that changes the width of the specified node.
+               //      |       dojo.animateProperty({ 
+               //      |               node: "nodeId",
+               //      |               properties: { width: 400 },
+               //      |       }).play();
+               //              Dojo figures out the start value for the width and converts the
+               //              integer specified for the width to the more expressive but
+               //              verbose form `{ width: { end: '400', units: 'px' } }` which you
+               //              can also specify directly
+               //      example:
+               //              animate width, height, and padding over 2 seconds...the
+               //              pedantic way:
+               //      |       dojo.animateProperty({ node: node, duration:2000,
+               //      |               properties: {
+               //      |                       width: { start: '200', end: '400', unit:"px" },
+               //      |                       height: { start:'200', end: '400', unit:"px" },
+               //      |                       paddingTop: { start:'5', end:'50', unit:"px" } 
+               //      |               }
+               //      |       }).play();
+               //
+               //      example:
+               //              plug in a different easing function and register a callback for
+               //              when the animation ends. Easing functions accept values between
+               //              zero and one and return a value on that basis. In this case, an
+               //              exponential-in curve.
+               //      |       dojo.animateProperty({ 
+               //      |               node: "nodeId",
+               //      |               // dojo figures out the start value
+               //      |               properties: { width: { end: 400 } },
+               //      |               easing: function(n){
+               //      |                       return (n==0) ? 0 : Math.pow(2, 10 * (n - 1));
+               //      |               },
+               //      |               onEnd: function(){
+               //      |                       // called when the animation finishes
+               //      |               }
+               //      |       }).play(500); // delay playing half a second
+
+               args.node = d.byId(args.node);
+               if(!args.easing){ args.easing = d._defaultEasing; }
+
+               var anim = new d._Animation(args);
+               d.connect(anim, "beforeBegin", anim, function(){
+                       var pm = {};
+                       for(var p in this.properties){
+                               // Make shallow copy of properties into pm because we overwrite
+                               // some values below. In particular if start/end are functions
+                               // we don't want to overwrite them or the functions won't be
+                               // called if the animation is reused.
+                               if(p == "width" || p == "height"){
+                                       this.node.display = "block";
+                               }
+                               var prop = this.properties[p];
+                               prop = pm[p] = d.mixin({}, (d.isObject(prop) ? prop: { end: prop }));
+
+                               if(d.isFunction(prop.start)){
+                                       prop.start = prop.start();
+                               }
+                               if(d.isFunction(prop.end)){
+                                       prop.end = prop.end();
+                               }
+                               var isColor = (p.toLowerCase().indexOf("color") >= 0);
+                               function getStyle(node, p){
+                                       // dojo.style(node, "height") can return "auto" or "" on IE; this is more reliable:
+                                       var v = ({height: node.offsetHeight, width: node.offsetWidth})[p];
+                                       if(v !== undefined){ return v; }
+                                       v = d.style(node, p);
+                                       return (p=="opacity") ? Number(v) : (isColor ? v : parseFloat(v));
+                               }
+                               if(!("end" in prop)){
+                                       prop.end = getStyle(this.node, p);
+                               }else if(!("start" in prop)){
+                                       prop.start = getStyle(this.node, p);
+                               }
+
+                               if(isColor){
+                                       prop.start = new d.Color(prop.start);
+                                       prop.end = new d.Color(prop.end);
+                               }else{
+                                       prop.start = (p == "opacity") ? Number(prop.start) : parseFloat(prop.start);
+                               }
+                       }
+                       this.curve = new PropLine(pm);
+               });
+               d.connect(anim, "onAnimate", anim, function(propValues){
+                       // try{
+                       for(var s in propValues){
+                               d.style(this.node, s, propValues[s]);
+                               // this.node.style[s] = propValues[s];
+                       }
+               });
+               return anim; // dojo._Animation
+       }
+
+       dojo.anim = function(   /*DOMNode|String*/      node, 
+                                                       /*Object*/                      properties, 
+                                                       /*Integer?*/            duration, 
+                                                       /*Function?*/           easing, 
+                                                       /*Function?*/           onEnd,
+                                                       /*Integer?*/            delay){
+               //      summary:
+               //              A simpler interface to `dojo.animateProperty()`, also returns
+               //              an instance of `dojo._Animation` but begins the animation
+               //              immediately, unlike nearly every other Dojo animation API.
+               //      description:
+               //              `dojo.anim` is a simpler (but somewhat less powerful) version
+               //              of `dojo.animateProperty`.  It uses defaults for many basic properties
+               //              and allows for positional parameters to be used in place of the
+               //              packed "property bag" which is used for other Dojo animation
+               //              methods.
+               //
+               //              The `dojo._Animation` object returned from `dojo.anim` will be
+               //              already playing when it is returned from this function, so
+               //              calling play() on it again is (usually) a no-op.
+               //      node:
+               //              a DOM node or the id of a node to animate CSS properties on
+               //      duration:
+               //              The number of milliseconds over which the animation
+               //              should run. Defaults to the global animation default duration
+               //              (350ms).
+               //      easing:
+               //              An easing function over which to calculate acceleration
+               //              and deceleration of the animation through its duration.
+               //              A default easing algorithm is provided, but you may
+               //              plug in any you wish. A large selection of easing algorithms
+               //              are available in `dojox.fx.easing`.
+               //      onEnd:
+               //              A function to be called when the animation finishes
+               //              running.
+               //      delay:
+               //              The number of milliseconds to delay beginning the
+               //              animation by. The default is 0.
+               //      example:
+               //              Fade out a node
+               //      |       dojo.anim("id", { opacity: 0 });
+               //      example:
+               //              Fade out a node over a full second
+               //      |       dojo.anim("id", { opacity: 0 }, 1000);
+               return d.animateProperty({ 
+                       node: node,
+                       duration: duration||d._Animation.prototype.duration,
+                       properties: properties,
+                       easing: easing,
+                       onEnd: onEnd 
+               }).play(delay||0);
+       }
+})();
+
+}
+
+if(!dojo._hasResource["dojo._base.browser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.browser"] = true;
+dojo.provide("dojo._base.browser");
+
+
+
+
+
+
+
+
+
+//Need this to be the last code segment in base, so do not place any
+//dojo.requireIf calls in this file. Otherwise, due to how the build system
+//puts all requireIf dependencies after the current file, the require calls
+//could be called before all of base is defined.
+if(dojo.config.require){
+       dojo.forEach(dojo.config.require, "dojo['require'](item);");
+}
+
+}
+
+
+       if(dojo.config.afterOnLoad && dojo.isBrowser){
+               //Dojo is being added to the page after page load, so just trigger
+               //the init sequence after a timeout. Using a timeout so the rest of this
+               //script gets evaluated properly. This work needs to happen after the
+               //dojo.config.require work done in dojo._base.
+               window.setTimeout(dojo._fakeLoadInit, 1000);
+       }
+
+})();
+
diff --git a/webapps/cometd/examples/dojo/dojo/dojo.xd.js b/webapps/cometd/examples/dojo/dojo/dojo.xd.js
new file mode 100644 (file)
index 0000000..8372855
--- /dev/null
@@ -0,0 +1,20 @@
+/*\r
+       Copyright (c) 2004-2007, The Dojo Foundation\r
+       All Rights Reserved.\r
+\r
+       Licensed under the Academic Free License version 2.1 or above OR the\r
+       modified BSD license. For more information on Dojo licensing, see:\r
+\r
+               http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing\r
+*/\r
+\r
+/*\r
+       This is a compiled version of Dojo, built for deployment and not for\r
+       development. To get an editable version, please visit:\r
+\r
+               http://dojotoolkit.org\r
+\r
+       for documentation and information on getting the source.\r
+*/\r
+\r
+if(typeof dojo=="undefined"){(function(){if(typeof this["djConfig"]=="undefined"){this.djConfig={};}if((!this["console"])||(!console["firebug"])){this.console={};}var cn=["assert","count","debug","dir","dirxml","error","group","groupEnd","info","log","profile","profileEnd","time","timeEnd","trace","warn"];var i=0,tn;while((tn=cn[i++])){if(!console[tn]){console[tn]=function(){};}}if(typeof this["dojo"]=="undefined"){this.dojo={};}var d=dojo;dojo.global=this;var _5={isDebug:false,libraryScriptUri:"",preventBackButtonFix:true,delayMozLoadingFix:false};for(var _6 in _5){if(typeof djConfig[_6]=="undefined"){djConfig[_6]=_5[_6];}}var _7=["Browser","Rhino","Spidermonkey","Mobile"];var t;while(t=_7.shift()){d["is"+t]=false;}dojo.locale=djConfig.locale;var _9="$Rev: 11742 $".match(/\d+/);dojo.version={major:0,minor:0,patch:0,flag:"dev",revision:_9?Number(_9[0]):999999,toString:function(){with(d.version){return major+"."+minor+"."+patch+flag+" ("+revision+")";}}};if(typeof OpenAjax!="undefined"){OpenAjax.hub.registerLibrary("dojo","http://dojotoolkit.org",d.version.toString());}dojo._mixin=function(_a,_b){var _c={};for(var x in _b){if(_c[x]===undefined||_c[x]!=_b[x]){_a[x]=_b[x];}}if(d["isIE"]&&_b){var p=_b.toString;if(typeof p=="function"&&p!=_a.toString&&p!=_c.toString&&p!="\nfunction toString() {\n    [native code]\n}\n"){_a.toString=_b.toString;}}return _a;};dojo.mixin=function(_f,_10){for(var i=1,l=arguments.length;i<l;i++){d._mixin(_f,arguments[i]);}return _f;};dojo._getProp=function(_13,_14,_15){var obj=_15||d.global;for(var i=0,p;obj&&(p=_13[i]);i++){obj=(p in obj?obj[p]:(_14?obj[p]={}:undefined));}return obj;};dojo.setObject=function(_19,_1a,_1b){var _1c=_19.split("."),p=_1c.pop(),obj=d._getProp(_1c,true,_1b);return (obj&&p?(obj[p]=_1a):undefined);};dojo.getObject=function(_1f,_20,_21){return d._getProp(_1f.split("."),_20,_21);};dojo.exists=function(_22,obj){return !!d.getObject(_22,false,obj);};dojo["eval"]=function(_24){return d.global.eval?d.global.eval(_24):eval(_24);};d.deprecated=d.experimental=function(){};})();(function(){var d=dojo;d.mixin(d,{_loadedModules:{},_inFlightCount:0,_hasResource:{},_modulePrefixes:{dojo:{name:"dojo",value:"."},doh:{name:"doh",value:"../util/doh"},tests:{name:"tests",value:"tests"}},_moduleHasPrefix:function(_26){var mp=this._modulePrefixes;return !!(mp[_26]&&mp[_26].value);},_getModulePrefix:function(_28){var mp=this._modulePrefixes;if(this._moduleHasPrefix(_28)){return mp[_28].value;}return _28;},_loadedUrls:[],_postLoad:false,_loaders:[],_unloaders:[],_loadNotifying:false});dojo._loadUriAndCheck=function(uri,_2b,cb){var ok=false;try{ok=this._loadUri(uri,cb);}catch(e){console.error("failed loading "+uri+" with error: "+e);}return !!(ok&&this._loadedModules[_2b]);};dojo.loaded=function(){this._loadNotifying=true;this._postLoad=true;var mll=this._loaders;this._loaders=[];for(var x=0;x<mll.length;x++){try{mll[x]();}catch(e){console.error(e);}}this._loadNotifying=false;if(d._postLoad&&d._inFlightCount==0&&this._loaders.length>0){d._callLoaded();}};dojo.unloaded=function(){var mll=this._unloaders;while(mll.length){(mll.pop())();}};dojo.addOnLoad=function(obj,_32){if(arguments.length==1){d._loaders.push(obj);}else{if(arguments.length>1){d._loaders.push(function(){obj[_32]();});}}if(d._postLoad&&d._inFlightCount==0&&!d._loadNotifying){d._callLoaded();}};dojo.addOnUnload=function(obj,_34){if(arguments.length==1){d._unloaders.push(obj);}else{if(arguments.length>1){d._unloaders.push(function(){obj[_34]();});}}};dojo._modulesLoaded=function(){if(d._postLoad){return;}if(d._inFlightCount>0){console.warn("files still in flight!");return;}d._callLoaded();};dojo._callLoaded=function(){if(typeof setTimeout=="object"||(djConfig.useXDomain&&d.isOpera)){setTimeout("dojo.loaded();",0);}else{d.loaded();}};dojo._getModuleSymbols=function(_35){var _36=_35.split(".");for(var i=_36.length;i>0;i--){var _38=_36.slice(0,i).join(".");if((i==1)&&!this._moduleHasPrefix(_38)){_36[0]="../"+_36[0];}else{var _39=this._getModulePrefix(_38);if(_39!=_38){_36.splice(0,i,_39);break;}}}return _36;};dojo._global_omit_module_check=false;dojo._loadModule=dojo.require=function(_3a,_3b){_3b=this._global_omit_module_check||_3b;var _3c=this._loadedModules[_3a];if(_3c){return _3c;}var _3d=this._getModuleSymbols(_3a).join("/")+".js";var _3e=(!_3b)?_3a:null;var ok=this._loadPath(_3d,_3e);if(!ok&&!_3b){throw new Error("Could not load '"+_3a+"'; last tried '"+_3d+"'");}if(!_3b&&!this._isXDomain){_3c=this._loadedModules[_3a];if(!_3c){throw new Error("symbol '"+_3a+"' is not defined after loading '"+_3d+"'");}}return _3c;};dojo.provide=function(_40){_40=_40+"";return (d._loadedModules[_40]=d.getObject(_40,true));};dojo.platformRequire=function(_41){var _42=_41.common||[];var _43=_42.concat(_41[d._name]||_41["default"]||[]);for(var x=0;x<_43.length;x++){var _45=_43[x];if(_45.constructor==Array){d._loadModule.apply(d,_45);}else{d._loadModule(_45);}}};dojo.requireIf=function(_46,_47){if(_46===true){var _48=[];for(var i=1;i<arguments.length;i++){_48.push(arguments[i]);}d.require.apply(d,_48);}};dojo.requireAfterIf=d.requireIf;dojo.registerModulePath=function(_4a,_4b){d._modulePrefixes[_4a]={name:_4a,value:_4b};};dojo.requireLocalization=function(_4c,_4d,_4e,_4f){d.require("dojo.i18n");d.i18n._requireLocalization.apply(d.hostenv,arguments);};var ore=new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$");var ire=new RegExp("^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$");dojo._Url=function(){var n=null;var _a=arguments;var uri=_a[0];for(var i=1;i<_a.length;i++){if(!_a[i]){continue;}var _56=new d._Url(_a[i]+"");var _57=new d._Url(uri+"");if(_56.path==""&&!_56.scheme&&!_56.authority&&!_56.query){if(_56.fragment!=n){_57.fragment=_56.fragment;}_56=_57;}else{if(!_56.scheme){_56.scheme=_57.scheme;if(!_56.authority){_56.authority=_57.authority;if(_56.path.charAt(0)!="/"){var _58=_57.path.substring(0,_57.path.lastIndexOf("/")+1)+_56.path;var _59=_58.split("/");for(var j=0;j<_59.length;j++){if(_59[j]=="."){if(j==_59.length-1){_59[j]="";}else{_59.splice(j,1);j--;}}else{if(j>0&&!(j==1&&_59[0]=="")&&_59[j]==".."&&_59[j-1]!=".."){if(j==(_59.length-1)){_59.splice(j,1);_59[j-1]="";}else{_59.splice(j-1,2);j-=2;}}}}_56.path=_59.join("/");}}}}uri="";if(_56.scheme){uri+=_56.scheme+":";}if(_56.authority){uri+="//"+_56.authority;}uri+=_56.path;if(_56.query){uri+="?"+_56.query;}if(_56.fragment){uri+="#"+_56.fragment;}}this.uri=uri.toString();var r=this.uri.match(ore);this.scheme=r[2]||(r[1]?"":n);this.authority=r[4]||(r[3]?"":n);this.path=r[5];this.query=r[7]||(r[6]?"":n);this.fragment=r[9]||(r[8]?"":n);if(this.authority!=n){r=this.authority.match(ire);this.user=r[3]||n;this.password=r[4]||n;this.host=r[5];this.port=r[7]||n;}};dojo._Url.prototype.toString=function(){return this.uri;};dojo.moduleUrl=function(_5c,url){var loc=d._getModuleSymbols(_5c).join("/");if(!loc){return null;}if(loc.lastIndexOf("/")!=loc.length-1){loc+="/";}var _5f=loc.indexOf(":");if(loc.charAt(0)!="/"&&(_5f==-1||_5f>loc.indexOf("/"))){loc=d.baseUrl+loc;}return new d._Url(loc,url);};})();dojo.provide("dojo._base._loader.loader_xd");dojo._xdReset=function(){this._isXDomain=djConfig.useXDomain||false;this._xdTimer=0;this._xdInFlight={};this._xdOrderedReqs=[];this._xdDepMap={};this._xdContents=[];this._xdDefList=[];};dojo._xdReset();dojo._xdCreateResource=function(_60,_61,_62){var _63=_60.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg,"");var _64=[];var _65=/dojo.(require|requireIf|provide|requireAfterIf|platformRequire|requireLocalization)\(([\w\W]*?)\)/mg;var _66;while((_66=_65.exec(_63))!=null){if(_66[1]=="requireLocalization"){eval(_66[0]);}else{_64.push("\""+_66[1]+"\", "+_66[2]);}}var _67=[];_67.push("dojo._xdResourceLoaded({\n");if(_64.length>0){_67.push("depends: [");for(var i=0;i<_64.length;i++){if(i>0){_67.push(",\n");}_67.push("["+_64[i]+"]");}_67.push("],");}_67.push("\ndefineResource: function(dojo){");if(!djConfig["debugAtAllCosts"]||_61=="dojo._base._loader.loader_debug"){_67.push(_60);}_67.push("\n}, resourceName: '"+_61+"', resourcePath: '"+_62+"'});");return _67.join("");};dojo._xdIsXDomainPath=function(_69){var _6a=_69.indexOf(":");var _6b=_69.indexOf("/");if(_6a>0&&_6a<_6b){return true;}else{var url=this.baseUrl;_6a=url.indexOf(":");_6b=url.indexOf("/");if(_6a>0&&_6a<_6b&&(!location.host||url.indexOf("http://"+location.host)!=0)){return true;}}return false;};dojo._loadPath=function(_6d,_6e,cb){var _70=this._xdIsXDomainPath(_6d);this._isXDomain|=_70;var uri=this.baseUrl+_6d;if(_70){var _72=_6d.indexOf(":");var _73=_6d.indexOf("/");if(_72>0&&_72<_73){uri=_6d;}}if(djConfig.cacheBust&&dojo.isBrowser){uri+="?"+String(djConfig.cacheBust).replace(/\W+/g,"");}try{return ((!_6e||this._isXDomain)?this._loadUri(uri,cb,_70,_6e):this._loadUriAndCheck(uri,_6e,cb));}catch(e){console.debug(e);return false;}};dojo._loadUri=function(uri,cb,_76,_77){if(this._loadedUrls[uri]){return 1;}if(this._isXDomain&&_77&&_77!="dojo.i18n"){this._xdOrderedReqs.push(_77);if(_76||uri.indexOf("/nls/")==-1){this._xdInFlight[_77]=true;this._inFlightCount++;}if(!this._xdTimer){this._xdTimer=setInterval("dojo._xdWatchInFlight();",100);}this._xdStartTime=(new Date()).getTime();}if(_76){var _78=uri.lastIndexOf(".");if(_78<=0){_78=uri.length-1;}var _79=uri.substring(0,_78)+".xd";if(_78!=uri.length-1){_79+=uri.substring(_78,uri.length);}var _7a=document.createElement("script");_7a.type="text/javascript";_7a.src=_79;if(!this.headElement){this._headElement=document.getElementsByTagName("head")[0];if(!this._headElement){this._headElement=document.getElementsByTagName("html")[0];}}this._headElement.appendChild(_7a);}else{var _7b=this._getText(uri,null,true);if(_7b==null){return 0;}if(this._isXDomain&&uri.indexOf("/nls/")==-1&&_77!="dojo.i18n"){var res=this._xdCreateResource(_7b,_77,uri);dojo.eval(res);}else{if(cb){_7b="("+_7b+")";}var _7d=dojo.eval(_7b);if(cb){cb(_7d);}}}this._loadedUrls[uri]=true;this._loadedUrls.push(uri);return true;};dojo._xdResourceLoaded=function(res){var _7f=res.depends;var _80=null;var _81=null;var _82=[];if(_7f&&_7f.length>0){var dep=null;var _84=0;var _85=false;for(var i=0;i<_7f.length;i++){dep=_7f[i];if(dep[0]=="provide"){_82.push(dep[1]);}else{if(!_80){_80=[];}if(!_81){_81=[];}var _87=this._xdUnpackDependency(dep);if(_87.requires){_80=_80.concat(_87.requires);}if(_87.requiresAfter){_81=_81.concat(_87.requiresAfter);}}var _88=dep[0];var _89=_88.split(".");if(_89.length==2){dojo[_89[0]][_89[1]].apply(dojo[_89[0]],dep.slice(1));}else{dojo[_88].apply(dojo,dep.slice(1));}}if(_82.length==1&&_82[0]=="dojo._base._loader.loader_debug"){res.defineResource(dojo);}else{var _8a=this._xdContents.push({content:res.defineResource,resourceName:res["resourceName"],resourcePath:res["resourcePath"],isDefined:false})-1;for(var i=0;i<_82.length;i++){this._xdDepMap[_82[i]]={requires:_80,requiresAfter:_81,contentIndex:_8a};}}for(var i=0;i<_82.length;i++){this._xdInFlight[_82[i]]=false;}}};dojo._xdLoadFlattenedBundle=function(_8b,_8c,_8d,_8e){_8d=_8d||"root";var _8f=dojo.i18n.normalizeLocale(_8d).replace("-","_");var _90=[_8b,"nls",_8c].join(".");var _91=dojo["provide"](_90);_91[_8f]=_8e;var _92=[_8b,_8f,_8c].join(".");var _93=dojo._xdBundleMap[_92];if(_93){for(var _94 in _93){_91[_94]=_8e;}}};dojo._xdInitExtraLocales=function(){var _95=djConfig.extraLocale;if(_95){if(!_95 instanceof Array){_95=[_95];}dojo._xdReqLoc=dojo.xdRequireLocalization;dojo.xdRequireLocalization=function(m,b,_98,_99){dojo._xdReqLoc(m,b,_98,_99);if(_98){return;}for(var i=0;i<_95.length;i++){dojo._xdReqLoc(m,b,_95[i],_99);}};}};dojo._xdBundleMap={};dojo.xdRequireLocalization=function(_9b,_9c,_9d,_9e){if(dojo._xdInitExtraLocales){dojo._xdInitExtraLocales();dojo._xdInitExtraLocales=null;dojo.xdRequireLocalization.apply(dojo,arguments);return;}var _9f=_9e.split(",");var _a0=dojo.i18n.normalizeLocale(_9d);var _a1="";for(var i=0;i<_9f.length;i++){if(_a0.indexOf(_9f[i])==0){if(_9f[i].length>_a1.length){_a1=_9f[i];}}}var _a3=_a1.replace("-","_");var _a4=dojo.getObject([_9b,"nls",_9c].join("."));if(_a4&&_a4[_a3]){bundle[_a0.replace("-","_")]=_a4[_a3];}else{var _a5=[_9b,(_a3||"root"),_9c].join(".");var _a6=dojo._xdBundleMap[_a5];if(!_a6){_a6=dojo._xdBundleMap[_a5]={};}_a6[_a0.replace("-","_")]=true;dojo.require(_9b+".nls"+(_a1?"."+_a1:"")+"."+_9c);}};dojo._xdRealRequireLocalization=dojo.requireLocalization;dojo.requireLocalization=function(_a7,_a8,_a9,_aa){var _ab=this.moduleUrl(_a7).toString();if(this._xdIsXDomainPath(_ab)){return dojo.xdRequireLocalization.apply(dojo,arguments);}else{return dojo._xdRealRequireLocalization.apply(dojo,arguments);}};dojo._xdUnpackDependency=function(dep){var _ad=null;var _ae=null;switch(dep[0]){case "requireIf":case "requireAfterIf":if(dep[1]===true){_ad=[{name:dep[2],content:null}];}break;case "platformRequire":var _af=dep[1];var _b0=_af["common"]||[];var _ad=(_af[dojo.hostenv.name_])?_b0.concat(_af[dojo.hostenv.name_]||[]):_b0.concat(_af["default"]||[]);if(_ad){for(var i=0;i<_ad.length;i++){if(_ad[i] instanceof Array){_ad[i]={name:_ad[i][0],content:null};}else{_ad[i]={name:_ad[i],content:null};}}}break;case "require":_ad=[{name:dep[1],content:null}];break;case "i18n._preloadLocalizations":dojo.i18n._preloadLocalizations.apply(dojo.i18n._preloadLocalizations,dep.slice(1));break;}if(dep[0]=="requireAfterIf"||dep[0]=="requireIf"){_ae=_ad;_ad=null;}return {requires:_ad,requiresAfter:_ae};};dojo._xdWalkReqs=function(){var _b2=null;var req;for(var i=0;i<this._xdOrderedReqs.length;i++){req=this._xdOrderedReqs[i];if(this._xdDepMap[req]){_b2=[req];_b2[req]=true;this._xdEvalReqs(_b2);}}};dojo._xdEvalReqs=function(_b5){while(_b5.length>0){var req=_b5[_b5.length-1];var res=this._xdDepMap[req];if(res){var _b8=res.requires;if(_b8&&_b8.length>0){var _b9;for(var i=0;i<_b8.length;i++){_b9=_b8[i].name;if(_b9&&!_b5[_b9]){_b5.push(_b9);_b5[_b9]=true;this._xdEvalReqs(_b5);}}}var _bb=this._xdContents[res.contentIndex];if(!_bb.isDefined){var _bc=_bb.content;_bc["resourceName"]=_bb["resourceName"];_bc["resourcePath"]=_bb["resourcePath"];this._xdDefList.push(_bc);_bb.isDefined=true;}this._xdDepMap[req]=null;var _b8=res.requiresAfter;if(_b8&&_b8.length>0){var _b9;for(var i=0;i<_b8.length;i++){_b9=_b8[i].name;if(_b9&&!_b5[_b9]){_b5.push(_b9);_b5[_b9]=true;this._xdEvalReqs(_b5);}}}}_b5.pop();}};dojo._xdClearInterval=function(){clearInterval(this._xdTimer);this._xdTimer=0;};dojo._xdWatchInFlight=function(){var _bd="";var _be=(djConfig.xdWaitSeconds||15)*1000;var _bf=(this._xdStartTime+_be)<(new Date()).getTime();for(var _c0 in this._xdInFlight){if(this._xdInFlight[_c0]===true){if(_bf){_bd+=_c0+" ";}else{return;}}}this._xdClearInterval();if(_bf){throw "Could not load cross-domain resources: "+_bd;}this._xdWalkReqs();var _c1=this._xdDefList.length;for(var i=0;i<_c1;i++){var _c3=dojo._xdDefList[i];if(djConfig["debugAtAllCosts"]&&_c3["resourceName"]){if(!this["_xdDebugQueue"]){this._xdDebugQueue=[];}this._xdDebugQueue.push({resourceName:_c3.resourceName,resourcePath:_c3.resourcePath});}else{_c3(dojo);}}for(var i=0;i<this._xdContents.length;i++){var _c4=this._xdContents[i];if(_c4.content&&!_c4.isDefined){_c4.content(dojo);}}this._xdReset();if(this["_xdDebugQueue"]&&this._xdDebugQueue.length>0){this._xdDebugFileLoaded();}else{this._xdNotifyLoaded();}};dojo._xdNotifyLoaded=function(){this._inFlightCount=0;if(this._initFired&&!this._loadNotifying){this._callLoaded();}};if(typeof window!="undefined"){dojo.isBrowser=true;dojo._name="browser";(function(){var d=dojo;if(document&&document.getElementsByTagName){var _c6=document.getElementsByTagName("script");var _c7=/dojo(\.xd)?\.js([\?\.]|$)/i;for(var i=0;i<_c6.length;i++){var src=_c6[i].getAttribute("src");if(!src){continue;}var m=src.match(_c7);if(m){if(!djConfig["baseUrl"]){djConfig["baseUrl"]=src.substring(0,m.index);}var cfg=_c6[i].getAttribute("djConfig");if(cfg){var _cc=eval("({ "+cfg+" })");for(var x in _cc){djConfig[x]=_cc[x];}}break;}}}d.baseUrl=djConfig["baseUrl"];var n=navigator;var dua=n.userAgent;var dav=n.appVersion;var tv=parseFloat(dav);d.isOpera=(dua.indexOf("Opera")>=0)?tv:0;d.isKhtml=(dav.indexOf("Konqueror")>=0)||(dav.indexOf("Safari")>=0)?tv:0;if(dav.indexOf("Safari")>=0){d.isSafari=parseFloat(dav.split("Version/")[1])||2;}var _d2=dua.indexOf("Gecko");d.isMozilla=d.isMoz=((_d2>=0)&&(!d.isKhtml))?tv:0;d.isFF=0;d.isIE=0;try{if(d.isMoz){d.isFF=parseFloat(dua.split("Firefox/")[1].split(" ")[0]);}if((document.all)&&(!d.isOpera)){d.isIE=parseFloat(dav.split("MSIE ")[1].split(";")[0]);}}catch(e){}if(dojo.isIE&&(window.location.protocol==="file:")){djConfig.ieForceActiveXXhr=true;}var cm=document["compatMode"];d.isQuirks=(cm=="BackCompat")||(cm=="QuirksMode")||(d.isIE<6);d.locale=djConfig.locale||(d.isIE?n.userLanguage:n.language).toLowerCase();d._println=console.debug;d._XMLHTTP_PROGIDS=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];d._xhrObj=function(){var _d4=null;var _d5=null;if(!dojo.isIE||!djConfig.ieForceActiveXXhr){try{_d4=new XMLHttpRequest();}catch(e){}}if(!_d4){for(var i=0;i<3;++i){var _d7=dojo._XMLHTTP_PROGIDS[i];try{_d4=new ActiveXObject(_d7);}catch(e){_d5=e;}if(_d4){dojo._XMLHTTP_PROGIDS=[_d7];break;}}}if(!_d4){throw new Error("XMLHTTP not available: "+_d5);}return _d4;};d._isDocumentOk=function(_d8){var _d9=_d8.status||0;return ((_d9>=200)&&(_d9<300))||(_d9==304)||(_d9==1223)||(!_d9&&(location.protocol=="file:"||location.protocol=="chrome:"));};var _da=window.location+"";var _db=document.getElementsByTagName("base");var _dc=(_db&&_db.length>0);d._getText=function(uri,_de){var _df=this._xhrObj();if(!_dc&&dojo._Url){uri=(new dojo._Url(_da,uri)).toString();}_df.open("GET",uri,false);try{_df.send(null);if(!d._isDocumentOk(_df)){var err=Error("Unable to load "+uri+" status:"+_df.status);err.status=_df.status;err.responseText=_df.responseText;throw err;}}catch(e){if(_de){return null;}throw e;}return _df.responseText;};})();dojo._initFired=false;dojo._loadInit=function(e){dojo._initFired=true;var _e2=(e&&e.type)?e.type.toLowerCase():"load";if(arguments.callee.initialized||(_e2!="domcontentloaded"&&_e2!="load")){return;}arguments.callee.initialized=true;if(typeof dojo["_khtmlTimer"]!="undefined"){clearInterval(dojo._khtmlTimer);delete dojo._khtmlTimer;}if(dojo._inFlightCount==0){dojo._modulesLoaded();}};if(document.addEventListener){if(dojo.isOpera||(dojo.isMoz&&(djConfig["enableMozDomContentLoaded"]===true))){document.addEventListener("DOMContentLoaded",dojo._loadInit,null);}window.addEventListener("load",dojo._loadInit,null);}if(/(WebKit|khtml)/i.test(navigator.userAgent)){dojo._khtmlTimer=setInterval(function(){if(/loaded|complete/.test(document.readyState)){dojo._loadInit();}},10);}(function(){var _w=window;var _e4=function(_e5,fp){var _e7=_w[_e5]||function(){};_w[_e5]=function(){fp.apply(_w,arguments);_e7.apply(_w,arguments);};};if(dojo.isIE){document.write("<scr"+"ipt defer src=\"//:\" "+"onreadystatechange=\"if(this.readyState=='complete'){dojo._loadInit();}\">"+"</scr"+"ipt>");var _e8=true;_e4("onbeforeunload",function(){_w.setTimeout(function(){_e8=false;},0);});_e4("onunload",function(){if(_e8){dojo.unloaded();}});try{document.namespaces.add("v","urn:schemas-microsoft-com:vml");document.createStyleSheet().addRule("v\\:*","behavior:url(#default#VML)");}catch(e){}}else{_e4("onbeforeunload",function(){dojo.unloaded();});}})();}if(djConfig.isDebug){dojo.require("dojo._firebug.firebug");}if(djConfig.debugAtAllCosts){djConfig.useXDomain=true;dojo.require("dojo._base._loader.loader_xd");dojo.require("dojo._base._loader.loader_debug");}}if(!dojo._hasResource["dojo._base.lang"]){dojo._hasResource["dojo._base.lang"]=true;dojo.provide("dojo._base.lang");dojo.isString=function(it){return typeof it=="string"||it instanceof String;};dojo.isArray=function(it){return it&&it instanceof Array||typeof it=="array";};dojo.isFunction=(function(){var _eb=function(it){return typeof it=="function"||it instanceof Function;};return dojo.isSafari?function(it){if(typeof it=="function"&&it=="[object NodeList]"){return false;}return _eb(it);}:_eb;})();dojo.isObject=function(it){return it!==undefined&&(it===null||typeof it=="object"||dojo.isArray(it)||dojo.isFunction(it));};dojo.isArrayLike=function(it){var d=dojo;return it&&it!==undefined&&!d.isString(it)&&!d.isFunction(it)&&!(it.tagName&&it.tagName.toLowerCase()=="form")&&(d.isArray(it)||isFinite(it.length));};dojo.isAlien=function(it){return it&&!dojo.isFunction(it)&&/\{\s*\[native code\]\s*\}/.test(String(it));};dojo.extend=function(_f2,_f3){for(var i=1,l=arguments.length;i<l;i++){dojo._mixin(_f2.prototype,arguments[i]);}return _f2;};dojo._hitchArgs=function(_f6,_f7){var pre=dojo._toArray(arguments,2);var _f9=dojo.isString(_f7);return function(){var _fa=dojo._toArray(arguments);var f=_f9?(_f6||dojo.global)[_f7]:_f7;return f&&f.apply(_f6||this,pre.concat(_fa));};};dojo.hitch=function(_fc,_fd){if(arguments.length>2){return dojo._hitchArgs.apply(dojo,arguments);}if(!_fd){_fd=_fc;_fc=null;}if(dojo.isString(_fd)){_fc=_fc||dojo.global;if(!_fc[_fd]){throw (["dojo.hitch: scope[\"",_fd,"\"] is null (scope=\"",_fc,"\")"].join(""));}return function(){return _fc[_fd].apply(_fc,arguments||[]);};}return !_fc?_fd:function(){return _fd.apply(_fc,arguments||[]);};};dojo.delegate=dojo._delegate=function(obj,_ff){function TMP(){};TMP.prototype=obj;var tmp=new TMP();if(_ff){dojo.mixin(tmp,_ff);}return tmp;};dojo.partial=function(_101){var arr=[null];return dojo.hitch.apply(dojo,arr.concat(dojo._toArray(arguments)));};dojo._toArray=function(obj,_104,_105){var arr=_105||[];for(var x=_104||0;x<obj.length;x++){arr.push(obj[x]);}return arr;};dojo.clone=function(o){if(!o){return o;}if(dojo.isArray(o)){var r=[];for(var i=0;i<o.length;++i){r.push(dojo.clone(o[i]));}return r;}if(!dojo.isObject(o)){return o;}if(o.nodeType&&o.cloneNode){return o.cloneNode(true);}if(o instanceof Date){return new Date(o.getTime());}var r=new o.constructor();for(var i in o){if(!(i in r)||r[i]!=o[i]){r[i]=dojo.clone(o[i]);}}return r;};dojo.trim=function(str){return str.replace(/^\s\s*/,"").replace(/\s\s*$/,"");};}if(!dojo._hasResource["dojo._base.declare"]){dojo._hasResource["dojo._base.declare"]=true;dojo.provide("dojo._base.declare");dojo.declare=function(_10c,_10d,_10e){if(dojo.isFunction(_10e)||(arguments.length>3)){dojo.deprecated("dojo.declare: for class '"+_10c+"' pass initializer function as 'constructor' property instead of as a separate argument.","","1.0");var c=_10e;_10e=arguments[3]||{};_10e.constructor=c;}var dd=arguments.callee,_111=null;if(dojo.isArray(_10d)){_111=_10d;_10d=_111.shift();}if(_111){for(var i=0,m;i<_111.length;i++){m=_111[i];if(!m){throw ("Mixin #"+i+" to declaration of "+_10c+" is null. It's likely a required module is not loaded.");}_10d=dd._delegate(_10d,m);}}var init=(_10e||0).constructor,ctor=dd._delegate(_10d),fn;for(var i in _10e){if(dojo.isFunction(fn=_10e[i])&&(!0[i])){fn.nom=i;}}dojo.extend(ctor,{declaredClass:_10c,_constructor:init,preamble:null},_10e||0);ctor.prototype.constructor=ctor;return dojo.setObject(_10c,ctor);};dojo.mixin(dojo.declare,{_delegate:function(base,_118){var bp=(base||0).prototype,mp=(_118||0).prototype;var ctor=dojo.declare._makeCtor();dojo.mixin(ctor,{superclass:bp,mixin:mp,extend:dojo.declare._extend});if(base){ctor.prototype=dojo._delegate(bp);}dojo.extend(ctor,dojo.declare._core,mp||0,{_constructor:null,preamble:null});ctor.prototype.constructor=ctor;ctor.prototype.declaredClass=(bp||0).declaredClass+"_"+(mp||0).declaredClass;return ctor;},_extend:function(_11c){for(var i in _11c){if(dojo.isFunction(fn=_11c[i])&&(!0[i])){fn.nom=i;}}dojo.extend(this,_11c);},_makeCtor:function(){return function(){this._construct(arguments);};},_core:{_construct:function(args){var c=args.callee,s=c.superclass,ct=s&&s.constructor,m=c.mixin,mct=m&&m.constructor,a=args,ii,fn;if(a[0]){if((fn=a[0]["preamble"])){a=fn.apply(this,a)||a;}}if(fn=c.prototype.preamble){a=fn.apply(this,a)||a;}if(ct&&ct.apply){ct.apply(this,a);}if(mct&&mct.apply){mct.apply(this,a);}if(ii=c.prototype._constructor){ii.apply(this,args);}if(this.constructor.prototype==c.prototype&&(ct=this.postscript)){ct.apply(this,args);}},_findMixin:function(_127){var c=this.constructor,p,m;while(c){p=c.superclass;m=c.mixin;if(m==_127||(m instanceof _127.constructor)){return p;}if(m&&(m=m._findMixin(_127))){return m;}c=p&&p.constructor;}},_findMethod:function(name,_12c,_12d,has){var p=_12d,c,m,f;do{c=p.constructor;m=c.mixin;if(m&&(m=this._findMethod(name,_12c,m,has))){return m;}if((f=p[name])&&(has==(f==_12c))){return p;}p=c.superclass;}while(p);return !has&&(p=this._findMixin(_12d))&&this._findMethod(name,_12c,p,has);},inherited:function(name,args,_135){var a=arguments;if(!dojo.isString(a[0])){_135=args;args=name;name=args.callee.nom;}var c=args.callee,p=this.constructor.prototype,a=_135||args,fn,mp;if(this[name]!=c||p[name]==c){mp=this._findMethod(name,c,p,true);if(!mp){throw (this.declaredClass+": name argument (\""+name+"\") to inherited must match callee (declare.js)");}p=this._findMethod(name,c,mp,false);}fn=p&&p[name];if(!fn){console.debug(mp.declaredClass+": no inherited \""+name+"\" was found (declare.js)");return;}return fn.apply(this,a);}}});}if(!dojo._hasResource["dojo._base.connect"]){dojo._hasResource["dojo._base.connect"]=true;dojo.provide("dojo._base.connect");dojo._listener={getDispatcher:function(){return function(){var ap=Array.prototype,c=arguments.callee,ls=c._listeners,t=c.target;var r=t&&t.apply(this,arguments);for(var i in ls){if(!(i in ap)){ls[i].apply(this,arguments);}}return r;};},add:function(_141,_142,_143){_141=_141||dojo.global;var f=_141[_142];if(!f||!f._listeners){var d=dojo._listener.getDispatcher();d.target=f;d._listeners=[];f=_141[_142]=d;}return f._listeners.push(_143);},remove:function(_146,_147,_148){var f=(_146||dojo.global)[_147];if(f&&f._listeners&&_148--){delete f._listeners[_148];}}};dojo.connect=function(obj,_14b,_14c,_14d,_14e){var a=arguments,args=[],i=0;args.push(dojo.isString(a[0])?null:a[i++],a[i++]);var a1=a[i+1];args.push(dojo.isString(a1)||dojo.isFunction(a1)?a[i++]:null,a[i++]);for(var l=a.length;i<l;i++){args.push(a[i]);}return dojo._connect.apply(this,args);};dojo._connect=function(obj,_155,_156,_157){var l=dojo._listener,h=l.add(obj,_155,dojo.hitch(_156,_157));return [obj,_155,h,l];};dojo.disconnect=function(_15a){if(_15a&&_15a[0]!==undefined){dojo._disconnect.apply(this,_15a);delete _15a[0];}};dojo._disconnect=function(obj,_15c,_15d,_15e){_15e.remove(obj,_15c,_15d);};dojo._topics={};dojo.subscribe=function(_15f,_160,_161){return [_15f,dojo._listener.add(dojo._topics,_15f,dojo.hitch(_160,_161))];};dojo.unsubscribe=function(_162){if(_162){dojo._listener.remove(dojo._topics,_162[0],_162[1]);}};dojo.publish=function(_163,args){var f=dojo._topics[_163];if(f){f.apply(this,args||[]);}};dojo.connectPublisher=function(_166,obj,_168){var pf=function(){dojo.publish(_166,arguments);};return (_168)?dojo.connect(obj,_168,pf):dojo.connect(obj,pf);};}if(!dojo._hasResource["dojo._base.Deferred"]){dojo._hasResource["dojo._base.Deferred"]=true;dojo.provide("dojo._base.Deferred");dojo.Deferred=function(_16a){this.chain=[];this.id=this._nextId();this.fired=-1;this.paused=0;this.results=[null,null];this.canceller=_16a;this.silentlyCancelled=false;};dojo.extend(dojo.Deferred,{_nextId:(function(){var n=1;return function(){return n++;};})(),cancel:function(){var err;if(this.fired==-1){if(this.canceller){err=this.canceller(this);}else{this.silentlyCancelled=true;}if(this.fired==-1){if(!(err instanceof Error)){var res=err;err=new Error("Deferred Cancelled");err.dojoType="cancel";err.cancelResult=res;}this.errback(err);}}else{if((this.fired==0)&&(this.results[0] instanceof dojo.Deferred)){this.results[0].cancel();}}},_resback:function(res){this.fired=((res instanceof Error)?1:0);this.results[this.fired]=res;this._fire();},_check:function(){if(this.fired!=-1){if(!this.silentlyCancelled){throw new Error("already called!");}this.silentlyCancelled=false;return;}},callback:function(res){this._check();this._resback(res);},errback:function(res){this._check();if(!(res instanceof Error)){res=new Error(res);}this._resback(res);},addBoth:function(cb,cbfn){var _173=dojo.hitch(cb,cbfn);if(arguments.length>2){_173=dojo.partial(_173,arguments,2);}return this.addCallbacks(_173,_173);},addCallback:function(cb,cbfn){var _176=dojo.hitch(cb,cbfn);if(arguments.length>2){_176=dojo.partial(_176,arguments,2);}return this.addCallbacks(_176,null);},addErrback:function(cb,cbfn){var _179=dojo.hitch(cb,cbfn);if(arguments.length>2){_179=dojo.partial(_179,arguments,2);}return this.addCallbacks(null,_179);},addCallbacks:function(cb,eb){this.chain.push([cb,eb]);if(this.fired>=0){this._fire();}return this;},_fire:function(){var _17c=this.chain;var _17d=this.fired;var res=this.results[_17d];var self=this;var cb=null;while((_17c.length>0)&&(this.paused==0)){var f=_17c.shift()[_17d];if(!f){continue;}try{res=f(res);_17d=((res instanceof Error)?1:0);if(res instanceof dojo.Deferred){cb=function(res){self._resback(res);self.paused--;if((self.paused==0)&&(self.fired>=0)){self._fire();}};this.paused++;}}catch(err){console.debug(err);_17d=1;res=err;}}this.fired=_17d;this.results[_17d]=res;if((cb)&&(this.paused)){res.addBoth(cb);}}});}if(!dojo._hasResource["dojo._base.json"]){dojo._hasResource["dojo._base.json"]=true;dojo.provide("dojo._base.json");dojo.fromJson=function(json){try{return eval("("+json+")");}catch(e){console.debug(e);return json;}};dojo._escapeString=function(str){return ("\""+str.replace(/(["\\])/g,"\\$1")+"\"").replace(/[\f]/g,"\\f").replace(/[\b]/g,"\\b").replace(/[\n]/g,"\\n").replace(/[\t]/g,"\\t").replace(/[\r]/g,"\\r");};dojo.toJsonIndentStr="\t";dojo.toJson=function(it,_186,_187){_187=_187||"";var _188=_186?_187+dojo.toJsonIndentStr:"";var _189=_186?"\n":"";var _18a=typeof it;if(_18a=="undefined"){return "undefined";}if(_18a=="number"||_18a=="boolean"){return it+"";}if(it===null){return "null";}if(dojo.isString(it)){return dojo._escapeString(it);}if(it.nodeType&&it.cloneNode){return "";}var _18b=arguments.callee;var _18c;if(typeof it.__json__=="function"){_18c=it.__json__();if(it!==_18c){return _18b(_18c,_186,_188);}}if(typeof it.json=="function"){_18c=it.json();if(it!==_18c){return _18b(_18c,_186,_188);}}var sep=_186?" ":"";if(dojo.isArray(it)){var res=dojo.map(it,function(obj){var val=_18b(obj,_186,_188);if(typeof val!="string"){val="undefined";}return _189+_188+val;});return "["+res.join(","+sep)+_189+_187+"]";}if(_18a=="function"){return null;}var _191=[];for(var key in it){var _193;if(typeof key=="number"){_193="\""+key+"\"";}else{if(typeof key=="string"){_193=dojo._escapeString(key);}else{continue;}}val=_18b(it[key],_186,_188);if(typeof val!="string"){continue;}_191.push(_189+_188+_193+":"+sep+val);}return "{"+_191.join(","+sep)+_189+_187+"}";};}if(!dojo._hasResource["dojo._base.array"]){dojo._hasResource["dojo._base.array"]=true;dojo.provide("dojo._base.array");(function(){var _194=function(arr,obj,cb){return [(dojo.isString(arr)?arr.split(""):arr),(obj||dojo.global),(dojo.isString(cb)?(new Function("item","index","array",cb)):cb)];};dojo.mixin(dojo,{indexOf:function(_198,_199,_19a,_19b){var i=0,step=1,end=_198.length;if(_19b){i=end-1;step=end=-1;}for(i=_19a||i;i!=end;i+=step){if(_198[i]==_199){return i;}}return -1;},lastIndexOf:function(_19f,_1a0,_1a1){return dojo.indexOf(_19f,_1a0,_1a1,true);},forEach:function(arr,_1a3,obj){if(!arr||!arr.length){return;}var _p=_194(arr,obj,_1a3);arr=_p[0];for(var i=0,l=_p[0].length;i<l;i++){_p[2].call(_p[1],arr[i],i,arr);}},_everyOrSome:function(_1a8,arr,_1aa,obj){var _p=_194(arr,obj,_1aa);arr=_p[0];for(var i=0,l=arr.length;i<l;i++){var _1af=!!_p[2].call(_p[1],arr[i],i,arr);if(_1a8^_1af){return _1af;}}return _1a8;},every:function(arr,_1b1,_1b2){return this._everyOrSome(true,arr,_1b1,_1b2);},some:function(arr,_1b4,_1b5){return this._everyOrSome(false,arr,_1b4,_1b5);},map:function(arr,func,obj){var _p=_194(arr,obj,func);arr=_p[0];var _1ba=((arguments[3])?(new arguments[3]()):[]);for(var i=0;i<arr.length;++i){_1ba.push(_p[2].call(_p[1],arr[i],i,arr));}return _1ba;},filter:function(arr,_1bd,obj){var _p=_194(arr,obj,_1bd);arr=_p[0];var _1c0=[];for(var i=0;i<arr.length;i++){if(_p[2].call(_p[1],arr[i],i,arr)){_1c0.push(arr[i]);}}return _1c0;}});})();}if(!dojo._hasResource["dojo._base.Color"]){dojo._hasResource["dojo._base.Color"]=true;dojo.provide("dojo._base.Color");dojo.Color=function(_1c2){if(_1c2){this.setColor(_1c2);}};dojo.Color.named={black:[0,0,0],silver:[192,192,192],gray:[128,128,128],white:[255,255,255],maroon:[128,0,0],red:[255,0,0],purple:[128,0,128],fuchsia:[255,0,255],green:[0,128,0],lime:[0,255,0],olive:[128,128,0],yellow:[255,255,0],navy:[0,0,128],blue:[0,0,255],teal:[0,128,128],aqua:[0,255,255]};dojo.extend(dojo.Color,{r:255,g:255,b:255,a:1,_set:function(r,g,b,a){var t=this;t.r=r;t.g=g;t.b=b;t.a=a;},setColor:function(_1c8){var d=dojo;if(d.isString(_1c8)){d.colorFromString(_1c8,this);}else{if(d.isArray(_1c8)){d.colorFromArray(_1c8,this);}else{this._set(_1c8.r,_1c8.g,_1c8.b,_1c8.a);if(!(_1c8 instanceof d.Color)){this.sanitize();}}}return this;},sanitize:function(){return this;},toRgb:function(){var t=this;return [t.r,t.g,t.b];},toRgba:function(){var t=this;return [t.r,t.g,t.b,t.a];},toHex:function(){var arr=dojo.map(["r","g","b"],function(x){var s=this[x].toString(16);return s.length<2?"0"+s:s;},this);return "#"+arr.join("");},toCss:function(_1cf){var t=this,rgb=t.r+", "+t.g+", "+t.b;return (_1cf?"rgba("+rgb+", "+t.a:"rgb("+rgb)+")";},toString:function(){return this.toCss(true);}});dojo.blendColors=function(_1d2,end,_1d4,obj){var d=dojo,t=obj||new dojo.Color();d.forEach(["r","g","b","a"],function(x){t[x]=_1d2[x]+(end[x]-_1d2[x])*_1d4;if(x!="a"){t[x]=Math.round(t[x]);}});return t.sanitize();};dojo.colorFromRgb=function(_1d9,obj){var m=_1d9.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);return m&&dojo.colorFromArray(m[1].split(/\s*,\s*/),obj);};dojo.colorFromHex=function(_1dc,obj){var d=dojo,t=obj||new d.Color(),bits=(_1dc.length==4)?4:8,mask=(1<<bits)-1;_1dc=Number("0x"+_1dc.substr(1));if(isNaN(_1dc)){return null;}d.forEach(["b","g","r"],function(x){var c=_1dc&mask;_1dc>>=bits;t[x]=bits==4?17*c:c;});t.a=1;return t;};dojo.colorFromArray=function(a,obj){var t=obj||new dojo.Color();t._set(Number(a[0]),Number(a[1]),Number(a[2]),Number(a[3]));if(isNaN(t.a)){t.a=1;}return t.sanitize();};dojo.colorFromString=function(str,obj){var a=dojo.Color.named[str];return a&&dojo.colorFromArray(a,obj)||dojo.colorFromRgb(str,obj)||dojo.colorFromHex(str,obj);};}if(!dojo._hasResource["dojo._base"]){dojo._hasResource["dojo._base"]=true;dojo.provide("dojo._base");(function(){if(djConfig.require){for(var x=0;x<djConfig.require.length;x++){dojo["require"](djConfig.require[x]);}}})();}if(!dojo._hasResource["dojo._base.window"]){dojo._hasResource["dojo._base.window"]=true;dojo.provide("dojo._base.window");dojo._gearsObject=function(){var _1eb;var _1ec;var _1ed=dojo.getObject("google.gears");if(_1ed){return _1ed;}if(typeof GearsFactory!="undefined"){_1eb=new GearsFactory();}else{if(dojo.isIE){try{_1eb=new ActiveXObject("Gears.Factory");}catch(e){}}else{if(navigator.mimeTypes["application/x-googlegears"]){_1eb=document.createElement("object");_1eb.setAttribute("type","application/x-googlegears");_1eb.setAttribute("width",0);_1eb.setAttribute("height",0);_1eb.style.display="none";document.documentElement.appendChild(_1eb);}}}if(!_1eb){return null;}dojo.setObject("google.gears.factory",_1eb);return dojo.getObject("google.gears");};dojo.isGears=(!!dojo._gearsObject())||0;dojo.doc=window["document"]||null;dojo.body=function(){return dojo.doc.body||dojo.doc.getElementsByTagName("body")[0];};dojo.setContext=function(_1ee,_1ef){dojo.global=_1ee;dojo.doc=_1ef;};dojo._fireCallback=function(_1f0,_1f1,_1f2){if(_1f1&&dojo.isString(_1f0)){_1f0=_1f1[_1f0];}return (_1f1?_1f0.apply(_1f1,_1f2||[]):_1f0());};dojo.withGlobal=function(_1f3,_1f4,_1f5,_1f6){var rval;var _1f8=dojo.global;var _1f9=dojo.doc;try{dojo.setContext(_1f3,_1f3.document);rval=dojo._fireCallback(_1f4,_1f5,_1f6);}finally{dojo.setContext(_1f8,_1f9);}return rval;};dojo.withDoc=function(_1fa,_1fb,_1fc,_1fd){var rval;var _1ff=dojo.doc;try{dojo.doc=_1fa;rval=dojo._fireCallback(_1fb,_1fc,_1fd);}finally{dojo.doc=_1ff;}return rval;};(function(){var mp=djConfig["modulePaths"];if(mp){for(var _201 in mp){dojo.registerModulePath(_201,mp[_201]);}}})();}if(!dojo._hasResource["dojo._base.event"]){dojo._hasResource["dojo._base.event"]=true;dojo.provide("dojo._base.event");(function(){var del=(dojo._event_listener={add:function(node,name,fp){if(!node){return;}name=del._normalizeEventName(name);fp=del._fixCallback(name,fp);var _206=name;if(!dojo.isIE&&(name=="mouseenter")||(name=="mouseleave")){var _206=name;var ofp=fp;name=(name=="mouseenter")?"mouseover":"mouseout";fp=function(e){var id=dojo.isDescendant(e.relatedTarget,node);if(id==false){return ofp.call(this,e);}};}node.addEventListener(name,fp,false);return fp;},remove:function(node,_20b,_20c){node&&node.removeEventListener(del._normalizeEventName(_20b),_20c,false);},_normalizeEventName:function(name){return name.slice(0,2)=="on"?name.slice(2):name;},_fixCallback:function(name,fp){return name!="keypress"?fp:function(e){return fp.call(this,del._fixEvent(e,this));};},_fixEvent:function(evt,_212){switch(evt.type){case "keypress":del._setKeyChar(evt);break;}return evt;},_setKeyChar:function(evt){evt.keyChar=(evt.charCode?String.fromCharCode(evt.charCode):"");}});dojo.fixEvent=function(evt,_215){return del._fixEvent(evt,_215);};dojo.stopEvent=function(evt){evt.preventDefault();evt.stopPropagation();};var _217=dojo._listener;dojo._connect=function(obj,_219,_21a,_21b,_21c){var _21d=obj&&(obj.nodeType||obj.attachEvent||obj.addEventListener);var lid=!_21d?0:(!_21c?1:2),l=[dojo._listener,del,_217][lid];var h=l.add(obj,_219,dojo.hitch(_21a,_21b));return [obj,_219,h,lid];};dojo._disconnect=function(obj,_222,_223,_224){([dojo._listener,del,_217][_224]).remove(obj,_222,_223);};dojo.keys={BACKSPACE:8,TAB:9,CLEAR:12,ENTER:13,SHIFT:16,CTRL:17,ALT:18,PAUSE:19,CAPS_LOCK:20,ESCAPE:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT_ARROW:37,UP_ARROW:38,RIGHT_ARROW:39,DOWN_ARROW:40,INSERT:45,DELETE:46,HELP:47,LEFT_WINDOW:91,RIGHT_WINDOW:92,SELECT:93,NUMPAD_0:96,NUMPAD_1:97,NUMPAD_2:98,NUMPAD_3:99,NUMPAD_4:100,NUMPAD_5:101,NUMPAD_6:102,NUMPAD_7:103,NUMPAD_8:104,NUMPAD_9:105,NUMPAD_MULTIPLY:106,NUMPAD_PLUS:107,NUMPAD_ENTER:108,NUMPAD_MINUS:109,NUMPAD_PERIOD:110,NUMPAD_DIVIDE:111,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,F13:124,F14:125,F15:126,NUM_LOCK:144,SCROLL_LOCK:145};if(dojo.isIE){var _225=function(e,code){try{return (e.keyCode=code);}catch(e){return 0;}};var iel=dojo._listener;if(!djConfig._allow_leaks){_217=iel=dojo._ie_listener={handlers:[],add:function(_229,_22a,_22b){_229=_229||dojo.global;var f=_229[_22a];if(!f||!f._listeners){var d=dojo._getIeDispatcher();d.target=f&&(ieh.push(f)-1);d._listeners=[];f=_229[_22a]=d;}return f._listeners.push(ieh.push(_22b)-1);},remove:function(_22f,_230,_231){var f=(_22f||dojo.global)[_230],l=f&&f._listeners;if(f&&l&&_231--){delete ieh[l[_231]];delete l[_231];}}};var ieh=iel.handlers;}dojo.mixin(del,{add:function(node,_235,fp){if(!node){return;}_235=del._normalizeEventName(_235);if(_235=="onkeypress"){var kd=node.onkeydown;if(!kd||!kd._listeners||!kd._stealthKeydown){del.add(node,"onkeydown",del._stealthKeyDown);node.onkeydown._stealthKeydown=true;}}return iel.add(node,_235,del._fixCallback(fp));},remove:function(node,_239,_23a){iel.remove(node,del._normalizeEventName(_239),_23a);},_normalizeEventName:function(_23b){return (_23b.slice(0,2)!="on"?"on"+_23b:_23b);},_nop:function(){},_fixEvent:function(evt,_23d){if(!evt){var w=(_23d)&&((_23d.ownerDocument||_23d.document||_23d).parentWindow)||window;evt=w.event;}if(!evt){return (evt);}evt.target=evt.srcElement;evt.currentTarget=(_23d||evt.srcElement);evt.layerX=evt.offsetX;evt.layerY=evt.offsetY;var se=evt.srcElement,doc=(se&&se.ownerDocument)||document;var _241=((dojo.isIE<6)||(doc["compatMode"]=="BackCompat"))?doc.body:doc.documentElement;var _242=dojo._getIeDocumentElementOffset();evt.pageX=evt.clientX+dojo._fixIeBiDiScrollLeft(_241.scrollLeft||0)-_242.x;evt.pageY=evt.clientY+(_241.scrollTop||0)-_242.y;if(evt.type=="mouseover"){evt.relatedTarget=evt.fromElement;}if(evt.type=="mouseout"){evt.relatedTarget=evt.toElement;}evt.stopPropagation=del._stopPropagation;evt.preventDefault=del._preventDefault;return del._fixKeys(evt);},_fixKeys:function(evt){switch(evt.type){case "keypress":var c=("charCode" in evt?evt.charCode:evt.keyCode);if(c==10){c=0;evt.keyCode=13;}else{if(c==13||c==27){c=0;}else{if(c==3){c=99;}}}evt.charCode=c;del._setKeyChar(evt);break;}return evt;},_punctMap:{106:42,111:47,186:59,187:43,188:44,189:45,190:46,191:47,192:96,219:91,220:92,221:93,222:39},_stealthKeyDown:function(evt){var kp=evt.currentTarget.onkeypress;if(!kp||!kp._listeners){return;}var k=evt.keyCode;var _248=(k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);if(_248||evt.ctrlKey){var c=(_248?0:k);if(evt.ctrlKey){if(k==3||k==13){return;}else{if(c>95&&c<106){c-=48;}else{if((!evt.shiftKey)&&(c>=65&&c<=90)){c+=32;}else{c=del._punctMap[c]||c;}}}}var faux=del._synthesizeEvent(evt,{type:"keypress",faux:true,charCode:c});kp.call(evt.currentTarget,faux);evt.cancelBubble=faux.cancelBubble;evt.returnValue=faux.returnValue;_225(evt,faux.keyCode);}},_stopPropagation:function(){this.cancelBubble=true;},_preventDefault:function(){this.bubbledKeyCode=this.keyCode;if(this.ctrlKey){_225(this,0);}this.returnValue=false;}});dojo.stopEvent=function(evt){evt=evt||window.event;del._stopPropagation.call(evt);del._preventDefault.call(evt);};}del._synthesizeEvent=function(evt,_24d){var faux=dojo.mixin({},evt,_24d);del._setKeyChar(faux);faux.preventDefault=function(){evt.preventDefault();};faux.stopPropagation=function(){evt.stopPropagation();};return faux;};if(dojo.isOpera){dojo.mixin(del,{_fixEvent:function(evt,_250){switch(evt.type){case "keypress":var c=evt.which;if(c==3){c=99;}c=((c<41)&&(!evt.shiftKey)?0:c);if((evt.ctrlKey)&&(!evt.shiftKey)&&(c>=65)&&(c<=90)){c+=32;}return del._synthesizeEvent(evt,{charCode:c});}return evt;}});}if(dojo.isSafari){dojo.mixin(del,{_fixEvent:function(evt,_253){switch(evt.type){case "keypress":var c=evt.charCode,s=evt.shiftKey,k=evt.keyCode;k=k||_257[evt.keyIdentifier]||0;if(evt.keyIdentifier=="Enter"){c=0;}else{if((evt.ctrlKey)&&(c>0)&&(c<27)){c+=96;}else{if(c==dojo.keys.SHIFT_TAB){c=dojo.keys.TAB;s=true;}else{c=(c>=32&&c<63232?c:0);}}}return del._synthesizeEvent(evt,{charCode:c,shiftKey:s,keyCode:k});}return evt;}});dojo.mixin(dojo.keys,{SHIFT_TAB:25,UP_ARROW:63232,DOWN_ARROW:63233,LEFT_ARROW:63234,RIGHT_ARROW:63235,F1:63236,F2:63237,F3:63238,F4:63239,F5:63240,F6:63241,F7:63242,F8:63243,F9:63244,F10:63245,F11:63246,F12:63247,PAUSE:63250,DELETE:63272,HOME:63273,END:63275,PAGE_UP:63276,PAGE_DOWN:63277,INSERT:63302,PRINT_SCREEN:63248,SCROLL_LOCK:63249,NUM_LOCK:63289});var dk=dojo.keys,_257={"Up":dk.UP_ARROW,"Down":dk.DOWN_ARROW,"Left":dk.LEFT_ARROW,"Right":dk.RIGHT_ARROW,"PageUp":dk.PAGE_UP,"PageDown":dk.PAGE_DOWN};}})();if(dojo.isIE){dojo._getIeDispatcher=function(){return function(){var ap=Array.prototype,h=dojo._ie_listener.handlers,c=arguments.callee,ls=c._listeners,t=h[c.target];var r=t&&t.apply(this,arguments);for(var i in ls){if(!(i in ap)){h[ls[i]].apply(this,arguments);}}return r;};};dojo._event_listener._fixCallback=function(fp){var f=dojo._event_listener._fixEvent;return function(e){return fp.call(this,f(e,this));};};}}if(!dojo._hasResource["dojo._base.html"]){dojo._hasResource["dojo._base.html"]=true;dojo.provide("dojo._base.html");try{document.execCommand("BackgroundImageCache",false,true);}catch(e){}if(dojo.isIE||dojo.isOpera){dojo.byId=function(id,doc){if(dojo.isString(id)){var _d=doc||dojo.doc;var te=_d.getElementById(id);if(te&&te.attributes.id.value==id){return te;}else{var eles=_d.all[id];if(!eles){return;}if(!eles.length){return eles;}var i=0;while((te=eles[i++])){if(te.attributes.id.value==id){return te;}}}}else{return id;}};}else{dojo.byId=function(id,doc){if(dojo.isString(id)){return (doc||dojo.doc).getElementById(id);}else{return id;}};}(function(){var _26b=null;dojo._destroyElement=function(node){node=dojo.byId(node);try{if(!_26b){_26b=document.createElement("div");}_26b.appendChild(node.parentNode?node.parentNode.removeChild(node):node);_26b.innerHTML="";}catch(e){}};dojo.isDescendant=function(node,_26e){try{node=dojo.byId(node);_26e=dojo.byId(_26e);while(node){if(node===_26e){return true;}node=node.parentNode;}}catch(e){return -1;}return false;};dojo.setSelectable=function(node,_270){node=dojo.byId(node);if(dojo.isMozilla){node.style.MozUserSelect=_270?"":"none";}else{if(dojo.isKhtml){node.style.KhtmlUserSelect=_270?"auto":"none";}else{if(dojo.isIE){node.unselectable=_270?"":"on";dojo.query("*",node).forEach(function(_271){_271.unselectable=_270?"":"on";});}}}};var _272=function(node,ref){ref.parentNode.insertBefore(node,ref);return true;};var _275=function(node,ref){var pn=ref.parentNode;if(ref==pn.lastChild){pn.appendChild(node);}else{return _272(node,ref.nextSibling);}return true;};dojo.place=function(node,_27a,_27b){if(!node||!_27a||_27b===undefined){return false;}node=dojo.byId(node);_27a=dojo.byId(_27a);if(typeof _27b=="number"){var cn=_27a.childNodes;if((_27b==0&&cn.length==0)||cn.length==_27b){_27a.appendChild(node);return true;}if(_27b==0){return _272(node,_27a.firstChild);}return _275(node,cn[_27b-1]);}switch(_27b.toLowerCase()){case "before":return _272(node,_27a);case "after":return _275(node,_27a);case "first":if(_27a.firstChild){return _272(node,_27a.firstChild);}else{_27a.appendChild(node);return true;}break;default:_27a.appendChild(node);return true;}};dojo.boxModel="content-box";if(dojo.isIE){var _dcm=document.compatMode;dojo.boxModel=(_dcm=="BackCompat")||(_dcm=="QuirksMode")||(dojo.isIE<6)?"border-box":"content-box";}var gcs,dv=document.defaultView;if(dojo.isSafari){gcs=function(node){var s=dv.getComputedStyle(node,null);if(!s&&node.style){node.style.display="";s=dv.getComputedStyle(node,null);}return s||{};};}else{if(dojo.isIE){gcs=function(node){return node.currentStyle;};}else{gcs=function(node){return dv.getComputedStyle(node,null);};}}dojo.getComputedStyle=gcs;if(!dojo.isIE){dojo._toPixelValue=function(_284,_285){return parseFloat(_285)||0;};}else{dojo._toPixelValue=function(_286,_287){if(!_287){return 0;}if(_287=="medium"){return 4;}if(_287.slice&&(_287.slice(-2)=="px")){return parseFloat(_287);}with(_286){var _288=style.left;var _289=runtimeStyle.left;runtimeStyle.left=currentStyle.left;try{style.left=_287;_287=style.pixelLeft;}catch(e){_287=0;}style.left=_288;runtimeStyle.left=_289;}return _287;};}dojo._getOpacity=(dojo.isIE?function(node){try{return (node.filters.alpha.opacity/100);}catch(e){return 1;}}:function(node){return dojo.getComputedStyle(node).opacity;});dojo._setOpacity=(dojo.isIE?function(node,_28d){if(_28d==1){node.style.cssText=node.style.cssText.replace(/FILTER:[^;]*;?/i,"");if(node.nodeName.toLowerCase()=="tr"){dojo.query("> td",node).forEach(function(i){i.style.cssText=i.style.cssText.replace(/FILTER:[^;]*;?/i,"");});}}else{var o="Alpha(Opacity="+(_28d*100)+")";node.style.filter=o;}if(node.nodeName.toLowerCase()=="tr"){dojo.query("> td",node).forEach(function(i){i.style.filter=o;});}return _28d;}:function(node,_292){return node.style.opacity=_292;});var _293={width:true,height:true,left:true,top:true};var _294=function(node,type,_297){type=type.toLowerCase();if(_293[type]===true){return dojo._toPixelValue(node,_297);}else{if(_293[type]===false){return _297;}else{if(dojo.isOpera&&type=="cssText"){}if((type.indexOf("margin")>=0)||(type.indexOf("padding")>=0)||(type.indexOf("width")>=0)||(type.indexOf("height")>=0)||(type.indexOf("max")>=0)||(type.indexOf("min")>=0)||(type.indexOf("offset")>=0)){_293[type]=true;return dojo._toPixelValue(node,_297);}else{_293[type]=false;return _297;}}}};dojo.style=function(node,_299,_29a){var n=dojo.byId(node),args=arguments.length,op=(_299=="opacity");if(args==3){return op?dojo._setOpacity(n,_29a):n.style[_299]=_29a;}if(args==2&&op){return dojo._getOpacity(n);}var s=dojo.getComputedStyle(n);return (args==1)?s:_294(n,_299,s[_299]);};dojo._getPadExtents=function(n,_2a0){var s=_2a0||gcs(n),px=dojo._toPixelValue,l=px(n,s.paddingLeft),t=px(n,s.paddingTop);return {l:l,t:t,w:l+px(n,s.paddingRight),h:t+px(n,s.paddingBottom)};};dojo._getBorderExtents=function(n,_2a6){var ne="none",px=dojo._toPixelValue,s=_2a6||gcs(n),bl=(s.borderLeftStyle!=ne?px(n,s.borderLeftWidth):0),bt=(s.borderTopStyle!=ne?px(n,s.borderTopWidth):0);return {l:bl,t:bt,w:bl+(s.borderRightStyle!=ne?px(n,s.borderRightWidth):0),h:bt+(s.borderBottomStyle!=ne?px(n,s.borderBottomWidth):0)};};dojo._getPadBorderExtents=function(n,_2ad){var s=_2ad||gcs(n),p=dojo._getPadExtents(n,s),b=dojo._getBorderExtents(n,s);return {l:p.l+b.l,t:p.t+b.t,w:p.w+b.w,h:p.h+b.h};};dojo._getMarginExtents=function(n,_2b2){var s=_2b2||gcs(n),px=dojo._toPixelValue,l=px(n,s.marginLeft),t=px(n,s.marginTop),r=px(n,s.marginRight),b=px(n,s.marginBottom);if(dojo.isSafari&&(s.position!="absolute")){r=l;}return {l:l,t:t,w:l+r,h:t+b};};dojo._getMarginBox=function(node,_2ba){var s=_2ba||gcs(node),me=dojo._getMarginExtents(node,s);var l=node.offsetLeft-me.l,t=node.offsetTop-me.t;if(dojo.isMoz){var sl=parseFloat(s.left),st=parseFloat(s.top);if(!isNaN(sl)&&!isNaN(st)){l=sl,t=st;}else{var p=node.parentNode;if(p&&p.style){var pcs=gcs(p);if(pcs.overflow!="visible"){var be=dojo._getBorderExtents(p,pcs);l+=be.l,t+=be.t;}}}}else{if(dojo.isOpera){var p=node.parentNode;if(p){var be=dojo._getBorderExtents(p);l-=be.l,t-=be.t;}}}return {l:l,t:t,w:node.offsetWidth+me.w,h:node.offsetHeight+me.h};};dojo._getContentBox=function(node,_2c5){var s=_2c5||gcs(node),pe=dojo._getPadExtents(node,s),be=dojo._getBorderExtents(node,s),w=node.clientWidth,h;if(!w){w=node.offsetWidth,h=node.offsetHeight;}else{h=node.clientHeight,be.w=be.h=0;}if(dojo.isOpera){pe.l+=be.l;pe.t+=be.t;}return {l:pe.l,t:pe.t,w:w-pe.w-be.w,h:h-pe.h-be.h};};dojo._getBorderBox=function(node,_2cc){var s=_2cc||gcs(node),pe=dojo._getPadExtents(node,s),cb=dojo._getContentBox(node,s);return {l:cb.l-pe.l,t:cb.t-pe.t,w:cb.w+pe.w,h:cb.h+pe.h};};dojo._setBox=function(node,l,t,w,h,u){u=u||"px";with(node.style){if(!isNaN(l)){left=l+u;}if(!isNaN(t)){top=t+u;}if(w>=0){width=w+u;}if(h>=0){height=h+u;}}};dojo._usesBorderBox=function(node){var n=node.tagName;return dojo.boxModel=="border-box"||n=="TABLE"||n=="BUTTON";};dojo._setContentSize=function(node,_2d9,_2da,_2db){var bb=dojo._usesBorderBox(node);if(bb){var pb=dojo._getPadBorderExtents(node,_2db);if(_2d9>=0){_2d9+=pb.w;}if(_2da>=0){_2da+=pb.h;}}dojo._setBox(node,NaN,NaN,_2d9,_2da);};dojo._setMarginBox=function(node,_2df,_2e0,_2e1,_2e2,_2e3){var s=_2e3||dojo.getComputedStyle(node);var bb=dojo._usesBorderBox(node),pb=bb?_2e7:dojo._getPadBorderExtents(node,s),mb=dojo._getMarginExtents(node,s);if(_2e1>=0){_2e1=Math.max(_2e1-pb.w-mb.w,0);}if(_2e2>=0){_2e2=Math.max(_2e2-pb.h-mb.h,0);}dojo._setBox(node,_2df,_2e0,_2e1,_2e2);};var _2e7={l:0,t:0,w:0,h:0};dojo.marginBox=function(node,box){var n=dojo.byId(node),s=gcs(n),b=box;return !b?dojo._getMarginBox(n,s):dojo._setMarginBox(n,b.l,b.t,b.w,b.h,s);};dojo.contentBox=function(node,box){var n=dojo.byId(node),s=gcs(n),b=box;return !b?dojo._getContentBox(n,s):dojo._setContentSize(n,b.w,b.h,s);};var _2f3=function(node,prop){if(!(node=(node||0).parentNode)){return 0;}var val,_2f7=0,_b=dojo.body();while(node&&node.style){if(gcs(node).position=="fixed"){return 0;}val=node[prop];if(val){_2f7+=val-0;if(node==_b){break;}}node=node.parentNode;}return _2f7;};dojo._docScroll=function(){var _b=dojo.body();var _w=dojo.global;var de=dojo.doc.documentElement;return {y:(_w.pageYOffset||de.scrollTop||_b.scrollTop||0),x:(_w.pageXOffset||dojo._fixIeBiDiScrollLeft(de.scrollLeft)||_b.scrollLeft||0)};};dojo._isBodyLtr=function(){return !("_bodyLtr" in dojo)?dojo._bodyLtr=dojo.getComputedStyle(dojo.body()).direction=="ltr":dojo._bodyLtr;};dojo._getIeDocumentElementOffset=function(){var de=dojo.doc.documentElement;if(dojo.isIE>=7){return {x:de.getBoundingClientRect().left,y:de.getBoundingClientRect().top};}else{return {x:dojo._isBodyLtr()||window.parent==window?de.clientLeft:de.offsetWidth-de.clientWidth-de.clientLeft,y:de.clientTop};}};dojo._fixIeBiDiScrollLeft=function(_2fd){if(dojo.isIE&&!dojo._isBodyLtr()){var de=dojo.doc.documentElement;return _2fd+de.clientWidth-de.scrollWidth;}return _2fd;};dojo._abs=function(node,_300){var _301=node.ownerDocument;var ret={x:0,y:0};var _303=false;var db=dojo.body();if(dojo.isIE){var _305=node.getBoundingClientRect();var _306=dojo._getIeDocumentElementOffset();ret.x=_305.left-_306.x;ret.y=_305.top-_306.y;}else{if(_301["getBoxObjectFor"]){var bo=_301.getBoxObjectFor(node);ret.x=bo.x-_2f3(node,"scrollLeft");ret.y=bo.y-_2f3(node,"scrollTop");}else{if(node["offsetParent"]){_303=true;var _308;if(dojo.isSafari&&(gcs(node).position=="absolute")&&(node.parentNode==db)){_308=db;}else{_308=db.parentNode;}if(node.parentNode!=db){var nd=node;if(dojo.isOpera||(dojo.isSafari>=3)){nd=db;}ret.x-=_2f3(nd,"scrollLeft");ret.y-=_2f3(nd,"scrollTop");}var _30a=node;do{var n=_30a["offsetLeft"];if(!dojo.isOpera||n>0){ret.x+=isNaN(n)?0:n;}var m=_30a["offsetTop"];ret.y+=isNaN(m)?0:m;_30a=_30a.offsetParent;}while((_30a!=_308)&&_30a);}else{if(node["x"]&&node["y"]){ret.x+=isNaN(node.x)?0:node.x;ret.y+=isNaN(node.y)?0:node.y;}}}}if(_303||_300){var _30d=dojo._docScroll();var m=_303?(!_300?-1:0):1;ret.y+=m*_30d.y;ret.x+=m*_30d.x;}return ret;};dojo.coords=function(node,_30f){var n=dojo.byId(node),s=gcs(n),mb=dojo._getMarginBox(n,s);var abs=dojo._abs(n,_30f);mb.x=abs.x;mb.y=abs.y;return mb;};var _314=function(name){switch(name.toLowerCase()){case "tabindex":return dojo.isIE?"tabIndex":"tabindex";default:return name;}};var _316={colspan:"colSpan",enctype:"enctype",frameborder:"frameborder",method:"method",rowspan:"rowSpan",scrolling:"scrolling",shape:"shape",span:"span",type:"type",valuetype:"valueType"};var _317=function(node,name){var attr=node.getAttributeNode(name);return attr?attr.specified:false;};dojo.hasAttr=function(node,name){return _317(dojo.byId(node),_314(name));};dojo.attr=function(node,name,_31f){node=dojo.byId(node);name=_314(name);if(arguments.length==3){node.setAttribute(name,_31f);return undefined;}else{var prop=_316[name.toLowerCase()];if(prop){return node[prop];}else{return _317(node,name)?node.getAttribute(name):null;}}};dojo.removeAttr=function(node,name){dojo.byId(node).removeAttribute(_314(name));};})();dojo.hasClass=function(node,_324){return ((" "+dojo.byId(node).className+" ").indexOf(" "+_324+" ")>=0);};dojo.addClass=function(node,_326){node=dojo.byId(node);var cls=node.className;if((" "+cls+" ").indexOf(" "+_326+" ")<0){node.className=cls+(cls?" ":"")+_326;}};dojo.removeClass=function(node,_329){node=dojo.byId(node);var t=dojo.trim((" "+node.className+" ").replace(" "+_329+" "," "));if(node.className!=t){node.className=t;}};dojo.toggleClass=function(node,_32c,_32d){if(_32d===undefined){_32d=!dojo.hasClass(node,_32c);}dojo[_32d?"addClass":"removeClass"](node,_32c);};}if(!dojo._hasResource["dojo._base.NodeList"]){dojo._hasResource["dojo._base.NodeList"]=true;dojo.provide("dojo._base.NodeList");(function(){var d=dojo;var tnl=function(arr){arr.constructor=dojo.NodeList;dojo._mixin(arr,dojo.NodeList.prototype);return arr;};dojo.NodeList=function(){return tnl(Array.apply(null,arguments));};dojo.NodeList._wrap=tnl;dojo.extend(dojo.NodeList,{slice:function(){var a=dojo._toArray(arguments);return tnl(a.slice.apply(this,a));},splice:function(){var a=dojo._toArray(arguments);return tnl(a.splice.apply(this,a));},concat:function(){var a=dojo._toArray(arguments,0,[this]);return tnl(a.concat.apply([],a));},indexOf:function(_334,_335){return d.indexOf(this,_334,_335);},lastIndexOf:function(){return d.lastIndexOf.apply(d,d._toArray(arguments,0,[this]));},every:function(_336,_337){return d.every(this,_336,_337);},some:function(_338,_339){return d.some(this,_338,_339);},map:function(func,obj){return d.map(this,func,obj,d.NodeList);},forEach:function(_33c,_33d){d.forEach(this,_33c,_33d);return this;},coords:function(){return d.map(this,d.coords);},style:function(){var aa=d._toArray(arguments,0,[null]);var s=this.map(function(i){aa[0]=i;return d.style.apply(d,aa);});return (arguments.length>1)?this:s;},styles:function(){d.deprecated("NodeList.styles","use NodeList.style instead","1.1");return this.style.apply(this,arguments);},addClass:function(_341){this.forEach(function(i){d.addClass(i,_341);});return this;},removeClass:function(_343){this.forEach(function(i){d.removeClass(i,_343);});return this;},place:function(_345,_346){var item=d.query(_345)[0];_346=_346||"last";for(var x=0;x<this.length;x++){d.place(this[x],item,_346);}return this;},connect:function(_349,_34a,_34b){this.forEach(function(item){d.connect(item,_349,_34a,_34b);});return this;},orphan:function(_34d){var _34e=(_34d)?d._filterQueryResult(this,_34d):this;_34e.forEach(function(item){if(item["parentNode"]){item.parentNode.removeChild(item);}});return _34e;},adopt:function(_350,_351){var item=this[0];return d.query(_350).forEach(function(ai){d.place(ai,item,(_351||"last"));});},query:function(_354){_354=_354||"";var ret=d.NodeList();this.forEach(function(item){d.query(_354,item).forEach(function(_357){if(typeof _357!="undefined"){ret.push(_357);}});});return ret;},filter:function(_358){var _359=this;var _a=arguments;var r=d.NodeList();var rp=function(t){if(typeof t!="undefined"){r.push(t);}};if(d.isString(_358)){_359=d._filterQueryResult(this,_a[0]);if(_a.length==1){return _359;}d.forEach(d.filter(_359,_a[1],_a[2]),rp);return r;}d.forEach(d.filter(_359,_a[0],_a[1]),rp);return r;},addContent:function(_35e,_35f){var ta=d.doc.createElement("span");if(d.isString(_35e)){ta.innerHTML=_35e;}else{ta.appendChild(_35e);}var ct=((_35f=="first")||(_35f=="after"))?"lastChild":"firstChild";this.forEach(function(item){var tn=ta.cloneNode(true);while(tn[ct]){d.place(tn[ct],item,_35f);}});return this;}});d.forEach(["blur","click","keydown","keypress","keyup","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup"],function(evt){var _oe="on"+evt;dojo.NodeList.prototype[_oe]=function(a,b){return this.connect(_oe,a,b);};});})();}if(!dojo._hasResource["dojo._base.query"]){dojo._hasResource["dojo._base.query"]=true;dojo.provide("dojo._base.query");(function(){var d=dojo;var _369=dojo.isIE?"children":"childNodes";var _36a=function(_36b){if(_36b.charAt(_36b.length-1)==">"){_36b+=" *";}_36b+=" ";var ts=function(s,e){return d.trim(_36b.slice(s,e));};var _36f=[];var _370=-1;var _371=-1;var _372=-1;var _373=-1;var _374=-1;var inId=-1;var _376=-1;var lc="";var cc="";var _379;var x=0;var ql=_36b.length;var _37c=null;var _cp=null;var _37e=function(){if(_376>=0){var tv=(_376==x)?null:ts(_376,x).toLowerCase();_37c[(">~+".indexOf(tv)<0)?"tag":"oper"]=tv;_376=-1;}};var _380=function(){if(inId>=0){_37c.id=ts(inId,x).replace(/\\/g,"");inId=-1;}};var _381=function(){if(_374>=0){_37c.classes.push(ts(_374+1,x).replace(/\\/g,""));_374=-1;}};var _382=function(){_380();_37e();_381();};for(;lc=cc,cc=_36b.charAt(x),x<ql;x++){if(lc=="\\"){continue;}if(!_37c){_379=x;_37c={query:null,pseudos:[],attrs:[],classes:[],tag:null,oper:null,id:null};_376=x;}if(_370>=0){if(cc=="]"){if(!_cp.attr){_cp.attr=ts(_370+1,x);}else{_cp.matchFor=ts((_372||_370+1),x);}var cmf=_cp.matchFor;if(cmf){if((cmf.charAt(0)=="\"")||(cmf.charAt(0)=="'")){_cp.matchFor=cmf.substring(1,cmf.length-1);}}_37c.attrs.push(_cp);_cp=null;_370=_372=-1;}else{if(cc=="="){var _384=("|~^$*".indexOf(lc)>=0)?lc:"";_cp.type=_384+cc;_cp.attr=ts(_370+1,x-_384.length);_372=x+1;}}}else{if(_371>=0){if(cc==")"){if(_373>=0){_cp.value=ts(_371+1,x);}_373=_371=-1;}}else{if(cc=="#"){_382();inId=x+1;}else{if(cc=="."){_382();_374=x;}else{if(cc==":"){_382();_373=x;}else{if(cc=="["){_382();_370=x;_cp={};}else{if(cc=="("){if(_373>=0){_cp={name:ts(_373+1,x),value:null};_37c.pseudos.push(_cp);}_371=x;}else{if(cc==" "&&lc!=cc){_382();if(_373>=0){_37c.pseudos.push({name:ts(_373+1,x)});}_37c.hasLoops=(_37c.pseudos.length||_37c.attrs.length||_37c.classes.length);_37c.query=ts(_379,x);_37c.tag=(_37c["oper"])?null:(_37c.tag||"*");_36f.push(_37c);_37c=null;}}}}}}}}}return _36f;};var _385={"*=":function(attr,_387){return "[contains(@"+attr+", '"+_387+"')]";},"^=":function(attr,_389){return "[starts-with(@"+attr+", '"+_389+"')]";},"$=":function(attr,_38b){return "[substring(@"+attr+", string-length(@"+attr+")-"+(_38b.length-1)+")='"+_38b+"']";},"~=":function(attr,_38d){return "[contains(concat(' ',@"+attr+",' '), ' "+_38d+" ')]";},"|=":function(attr,_38f){return "[contains(concat(' ',@"+attr+",' '), ' "+_38f+"-')]";},"=":function(attr,_391){return "[@"+attr+"='"+_391+"']";}};var _392=function(_393,_394,_395,_396){d.forEach(_394.attrs,function(attr){var _398;if(attr.type&&_393[attr.type]){_398=_393[attr.type](attr.attr,attr.matchFor);}else{if(attr.attr.length){_398=_395(attr.attr);}}if(_398){_396(_398);}});};var _399=function(_39a){var _39b=".";var _39c=_36a(d.trim(_39a));while(_39c.length){var tqp=_39c.shift();var _39e;if(tqp.oper==">"){_39e="/";tqp=_39c.shift();}else{_39e="//";}_39b+=_39e+tqp.tag;if(tqp.id){_39b+="[@id='"+tqp.id+"'][1]";}d.forEach(tqp.classes,function(cn){var cnl=cn.length;var _3a1=" ";if(cn.charAt(cnl-1)=="*"){_3a1="";cn=cn.substr(0,cnl-1);}_39b+="[contains(concat(' ',@class,' '), ' "+cn+_3a1+"')]";});_392(_385,tqp,function(_3a2){return "[@"+_3a2+"]";},function(_3a3){_39b+=_3a3;});}return _39b;};var _3a4={};var _3a5=function(path){if(_3a4[path]){return _3a4[path];}var doc=d.doc;var _3a8=_399(path);var tf=function(_3aa){var ret=[];var _3ac;try{_3ac=doc.evaluate(_3a8,_3aa,null,XPathResult.ANY_TYPE,null);}catch(e){console.debug("failure in exprssion:",_3a8,"under:",_3aa);console.debug(e);}var _3ad=_3ac.iterateNext();while(_3ad){ret.push(_3ad);_3ad=_3ac.iterateNext();}return ret;};return _3a4[path]=tf;};var _3ae={};var _3af={};var _3b0=function(_3b1,_3b2){if(!_3b1){return _3b2;}if(!_3b2){return _3b1;}return function(){return _3b1.apply(window,arguments)&&_3b2.apply(window,arguments);};};var _3b3=function(_3b4,_3b5,_3b6,idx){var nidx=idx+1;var _3b9=(_3b5.length==nidx);var tqp=_3b5[idx];if(tqp.oper==">"){var ecn=_3b4[_369];if(!ecn||!ecn.length){return;}nidx++;_3b9=(_3b5.length==nidx);var tf=_3bd(_3b5[idx+1]);for(var x=0,ecnl=ecn.length,te;x<ecnl,te=ecn[x];x++){if(tf(te)){if(_3b9){_3b6.push(te);}else{_3b3(te,_3b5,_3b6,nidx);}}}}var _3c1=_3c2(tqp)(_3b4);if(_3b9){while(_3c1.length){_3b6.push(_3c1.shift());}}else{while(_3c1.length){_3b3(_3c1.shift(),_3b5,_3b6,nidx);}}};var _3c3=function(_3c4,_3c5){var ret=[];var x=_3c4.length-1,te;while(te=_3c4[x--]){_3b3(te,_3c5,ret,0);}return ret;};var _3bd=function(q){if(_3ae[q.query]){return _3ae[q.query];}var ff=null;if(q.tag){if(q.tag=="*"){ff=_3b0(ff,function(elem){return (elem.nodeType==1);});}else{ff=_3b0(ff,function(elem){return ((elem.nodeType==1)&&(q.tag==elem.tagName.toLowerCase()));});}}if(q.id){ff=_3b0(ff,function(elem){return ((elem.nodeType==1)&&(elem.id==q.id));});}if(q.hasLoops){ff=_3b0(ff,_3ce(q));}return _3ae[q.query]=ff;};var _3cf=function(node){var pn=node.parentNode;var pnc=pn.childNodes;var nidx=-1;var _3d4=pn.firstChild;if(!_3d4){return nidx;}var ci=node["__cachedIndex"];var cl=pn["__cachedLength"];if(((typeof cl=="number")&&(cl!=pnc.length))||(typeof ci!="number")){pn["__cachedLength"]=pnc.length;var idx=1;do{if(_3d4===node){nidx=idx;}if(_3d4.nodeType==1){_3d4["__cachedIndex"]=idx;idx++;}_3d4=_3d4.nextSibling;}while(_3d4);}else{nidx=ci;}return nidx;};var _3d8=0;var _3d9="";var _3da=function(elem,attr){if(attr=="class"){return elem.className||_3d9;}if(attr=="for"){return elem.htmlFor||_3d9;}return elem.getAttribute(attr,2)||_3d9;};var _3dd={"*=":function(attr,_3df){return function(elem){return (_3da(elem,attr).indexOf(_3df)>=0);};},"^=":function(attr,_3e2){return function(elem){return (_3da(elem,attr).indexOf(_3e2)==0);};},"$=":function(attr,_3e5){var tval=" "+_3e5;return function(elem){var ea=" "+_3da(elem,attr);return (ea.lastIndexOf(_3e5)==(ea.length-_3e5.length));};},"~=":function(attr,_3ea){var tval=" "+_3ea+" ";return function(elem){var ea=" "+_3da(elem,attr)+" ";return (ea.indexOf(tval)>=0);};},"|=":function(attr,_3ef){var _3f0=" "+_3ef+"-";return function(elem){var ea=" "+(elem.getAttribute(attr,2)||"");return ((ea==_3ef)||(ea.indexOf(_3f0)==0));};},"=":function(attr,_3f4){return function(elem){return (_3da(elem,attr)==_3f4);};}};var _3f6={"first-child":function(name,_3f8){return function(elem){if(elem.nodeType!=1){return false;}var fc=elem.previousSibling;while(fc&&(fc.nodeType!=1)){fc=fc.previousSibling;}return (!fc);};},"last-child":function(name,_3fc){return function(elem){if(elem.nodeType!=1){return false;}var nc=elem.nextSibling;while(nc&&(nc.nodeType!=1)){nc=nc.nextSibling;}return (!nc);};},"empty":function(name,_400){return function(elem){var cn=elem.childNodes;var cnl=elem.childNodes.length;for(var x=cnl-1;x>=0;x--){var nt=cn[x].nodeType;if((nt==1)||(nt==3)){return false;}}return true;};},"not":function(name,_407){var ntf=_3bd(_36a(_407)[0]);return function(elem){return (!ntf(elem));};},"nth-child":function(name,_40b){var pi=parseInt;if(_40b=="odd"){return function(elem){return (((_3cf(elem))%2)==1);};}else{if((_40b=="2n")||(_40b=="even")){return function(elem){return ((_3cf(elem)%2)==0);};}else{if(_40b.indexOf("0n+")==0){var _40f=pi(_40b.substr(3));return function(elem){return (elem.parentNode[_369][_40f-1]===elem);};}else{if((_40b.indexOf("n+")>0)&&(_40b.length>3)){var _411=_40b.split("n+",2);var pred=pi(_411[0]);var idx=pi(_411[1]);return function(elem){return ((_3cf(elem)%pred)==idx);};}else{if(_40b.indexOf("n")==-1){var _40f=pi(_40b);return function(elem){return (_3cf(elem)==_40f);};}}}}}}};var _416=(d.isIE)?function(cond){var clc=cond.toLowerCase();return function(elem){return elem[cond]||elem[clc];};}:function(cond){return function(elem){return (elem&&elem.getAttribute&&elem.hasAttribute(cond));};};var _3ce=function(_41c){var _41d=(_3af[_41c.query]||_3ae[_41c.query]);if(_41d){return _41d;}var ff=null;if(_41c.id){if(_41c.tag!="*"){ff=_3b0(ff,function(elem){return (elem.tagName.toLowerCase()==_41c.tag);});}}d.forEach(_41c.classes,function(_420,idx,arr){var _423=_420.charAt(_420.length-1)=="*";if(_423){_420=_420.substr(0,_420.length-1);}var re=new RegExp("(?:^|\\s)"+_420+(_423?".*":"")+"(?:\\s|$)");ff=_3b0(ff,function(elem){return re.test(elem.className);});ff.count=idx;});d.forEach(_41c.pseudos,function(_426){if(_3f6[_426.name]){ff=_3b0(ff,_3f6[_426.name](_426.name,_426.value));}});_392(_3dd,_41c,_416,function(_427){ff=_3b0(ff,_427);});if(!ff){ff=function(){return true;};}return _3af[_41c.query]=ff;};var _428={};var _3c2=function(_429,root){var fHit=_428[_429.query];if(fHit){return fHit;}if(_429.id&&!_429.hasLoops&&!_429.tag){return _428[_429.query]=function(root){return [d.byId(_429.id)];};}var _42d=_3ce(_429);var _42e;if(_429.tag&&_429.id&&!_429.hasLoops){_42e=function(root){var te=d.byId(_429.id);if(_42d(te)){return [te];}};}else{var tret;if(!_429.hasLoops){_42e=function(root){var ret=[];var te,x=0,tret=root.getElementsByTagName(_429.tag);while(te=tret[x++]){ret.push(te);}return ret;};}else{_42e=function(root){var ret=[];var te,x=0,tret=root.getElementsByTagName(_429.tag);while(te=tret[x++]){if(_42d(te)){ret.push(te);}}return ret;};}}return _428[_429.query]=_42e;};var _43a={};var _43b={"*":d.isIE?function(root){return root.all;}:function(root){return root.getElementsByTagName("*");},">":function(root){var ret=[];var te,x=0,tret=root[_369];while(te=tret[x++]){if(te.nodeType==1){ret.push(te);}}return ret;}};var _443=function(_444){var _445=_36a(d.trim(_444));if(_445.length==1){var tt=_3c2(_445[0]);tt.nozip=true;return tt;}var sqf=function(root){var _449=_445.slice(0);var _44a;if(_449[0].oper==">"){_44a=[root];}else{_44a=_3c2(_449.shift())(root);}return _3c3(_44a,_449);};return sqf;};var _44b=((document["evaluate"]&&!d.isSafari)?function(_44c){var _44d=_44c.split(" ");if((document["evaluate"])&&(_44c.indexOf(":")==-1)&&((true))){if(((_44d.length>2)&&(_44c.indexOf(">")==-1))||(_44d.length>3)||(_44c.indexOf("[")>=0)||((1==_44d.length)&&(0<=_44c.indexOf(".")))){return _3a5(_44c);}}return _443(_44c);}:_443);var _44e=function(_44f){if(_43b[_44f]){return _43b[_44f];}if(0>_44f.indexOf(",")){return _43b[_44f]=_44b(_44f);}else{var _450=_44f.split(/\s*,\s*/);var tf=function(root){var _453=0;var ret=[];var tp;while(tp=_450[_453++]){ret=ret.concat(_44b(tp,tp.indexOf(" "))(root));}return ret;};return _43b[_44f]=tf;}};var _456=0;var _zip=function(arr){if(arr&&arr.nozip){return d.NodeList._wrap(arr);}var ret=new d.NodeList();if(!arr){return ret;}if(arr[0]){ret.push(arr[0]);}if(arr.length<2){return ret;}_456++;arr[0]["_zipIdx"]=_456;for(var x=1,te;te=arr[x];x++){if(arr[x]["_zipIdx"]!=_456){ret.push(te);}te["_zipIdx"]=_456;}return ret;};d.query=function(_45c,root){if(_45c.constructor==d.NodeList){return _45c;}if(!d.isString(_45c)){return new d.NodeList(_45c);}if(d.isString(root)){root=d.byId(root);}return _zip(_44e(_45c)(root||d.doc));};d._filterQueryResult=function(_45e,_45f){var tnl=new d.NodeList();var ff=(_45f)?_3bd(_36a(_45f)[0]):function(){return true;};for(var x=0,te;te=_45e[x];x++){if(ff(te)){tnl.push(te);}}return tnl;};})();}if(!dojo._hasResource["dojo._base.xhr"]){dojo._hasResource["dojo._base.xhr"]=true;dojo.provide("dojo._base.xhr");(function(){var _d=dojo;function setValue(obj,name,_467){var val=obj[name];if(_d.isString(val)){obj[name]=[val,_467];}else{if(_d.isArray(val)){val.push(_467);}else{obj[name]=_467;}}};dojo.formToObject=function(_469){var ret={};var iq="input:not([type=file]):not([type=submit]):not([type=image]):not([type=reset]):not([type=button]), select, textarea";_d.query(iq,_469).filter(function(node){return !node.disabled;}).forEach(function(item){var _in=item.name;var type=(item.type||"").toLowerCase();if(type=="radio"||type=="checkbox"){if(item.checked){setValue(ret,_in,item.value);}}else{if(item.multiple){ret[_in]=[];_d.query("option",item).forEach(function(opt){if(opt.selected){setValue(ret,_in,opt.value);}});}else{setValue(ret,_in,item.value);if(type=="image"){ret[_in+".x"]=ret[_in+".y"]=ret[_in].x=ret[_in].y=0;}}}});return ret;};dojo.objectToQuery=function(map){var enc=encodeURIComponent;var _473=[];var _474={};for(var name in map){var _476=map[name];if(_476!=_474[name]){var _477=enc(name)+"=";if(_d.isArray(_476)){for(var i=0;i<_476.length;i++){_473.push(_477+enc(_476[i]));}}else{_473.push(_477+enc(_476));}}}return _473.join("&");};dojo.formToQuery=function(_479){return _d.objectToQuery(_d.formToObject(_479));};dojo.formToJson=function(_47a,_47b){return _d.toJson(_d.formToObject(_47a),_47b);};dojo.queryToObject=function(str){var ret={};var qp=str.split("&");var dec=decodeURIComponent;_d.forEach(qp,function(item){if(item.length){var _481=item.split("=");var name=dec(_481.shift());var val=dec(_481.join("="));if(_d.isString(ret[name])){ret[name]=[ret[name]];}if(_d.isArray(ret[name])){ret[name].push(val);}else{ret[name]=val;}}});return ret;};dojo._blockAsync=false;dojo._contentHandlers={"text":function(xhr){return xhr.responseText;},"json":function(xhr){if(!djConfig.usePlainJson){console.warn("Consider using mimetype:text/json-comment-filtered"+" to avoid potential security issues with JSON endpoints"+" (use djConfig.usePlainJson=true to turn off this message)");}return _d.fromJson(xhr.responseText);},"json-comment-filtered":function(xhr){var _487=xhr.responseText;var _488=_487.indexOf("/*");var _489=_487.lastIndexOf("*/");if(_488==-1||_489==-1){throw new Error("JSON was not comment filtered");}return _d.fromJson(_487.substring(_488+2,_489));},"javascript":function(xhr){return _d.eval(xhr.responseText);},"xml":function(xhr){var _48c=xhr.responseXML;if(_d.isIE&&(!_48c||window.location.protocol=="file:")){_d.forEach(["MSXML2","Microsoft","MSXML","MSXML3"],function(_48d){try{var dom=new ActiveXObject(_48d+".XMLDOM");dom.async=false;dom.loadXML(xhr.responseText);_48c=dom;}catch(e){}});}return _48c;}};dojo._contentHandlers["json-comment-optional"]=function(xhr){var _490=_d._contentHandlers;try{return _490["json-comment-filtered"](xhr);}catch(e){return _490["json"](xhr);}};dojo._ioSetArgs=function(args,_492,_493,_494){var _495={args:args,url:args.url};var _496=null;if(args.form){var form=_d.byId(args.form);var _498=form.getAttributeNode("action");_495.url=_495.url||(_498?_498.value:null);_496=_d.formToObject(form);}var _499=[{}];if(_496){_499.push(_496);}if(args.content){_499.push(args.content);}if(args.preventCache){_499.push({"dojo.preventCache":new Date().valueOf()});}_495.query=_d.objectToQuery(_d.mixin.apply(null,_499));_495.handleAs=args.handleAs||"text";var d=new _d.Deferred(_492);d.addCallbacks(_493,function(_49b){return _494(_49b,d);});var ld=args.load;if(ld&&_d.isFunction(ld)){d.addCallback(function(_49d){return ld.call(args,_49d,_495);});}var err=args.error;if(err&&_d.isFunction(err)){d.addErrback(function(_49f){return err.call(args,_49f,_495);});}var _4a0=args.handle;if(_4a0&&_d.isFunction(_4a0)){d.addBoth(function(_4a1){return _4a0.call(args,_4a1,_495);});}d.ioArgs=_495;return d;};var _4a2=function(dfd){dfd.canceled=true;var xhr=dfd.ioArgs.xhr;var _at=typeof xhr.abort;if(_at=="function"||_at=="unknown"){xhr.abort();}var err=new Error("xhr cancelled");err.dojoType="cancel";return err;};var _4a7=function(dfd){return _d._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);};var _4a9=function(_4aa,dfd){console.debug(_4aa);return _4aa;};var _4ac=function(args){var dfd=_d._ioSetArgs(args,_4a2,_4a7,_4a9);dfd.ioArgs.xhr=_d._xhrObj(dfd.ioArgs.args);return dfd;};var _4af=null;var _4b0=[];var _4b1=function(){var now=(new Date()).getTime();if(!_d._blockAsync){for(var i=0,tif;i<_4b0.length&&(tif=_4b0[i]);i++){var dfd=tif.dfd;try{if(!dfd||dfd.canceled||!tif.validCheck(dfd)){_4b0.splice(i--,1);}else{if(tif.ioCheck(dfd)){_4b0.splice(i--,1);tif.resHandle(dfd);}else{if(dfd.startTime){if(dfd.startTime+(dfd.ioArgs.args.timeout||0)<now){_4b0.splice(i--,1);var err=new Error("timeout exceeded");err.dojoType="timeout";dfd.errback(err);dfd.cancel();}}}}}catch(e){console.debug(e);dfd.errback(new Error("_watchInFlightError!"));}}}if(!_4b0.length){clearInterval(_4af);_4af=null;return;}};dojo._ioCancelAll=function(){try{_d.forEach(_4b0,function(i){i.dfd.cancel();});}catch(e){}};if(_d.isIE){_d.addOnUnload(_d._ioCancelAll);}_d._ioWatch=function(dfd,_4b9,_4ba,_4bb){if(dfd.ioArgs.args.timeout){dfd.startTime=(new Date()).getTime();}_4b0.push({dfd:dfd,validCheck:_4b9,ioCheck:_4ba,resHandle:_4bb});if(!_4af){_4af=setInterval(_4b1,50);}_4b1();};var _4bc="application/x-www-form-urlencoded";var _4bd=function(dfd){return dfd.ioArgs.xhr.readyState;};var _4bf=function(dfd){return 4==dfd.ioArgs.xhr.readyState;};var _4c1=function(dfd){var xhr=dfd.ioArgs.xhr;if(_d._isDocumentOk(xhr)){dfd.callback(dfd);}else{var err=new Error("Unable to load "+dfd.ioArgs.url+" status:"+xhr.status);err.status=xhr.status;err.responseText=xhr.responseText;dfd.errback(err);}};var _4c5=function(type,dfd){var _4c8=dfd.ioArgs;var args=_4c8.args;_4c8.xhr.open(type,_4c8.url,args.sync!==true,args.user||undefined,args.password||undefined);if(args.headers){for(var hdr in args.headers){if(hdr.toLowerCase()==="content-type"&&!args.contentType){args.contentType=args.headers[hdr];}else{_4c8.xhr.setRequestHeader(hdr,args.headers[hdr]);}}}_4c8.xhr.setRequestHeader("Content-Type",args.contentType||_4bc);try{_4c8.xhr.send(_4c8.query);}catch(e){dfd.cancel();}_d._ioWatch(dfd,_4bd,_4bf,_4c1);return dfd;};dojo._ioAddQueryToUrl=function(_4cb){if(_4cb.query.length){_4cb.url+=(_4cb.url.indexOf("?")==-1?"?":"&")+_4cb.query;_4cb.query=null;}};dojo.xhrGet=function(args){var dfd=_4ac(args);_d._ioAddQueryToUrl(dfd.ioArgs);return _4c5("GET",dfd);};dojo.xhrPost=function(args){return _4c5("POST",_4ac(args));};dojo.rawXhrPost=function(args){var dfd=_4ac(args);dfd.ioArgs.query=args.postData;return _4c5("POST",dfd);};dojo.xhrPut=function(args){return _4c5("PUT",_4ac(args));};dojo.rawXhrPut=function(args){var dfd=_4ac(args);var _4d4=dfd.ioArgs;if(args.putData){_4d4.query=args.putData;args.putData=null;}return _4c5("PUT",dfd);};dojo.xhrDelete=function(args){var dfd=_4ac(args);_d._ioAddQueryToUrl(dfd.ioArgs);return _4c5("DELETE",dfd);};})();}if(!dojo._hasResource["dojo._base.fx"]){dojo._hasResource["dojo._base.fx"]=true;dojo.provide("dojo._base.fx");dojo._Line=function(_4d7,end){this.start=_4d7;this.end=end;this.getValue=function(n){return ((this.end-this.start)*n)+this.start;};};dojo.declare("dojo._Animation",null,{constructor:function(args){dojo.mixin(this,args);if(dojo.isArray(this.curve)){this.curve=new dojo._Line(this.curve[0],this.curve[1]);}},duration:1000,repeat:0,rate:10,_percent:0,_startRepeatCount:0,fire:function(evt,args){if(this[evt]){this[evt].apply(this,args||[]);}return this;},play:function(_4dd,_4de){var _t=this;if(_4de){_t._stopTimer();_t._active=_t._paused=false;_t._percent=0;}else{if(_t._active&&!_t._paused){return _t;}}_t.fire("beforeBegin");var d=_4dd||_t.delay;var _p=dojo.hitch(_t,"_play",_4de);if(d>0){setTimeout(_p,d);return _t;}_p();return _t;},_play:function(_4e2){var _t=this;_t._startTime=new Date().valueOf();if(_t._paused){_t._startTime-=_t.duration*_t._percent;}_t._endTime=_t._startTime+_t.duration;_t._active=true;_t._paused=false;var _4e4=_t.curve.getValue(_t._percent);if(!_t._percent){if(!_t._startRepeatCount){_t._startRepeatCount=_t.repeat;}_t.fire("onBegin",[_4e4]);}_t.fire("onPlay",[_4e4]);_t._cycle();return _t;},pause:function(){this._stopTimer();if(!this._active){return this;}this._paused=true;this.fire("onPause",[this.curve.getValue(this._percent)]);return this;},gotoPercent:function(_4e5,_4e6){this._stopTimer();this._active=this._paused=true;this._percent=_4e5;if(_4e6){this.play();}return this;},stop:function(_4e7){if(!this._timer){return;}this._stopTimer();if(_4e7){this._percent=1;}this.fire("onStop",[this.curve.getValue(this._percent)]);this._active=this._paused=false;return this;},status:function(){if(this._active){return this._paused?"paused":"playing";}return "stopped";},_cycle:function(){var _t=this;if(_t._active){var curr=new Date().valueOf();var step=(curr-_t._startTime)/(_t._endTime-_t._startTime);if(step>=1){step=1;}_t._percent=step;if(_t.easing){step=_t.easing(step);}_t.fire("onAnimate",[_t.curve.getValue(step)]);if(_t._percent<1){_t._startTimer();}else{_t._active=false;if(_t.repeat>0){_t.repeat--;_t.play(null,true);}else{if(_t.repeat==-1){_t.play(null,true);}else{if(_t._startRepeatCount){_t.repeat=_t._startRepeatCount;_t._startRepeatCount=0;}}}_t._percent=0;_t.fire("onEnd");}}return _t;}});(function(){var d=dojo;var ctr=0;var _4ed=[];var _4ee={run:function(){}};var _4ef=null;dojo._Animation.prototype._startTimer=function(){if(!this._timer){this._timer=dojo.connect(_4ee,"run",this,"_cycle");ctr++;}if(!_4ef){_4ef=setInterval(dojo.hitch(_4ee,"run"),this.rate);}};dojo._Animation.prototype._stopTimer=function(){dojo.disconnect(this._timer);this._timer=null;ctr--;if(!ctr){clearInterval(_4ef);_4ef=null;}};var _4f0=(d.isIE)?function(node){var ns=node.style;if(!ns.zoom.length&&d.style(node,"zoom")=="normal"){ns.zoom="1";}if(!ns.width.length&&d.style(node,"width")=="auto"){ns.width="auto";}}:function(){};dojo._fade=function(args){args.node=d.byId(args.node);var _4f4=d.mixin({properties:{}},args);var _4f5=(_4f4.properties.opacity={});_4f5.start=!("start" in _4f4)?function(){return Number(d.style(_4f4.node,"opacity"));}:_4f4.start;_4f5.end=_4f4.end;var anim=d.animateProperty(_4f4);d.connect(anim,"beforeBegin",d.partial(_4f0,_4f4.node));return anim;};dojo.fadeIn=function(args){return d._fade(d.mixin({end:1},args));};dojo.fadeOut=function(args){return d._fade(d.mixin({end:0},args));};dojo._defaultEasing=function(n){return 0.5+((Math.sin((n+1.5)*Math.PI))/2);};var _4fa=function(_4fb){this._properties=_4fb;for(var p in _4fb){var prop=_4fb[p];if(prop.start instanceof d.Color){prop.tempColor=new d.Color();}}this.getValue=function(r){var ret={};for(var p in this._properties){var prop=this._properties[p];var _502=prop.start;if(_502 instanceof d.Color){ret[p]=d.blendColors(_502,prop.end,r,prop.tempColor).toCss();}else{if(!d.isArray(_502)){ret[p]=((prop.end-_502)*r)+_502+(p!="opacity"?prop.units||"px":"");}}}return ret;};};dojo.animateProperty=function(args){args.node=d.byId(args.node);if(!args.easing){args.easing=d._defaultEasing;}var anim=new d._Animation(args);d.connect(anim,"beforeBegin",anim,function(){var pm={};for(var p in this.properties){var prop=(pm[p]=d.mixin({},this.properties[p]));if(d.isFunction(prop.start)){prop.start=prop.start();}if(d.isFunction(prop.end)){prop.end=prop.end();}var _508=(p.toLowerCase().indexOf("color")>=0);function getStyle(node,p){var v=({height:node.offsetHeight,width:node.offsetWidth})[p];if(v!==undefined){return v;}v=d.style(node,p);return (p=="opacity")?Number(v):parseFloat(v);};if(!("end" in prop)){prop.end=getStyle(this.node,p);}else{if(!("start" in prop)){prop.start=getStyle(this.node,p);}}if(_508){prop.start=new d.Color(prop.start);prop.end=new d.Color(prop.end);}else{prop.start=(p=="opacity")?Number(prop.start):parseFloat(prop.start);}}this.curve=new _4fa(pm);});d.connect(anim,"onAnimate",anim,function(_50c){for(var s in _50c){d.style(this.node,s,_50c[s]);}});return anim;};})();}if(!dojo._hasResource["dojo.i18n"]){dojo._hasResource["dojo.i18n"]=true;dojo.provide("dojo.i18n");dojo.i18n.getLocalization=function(_50e,_50f,_510){_510=dojo.i18n.normalizeLocale(_510);var _511=_510.split("-");var _512=[_50e,"nls",_50f].join(".");var _513=dojo._loadedModules[_512];if(_513){var _514;for(var i=_511.length;i>0;i--){var loc=_511.slice(0,i).join("_");if(_513[loc]){_514=_513[loc];break;}}if(!_514){_514=_513.ROOT;}if(_514){var _517=function(){};_517.prototype=_514;return new _517();}}throw new Error("Bundle not found: "+_50f+" in "+_50e+" , locale="+_510);};dojo.i18n.normalizeLocale=function(_518){var _519=_518?_518.toLowerCase():dojo.locale;if(_519=="root"){_519="ROOT";}return _519;};dojo.i18n._requireLocalization=function(_51a,_51b,_51c,_51d){var _51e=dojo.i18n.normalizeLocale(_51c);var _51f=[_51a,"nls",_51b].join(".");var _520="";if(_51d){var _521=_51d.split(",");for(var i=0;i<_521.length;i++){if(_51e.indexOf(_521[i])==0){if(_521[i].length>_520.length){_520=_521[i];}}}if(!_520){_520="ROOT";}}var _523=_51d?_520:_51e;var _524=dojo._loadedModules[_51f];var _525=null;if(_524){if(djConfig.localizationComplete&&_524._built){return;}var _526=_523.replace(/-/g,"_");var _527=_51f+"."+_526;_525=dojo._loadedModules[_527];}if(!_525){_524=dojo["provide"](_51f);var syms=dojo._getModuleSymbols(_51a);var _529=syms.concat("nls").join("/");var _52a;dojo.i18n._searchLocalePath(_523,_51d,function(loc){var _52c=loc.replace(/-/g,"_");var _52d=_51f+"."+_52c;var _52e=false;if(!dojo._loadedModules[_52d]){dojo["provide"](_52d);var _52f=[_529];if(loc!="ROOT"){_52f.push(loc);}_52f.push(_51b);var _530=_52f.join("/")+".js";_52e=dojo._loadPath(_530,null,function(hash){var _532=function(){};_532.prototype=_52a;_524[_52c]=new _532();for(var j in hash){_524[_52c][j]=hash[j];}});}else{_52e=true;}if(_52e&&_524[_52c]){_52a=_524[_52c];}else{_524[_52c]=_52a;}if(_51d){return true;}});}if(_51d&&_51e!=_520){_524[_51e.replace(/-/g,"_")]=_524[_520.replace(/-/g,"_")];}};(function(){var _534=djConfig.extraLocale;if(_534){if(!_534 instanceof Array){_534=[_534];}var req=dojo.i18n._requireLocalization;dojo.i18n._requireLocalization=function(m,b,_538,_539){req(m,b,_538,_539);if(_538){return;}for(var i=0;i<_534.length;i++){req(m,b,_534[i],_539);}};}})();dojo.i18n._searchLocalePath=function(_53b,down,_53d){_53b=dojo.i18n.normalizeLocale(_53b);var _53e=_53b.split("-");var _53f=[];for(var i=_53e.length;i>0;i--){_53f.push(_53e.slice(0,i).join("-"));}_53f.push(false);if(down){_53f.reverse();}for(var j=_53f.length-1;j>=0;j--){var loc=_53f[j]||"ROOT";var stop=_53d(loc);if(stop){break;}}};dojo.i18n._preloadLocalizations=function(_544,_545){function preload(_546){_546=dojo.i18n.normalizeLocale(_546);dojo.i18n._searchLocalePath(_546,true,function(loc){for(var i=0;i<_545.length;i++){if(_545[i]==loc){dojo["require"](_544+"_"+loc);return true;}}return false;});};preload();var _549=djConfig.extraLocale||[];for(var i=0;i<_549.length;i++){preload(_549[i]);}};}\r
diff --git a/webapps/cometd/examples/dojo/dojo/dojo.xd.js.uncompressed.js b/webapps/cometd/examples/dojo/dojo/dojo.xd.js.uncompressed.js
new file mode 100644 (file)
index 0000000..f4eeafe
--- /dev/null
@@ -0,0 +1,8203 @@
+/*\r
+       Copyright (c) 2004-2007, The Dojo Foundation\r
+       All Rights Reserved.\r
+\r
+       Licensed under the Academic Free License version 2.1 or above OR the\r
+       modified BSD license. For more information on Dojo licensing, see:\r
+\r
+               http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing\r
+*/\r
+\r
+/*\r
+       This is a compiled version of Dojo, built for deployment and not for\r
+       development. To get an editable version, please visit:\r
+\r
+               http://dojotoolkit.org\r
+\r
+       for documentation and information on getting the source.\r
+*/\r
+\r
+if(typeof dojo == "undefined"){\r
+\r
+/*=====\r
+djConfig = {\r
+       // summary:\r
+       //              Application code can set the global 'djConfig' prior to loading\r
+       //              the library to override certain global settings for how dojo works.\r
+       // description:  The variables that can be set are as follows:\r
+       //                      - isDebug: false\r
+       //                      - libraryScriptUri: ""\r
+       //                      - locale: undefined\r
+       //                      - extraLocale: undefined\r
+       //                      - preventBackButtonFix: true\r
+       // note:\r
+       //              'djConfig' does not exist under 'dojo.*' so that it can be set before the\r
+       //              'dojo' variable exists.\r
+       // note:\r
+       //              Setting any of these variables *after* the library has loaded does\r
+       //              nothing at all.\r
+}\r
+=====*/\r
+\r
+(function(){\r
+       // make sure djConfig is defined\r
+       if(typeof this["djConfig"] == "undefined"){\r
+               this.djConfig = {};\r
+       }\r
+\r
+       // firebug stubs\r
+       if((!this["console"])||(!console["firebug"])){\r
+               this.console = {};\r
+       }\r
+\r
+       var cn = [\r
+               "assert", "count", "debug", "dir", "dirxml", "error", "group",\r
+               "groupEnd", "info", "log", "profile", "profileEnd", "time",\r
+               "timeEnd", "trace", "warn"\r
+       ];\r
+       var i=0, tn;\r
+       while((tn=cn[i++])){\r
+               if(!console[tn]){\r
+                       console[tn] = function(){};\r
+               }\r
+       }\r
+\r
+       //TODOC:  HOW TO DOC THIS?\r
+       // dojo is the root variable of (almost all) our public symbols -- make sure it is defined.\r
+       if(typeof this["dojo"] == "undefined"){\r
+               this.dojo = {};\r
+       }\r
+\r
+       var d = dojo;\r
+\r
+/*=====\r
+dojo.global = {\r
+       // summary:\r
+       //              Alias for the global scope\r
+       //              (e.g. the window object in a browser).\r
+       // description:\r
+       //              Refer to 'dojo.global' rather than referring to window to ensure your\r
+       //              code runs correctly in contexts other than web browsers (e.g. Rhino on a server).\r
+}\r
+=====*/\r
+       dojo.global = this;\r
+\r
+       var _config =/*===== djConfig = =====*/{\r
+               isDebug: false,\r
+               libraryScriptUri: "",\r
+               preventBackButtonFix: true,\r
+               delayMozLoadingFix: false\r
+       };\r
+\r
+       for(var option in _config){\r
+               if(typeof djConfig[option] == "undefined"){\r
+                       djConfig[option] = _config[option];\r
+               }\r
+       }\r
+\r
+       var _platforms = ["Browser", "Rhino", "Spidermonkey", "Mobile"];\r
+       var t;\r
+       while(t=_platforms.shift()){\r
+               d["is"+t] = false;\r
+       }\r
+\r
+/*=====\r
+       // Override locale setting, if specified\r
+       dojo.locale = {\r
+               // summary: the locale as defined by Dojo (read-only)\r
+       };\r
+=====*/\r
+       dojo.locale = djConfig.locale;\r
+       \r
+       var rev = "$Rev: 11742 $".match(/\d+/);\r
+\r
+       dojo.version = {\r
+               // summary: version number of this instance of dojo.\r
+               major: 0, minor: 0, patch: 0, flag: "dev",\r
+               revision: rev ? Number(rev[0]) : 999999,\r
+               toString: function(){\r
+                       with(d.version){\r
+                               return major + "." + minor + "." + patch + flag + " (" + revision + ")";        // String\r
+                       }\r
+               }\r
+       }\r
+\r
+       // Register with the OpenAjax hub\r
+       if(typeof OpenAjax != "undefined"){\r
+               OpenAjax.hub.registerLibrary("dojo", "http://dojotoolkit.org", d.version.toString());\r
+       }\r
+\r
+       dojo._mixin = function(/*Object*/ obj, /*Object*/ props){\r
+               // summary:\r
+               //              Adds all properties and methods of props to obj. This addition is\r
+               //              "prototype extension safe", so that instances of objects will not\r
+               //              pass along prototype defaults.\r
+               var tobj = {};\r
+               for(var x in props){\r
+                       // the "tobj" condition avoid copying properties in "props"\r
+                       // inherited from Object.prototype.  For example, if obj has a custom\r
+                       // toString() method, don't overwrite it with the toString() method\r
+                       // that props inherited from Object.prototype\r
+                       if(tobj[x] === undefined || tobj[x] != props[x]){\r
+                               obj[x] = props[x];\r
+                       }\r
+               }\r
+               // IE doesn't recognize custom toStrings in for..in\r
+               if(d["isIE"] && props){\r
+                       var p = props.toString;\r
+                       if(typeof p == "function" && p != obj.toString && p != tobj.toString &&\r
+                               p != "\nfunction toString() {\n    [native code]\n}\n"){\r
+                                       obj.toString = props.toString;\r
+                       }\r
+               }\r
+               return obj; // Object\r
+       }\r
+\r
+       dojo.mixin = function(/*Object*/obj, /*Object...*/props){\r
+               // summary:     Adds all properties and methods of props to obj. \r
+               for(var i=1, l=arguments.length; i<l; i++){\r
+                       d._mixin(obj, arguments[i]);\r
+               }\r
+               return obj; // Object\r
+       }\r
+\r
+       dojo._getProp = function(/*Array*/parts, /*Boolean*/create, /*Object*/context){\r
+               var obj=context||d.global;\r
+               for(var i=0, p; obj&&(p=parts[i]); i++){\r
+                       obj = (p in obj ? obj[p] : (create ? obj[p]={} : undefined));\r
+               }\r
+               return obj; // mixed\r
+       }\r
+\r
+       dojo.setObject = function(/*String*/name, /*mixed*/value, /*Object*/context){\r
+               // summary: \r
+               //              Set a property from a dot-separated string, such as "A.B.C"\r
+               //      description: \r
+               //              Useful for longer api chains where you have to test each object in\r
+               //              the chain, or when you have an object reference in string format.\r
+               //              Objects are created as needed along 'path'.\r
+               //      name:   \r
+               //              Path to a property, in the form "A.B.C".\r
+               //      context:\r
+               //              Optional. Object to use as root of path. Defaults to\r
+               //              'dojo.global'. Null may be passed.\r
+               var parts=name.split("."), p=parts.pop(), obj=d._getProp(parts, true, context);\r
+               return (obj && p ? (obj[p]=value) : undefined); // mixed\r
+       }\r
+\r
+       dojo.getObject = function(/*String*/name, /*Boolean*/create, /*Object*/context){\r
+               // summary: \r
+               //              Get a property from a dot-separated string, such as "A.B.C"\r
+               //      description: \r
+               //              Useful for longer api chains where you have to test each object in\r
+               //              the chain, or when you have an object reference in string format.\r
+               //      name:   \r
+               //              Path to an property, in the form "A.B.C".\r
+               //      context:\r
+               //              Optional. Object to use as root of path. Defaults to\r
+               //              'dojo.global'. Null may be passed.\r
+               //      create: \r
+               //              Optional. If true, Objects will be created at any point along the\r
+               //              'path' that is undefined.\r
+               return d._getProp(name.split("."), create, context); // mixed\r
+       }\r
+\r
+       dojo.exists = function(/*String*/name, /*Object?*/obj){\r
+               // summary: \r
+               //              determine if an object supports a given method\r
+               // description: \r
+               //              useful for longer api chains where you have to test each object in\r
+               //              the chain\r
+               // name:        \r
+               //              Path to an object, in the form "A.B.C".\r
+               // obj:\r
+               //              Object to use as root of path. Defaults to\r
+               //              'dojo.global'. Null may be passed.\r
+               return !!d.getObject(name, false, obj); // Boolean\r
+       }\r
+\r
+\r
+       dojo["eval"] = function(/*String*/ scriptFragment){\r
+               // summary: \r
+               //              Perform an evaluation in the global scope.  Use this rather than\r
+               //              calling 'eval()' directly.\r
+               // description: \r
+               //              Placed in a separate function to minimize size of trapped\r
+               //              evaluation context.\r
+               // note:\r
+               //       - JSC eval() takes an optional second argument which can be 'unsafe'.\r
+               //       - Mozilla/SpiderMonkey eval() takes an optional second argument which is the\r
+               //       scope object for new symbols.\r
+\r
+               // FIXME: investigate Joseph Smarr's technique for IE:\r
+               //              http://josephsmarr.com/2007/01/31/fixing-eval-to-use-global-scope-in-ie/\r
+               //      see also:\r
+               //              http://trac.dojotoolkit.org/ticket/744\r
+               return d.global.eval ? d.global.eval(scriptFragment) : eval(scriptFragment);    // mixed\r
+       }\r
+\r
+       /*=====\r
+               dojo.deprecated = function(behaviour, extra, removal){\r
+                       //      summary: \r
+                       //              Log a debug message to indicate that a behavior has been\r
+                       //              deprecated.\r
+                       //      behaviour: String\r
+                       //              The API or behavior being deprecated. Usually in the form\r
+                       //              of "myApp.someFunction()".\r
+                       //      extra: String?\r
+                       //              Text to append to the message. Often provides advice on a\r
+                       //              new function or facility to achieve the same goal during\r
+                       //              the deprecation period.\r
+                       //      removal: String?\r
+                       //              Text to indicate when in the future the behavior will be\r
+                       //              removed. Usually a version number.\r
+                       //      example:\r
+                       //      |       dojo.deprecated("myApp.getTemp()", "use myApp.getLocaleTemp() instead", "1.0");\r
+               }\r
+\r
+               dojo.experimental = function(moduleName, extra){\r
+                       //      summary: Marks code as experimental.\r
+                       //      description: \r
+                       //              This can be used to mark a function, file, or module as\r
+                       //              experimental.  Experimental code is not ready to be used, and the\r
+                       //              APIs are subject to change without notice.  Experimental code may be\r
+                       //              completed deleted without going through the normal deprecation\r
+                       //              process.\r
+                       //      moduleName: String\r
+                       //              The name of a module, or the name of a module file or a specific\r
+                       //              function\r
+                       //      extra: String?\r
+                       //              some additional message for the user\r
+                       //      example:\r
+                       //      |       dojo.experimental("dojo.data.Result");\r
+                       //      example:\r
+                       //      |       dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA");\r
+               }\r
+       =====*/\r
+\r
+       //Real functions declared in dojo._firebug.firebug.\r
+       d.deprecated = d.experimental = function(){};\r
+\r
+})();\r
+// vim:ai:ts=4:noet\r
+\r
+/*\r
+ * loader.js - A bootstrap module.  Runs before the hostenv_*.js file. Contains\r
+ * all of the package loading methods.\r
+ */\r
+\r
+(function(){\r
+       var d = dojo;\r
+\r
+       d.mixin(d, {\r
+               _loadedModules: {},\r
+               _inFlightCount: 0,\r
+               _hasResource: {},\r
+\r
+               // FIXME: it should be possible to pull module prefixes in from djConfig\r
+               _modulePrefixes: {\r
+                       dojo: {name: "dojo", value: "."},\r
+                       doh: {name: "doh", value: "../util/doh"},\r
+                       tests: {name: "tests", value: "tests"}\r
+               },\r
+\r
+               _moduleHasPrefix: function(/*String*/module){\r
+                       // summary: checks to see if module has been established\r
+                       var mp = this._modulePrefixes;\r
+                       return !!(mp[module] && mp[module].value); // Boolean\r
+               },\r
+\r
+               _getModulePrefix: function(/*String*/module){\r
+                       // summary: gets the prefix associated with module\r
+                       var mp = this._modulePrefixes;\r
+                       if(this._moduleHasPrefix(module)){\r
+                               return mp[module].value; // String\r
+                       }\r
+                       return module; // String\r
+               },\r
+\r
+               _loadedUrls: [],\r
+\r
+               //WARNING: \r
+               //              This variable is referenced by packages outside of bootstrap:\r
+               //              FloatingPane.js and undo/browser.js\r
+               _postLoad: false,\r
+               \r
+               //Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.\r
+               _loaders: [],\r
+               _unloaders: [],\r
+               _loadNotifying: false\r
+       });\r
+\r
+\r
+       \r
+       // FIXME: probably need to add logging to this method\r
+       dojo._loadUriAndCheck = function(/*String (URL)*/uri, /*String*/moduleName, /*Function?*/cb){\r
+               // summary: calls loadUri then findModule and returns true if both succeed\r
+               var ok = false;\r
+               try{\r
+                       ok = this._loadUri(uri, cb);\r
+               }catch(e){\r
+                       console.error("failed loading " + uri + " with error: " + e);\r
+               }\r
+               return !!(ok && this._loadedModules[moduleName]); // Boolean\r
+       }\r
+\r
+       dojo.loaded = function(){\r
+               // summary:\r
+               //              signal fired when initial environment and package loading is\r
+               //              complete. You may use dojo.addOnLoad() or dojo.connect() to\r
+               //              this method in order to handle initialization tasks that\r
+               //              require the environment to be initialized. In a browser host,\r
+               //              declarative widgets will be constructed when this function\r
+               //              finishes runing.\r
+               this._loadNotifying = true;\r
+               this._postLoad = true;\r
+               var mll = this._loaders;\r
+               \r
+               //Clear listeners so new ones can be added\r
+               //For other xdomain package loads after the initial load.\r
+               this._loaders = [];\r
+\r
+               for(var x=0; x<mll.length; x++){\r
+                       try{ mll[x](); }catch(e){ console.error(e); /* let other events fire */ }\r
+               }\r
+\r
+               this._loadNotifying = false;\r
+               \r
+               //Make sure nothing else got added to the onload queue\r
+               //after this first run. If something did, and we are not waiting for any\r
+               //more inflight resources, run again.\r
+               if(d._postLoad && d._inFlightCount == 0 && this._loaders.length > 0){\r
+                       d._callLoaded();\r
+               }\r
+       }\r
+\r
+       dojo.unloaded = function(){\r
+               // summary:\r
+               //              signal fired by impending environment destruction. You may use\r
+               //              dojo.addOnUnload() or dojo.connect() to this method to perform\r
+               //              page/application cleanup methods.\r
+               var mll = this._unloaders;\r
+               while(mll.length){\r
+                       (mll.pop())();\r
+               }\r
+       }\r
+\r
+       dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName){\r
+               // summary:\r
+               //              Registers a function to be triggered after the DOM has finished\r
+               //              loading and widgets declared in markup have been instantiated.\r
+               //              Images and CSS files may or may not have finished downloading when\r
+               //              the specified function is called.  (Note that widgets' CSS and HTML\r
+               //              code is guaranteed to be downloaded before said widgets are\r
+               //              instantiated.)\r
+               // example:\r
+               //      |       dojo.addOnLoad(functionPointer);\r
+               //      |       dojo.addOnLoad(object, "functionName");\r
+               if(arguments.length == 1){\r
+                       d._loaders.push(obj);\r
+               }else if(arguments.length > 1){\r
+                       d._loaders.push(function(){\r
+                               obj[functionName]();\r
+                       });\r
+               }\r
+\r
+               //Added for xdomain loading. dojo.addOnLoad is used to\r
+               //indicate callbacks after doing some dojo.require() statements.\r
+               //In the xdomain case, if all the requires are loaded (after initial\r
+               //page load), then immediately call any listeners.\r
+               if(d._postLoad && d._inFlightCount == 0 && !d._loadNotifying){\r
+                       d._callLoaded();\r
+               }\r
+       }\r
+\r
+       dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){\r
+               // summary: registers a function to be triggered when the page unloads\r
+               // example:\r
+               //      |       dojo.addOnUnload(functionPointer)\r
+               //      |       dojo.addOnUnload(object, "functionName")\r
+               if(arguments.length == 1){\r
+                       d._unloaders.push(obj);\r
+               }else if(arguments.length > 1){\r
+                       d._unloaders.push(function(){\r
+                               obj[functionName]();\r
+                       });\r
+               }\r
+       }\r
+\r
+       dojo._modulesLoaded = function(){\r
+               if(d._postLoad){ return; }\r
+               if(d._inFlightCount > 0){ \r
+                       console.warn("files still in flight!");\r
+                       return;\r
+               }\r
+               d._callLoaded();\r
+       }\r
+\r
+       dojo._callLoaded = function(){\r
+               //The "object" check is for IE, and the other opera check fixes an issue\r
+               //in Opera where it could not find the body element in some widget test cases.\r
+               //For 0.9, maybe route all browsers through the setTimeout (need protection\r
+               //still for non-browser environments though). This might also help the issue with\r
+               //FF 2.0 and freezing issues where we try to do sync xhr while background css images\r
+               //are being loaded (trac #2572)? Consider for 0.9.\r
+               if(typeof setTimeout == "object" || (djConfig.useXDomain && d.isOpera)){\r
+                       setTimeout("dojo.loaded();", 0);\r
+               }else{\r
+                       d.loaded();\r
+               }\r
+       }\r
+\r
+       dojo._getModuleSymbols = function(/*String*/modulename){\r
+               // summary:\r
+               //              Converts a module name in dotted JS notation to an array\r
+               //              representing the path in the source tree\r
+               var syms = modulename.split(".");\r
+               for(var i = syms.length; i>0; i--){\r
+                       var parentModule = syms.slice(0, i).join(".");\r
+                       if((i==1) && !this._moduleHasPrefix(parentModule)){             \r
+                               // Support default module directory (sibling of dojo) for top-level modules \r
+                               syms[0] = "../" + syms[0];\r
+                       }else{\r
+                               var parentModulePath = this._getModulePrefix(parentModule);\r
+                               if(parentModulePath != parentModule){\r
+                                       syms.splice(0, i, parentModulePath);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+               // console.debug(syms);\r
+               return syms; // Array\r
+       }\r
+\r
+       dojo._global_omit_module_check = false;\r
+\r
+       dojo._loadModule = dojo.require = function(/*String*/moduleName, /*Boolean?*/omitModuleCheck){\r
+               //      summary:\r
+               //              loads a Javascript module from the appropriate URI\r
+               //      moduleName: String\r
+               //      omitModuleCheck: Boolean?\r
+               //      description:\r
+               //              _loadModule("A.B") first checks to see if symbol A.B is defined. If\r
+               //              it is, it is simply returned (nothing to do).\r
+               //      \r
+               //              If it is not defined, it will look for "A/B.js" in the script root\r
+               //              directory.\r
+               //      \r
+               //              It throws if it cannot find a file to load, or if the symbol A.B is\r
+               //              not defined after loading.\r
+               //      \r
+               //              It returns the object A.B.\r
+               //      \r
+               //              This does nothing about importing symbols into the current package.\r
+               //              It is presumed that the caller will take care of that. For example,\r
+               //              to import all symbols:\r
+               //      \r
+               //              |       with (dojo._loadModule("A.B")) {\r
+               //              |               ...\r
+               //              |       }\r
+               //      \r
+               //              And to import just the leaf symbol:\r
+               //      \r
+               //              |       var B = dojo._loadModule("A.B");\r
+               //              |       ...\r
+               //      returns: the required namespace object\r
+               omitModuleCheck = this._global_omit_module_check || omitModuleCheck;\r
+               var module = this._loadedModules[moduleName];\r
+               if(module){\r
+                       return module;\r
+               }\r
+\r
+               // convert periods to slashes\r
+               var relpath = this._getModuleSymbols(moduleName).join("/") + '.js';\r
+\r
+               var modArg = (!omitModuleCheck) ? moduleName : null;\r
+               var ok = this._loadPath(relpath, modArg);\r
+\r
+               if(!ok && !omitModuleCheck){\r
+                       throw new Error("Could not load '" + moduleName + "'; last tried '" + relpath + "'");\r
+               }\r
+\r
+               // check that the symbol was defined\r
+               // Don't bother if we're doing xdomain (asynchronous) loading.\r
+               if(!omitModuleCheck && !this._isXDomain){\r
+                       // pass in false so we can give better error\r
+                       module = this._loadedModules[moduleName];\r
+                       if(!module){\r
+                               throw new Error("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); \r
+                       }\r
+               }\r
+\r
+               return module;\r
+       }\r
+\r
+       dojo.provide = function(/*String*/ resourceName){\r
+               //      summary:\r
+               //              Each javascript source file must have (exactly) one dojo.provide()\r
+               //              call at the top of the file, corresponding to the file name.\r
+               //              For example, js/dojo/foo.js must have dojo.provide("dojo.foo"); at the\r
+               //              top of the file.\r
+               //      description:\r
+               //              Each javascript source file is called a resource.  When a resource\r
+               //              is loaded by the browser, dojo.provide() registers that it has been\r
+               //              loaded.\r
+               //      \r
+               //              For backwards compatibility reasons, in addition to registering the\r
+               //              resource, dojo.provide() also ensures that the javascript object\r
+               //              for the module exists.  For example,\r
+               //              dojo.provide("dojo.io.cometd"), in addition to registering that\r
+               //              cometd.js is a resource for the dojo.iomodule, will ensure that\r
+               //              the dojo.io javascript object exists, so that calls like\r
+               //              dojo.io.foo = function(){ ... } don't fail.\r
+               //\r
+               //              In the case of a build (or in the future, a rollup), where multiple\r
+               //              javascript source files are combined into one bigger file (similar\r
+               //              to a .lib or .jar file), that file will contain multiple\r
+               //              dojo.provide() calls, to note that it includes multiple resources.\r
+\r
+               //Make sure we have a string.\r
+               resourceName = resourceName + "";\r
+               return (d._loadedModules[resourceName] = d.getObject(resourceName, true)); // Object\r
+       }\r
+\r
+       //Start of old bootstrap2:\r
+\r
+       dojo.platformRequire = function(/*Object containing Arrays*/modMap){\r
+               //      description:\r
+               //              This method taks a "map" of arrays which one can use to optionally\r
+               //              load dojo modules. The map is indexed by the possible\r
+               //              dojo.name_ values, with two additional values: "default"\r
+               //              and "common". The items in the "default" array will be loaded if\r
+               //              none of the other items have been choosen based on the\r
+               //              hostenv.name_ item. The items in the "common" array will _always_\r
+               //              be loaded, regardless of which list is chosen.  Here's how it's\r
+               //              normally called:\r
+               //      \r
+               //              |       dojo.platformRequire({\r
+               //              |               // an example that passes multiple args to _loadModule()\r
+               //              |               browser: [\r
+               //              |                       ["foo.bar.baz", true, true], \r
+               //              |                       "foo.sample",\r
+               //              |                       "foo.test,\r
+               //              |               ],\r
+               //              |               default: [ "foo.sample.*" ],\r
+               //              |               common: [ "really.important.module.*" ]\r
+               //              |       });\r
+\r
+               // FIXME: dojo.name_ no longer works!!\r
+\r
+               var common = modMap.common || [];\r
+               var result = common.concat(modMap[d._name] || modMap["default"] || []);\r
+\r
+               for(var x=0; x<result.length; x++){\r
+                       var curr = result[x];\r
+                       if(curr.constructor == Array){\r
+                               d._loadModule.apply(d, curr);\r
+                       }else{\r
+                               d._loadModule(curr);\r
+                       }\r
+               }\r
+       }\r
+\r
+       dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){\r
+               // summary:\r
+               //              If the condition is true then call dojo.require() for the specified\r
+               //              resource\r
+               if(condition === true){\r
+                       // FIXME: why do we support chained require()'s here? does the build system?\r
+                       var args = [];\r
+                       for(var i = 1; i < arguments.length; i++){ \r
+                               args.push(arguments[i]);\r
+                       }\r
+                       d.require.apply(d, args);\r
+               }\r
+       }\r
+\r
+       dojo.requireAfterIf = d.requireIf;\r
+\r
+       dojo.registerModulePath = function(/*String*/module, /*String*/prefix){\r
+               //      summary: \r
+               //              maps a module name to a path\r
+               //      description: \r
+               //              An unregistered module is given the default path of ../<module>,\r
+               //              relative to Dojo root. For example, module acme is mapped to\r
+               //              ../acme.  If you want to use a different module name, use\r
+               //              dojo.registerModulePath. \r
+               d._modulePrefixes[module] = { name: module, value: prefix };\r
+       }\r
+\r
+       dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){\r
+               // summary:\r
+               //              Declares translated resources and loads them if necessary, in the\r
+               //              same style as dojo.require.  Contents of the resource bundle are\r
+               //              typically strings, but may be any name/value pair, represented in\r
+               //              JSON format.  See also dojo.i18n.getLocalization.\r
+               // moduleName: \r
+               //              name of the package containing the "nls" directory in which the\r
+               //              bundle is found\r
+               // bundleName: \r
+               //              bundle name, i.e. the filename without the '.js' suffix\r
+               // locale: \r
+               //              the locale to load (optional)  By default, the browser's user\r
+               //              locale as defined by dojo.locale\r
+               // availableFlatLocales: \r
+               //              A comma-separated list of the available, flattened locales for this\r
+               //              bundle. This argument should only be set by the build process.\r
+               // description:\r
+               //              Load translated resource bundles provided underneath the "nls"\r
+               //              directory within a package.  Translated resources may be located in\r
+               //              different packages throughout the source tree.  For example, a\r
+               //              particular widget may define one or more resource bundles,\r
+               //              structured in a program as follows, where moduleName is\r
+               //              mycode.mywidget and bundleNames available include bundleone and\r
+               //              bundletwo:\r
+               //\r
+               //                      ...\r
+               //                      mycode/\r
+               //                       mywidget/\r
+               //                        nls/\r
+               //                         bundleone.js (the fallback translation, English in this example)\r
+               //                         bundletwo.js (also a fallback translation)\r
+               //                         de/\r
+               //                          bundleone.js\r
+               //                          bundletwo.js\r
+               //                         de-at/\r
+               //                          bundleone.js\r
+               //                         en/\r
+               //                          (empty; use the fallback translation)\r
+               //                         en-us/\r
+               //                          bundleone.js\r
+               //                         en-gb/\r
+               //                          bundleone.js\r
+               //                         es/\r
+               //                          bundleone.js\r
+               //                          bundletwo.js\r
+               //                        ...etc\r
+               //                      ...\r
+               //\r
+               //              Each directory is named for a locale as specified by RFC 3066,\r
+               //              (http://www.ietf.org/rfc/rfc3066.txt), normalized in lowercase.\r
+               //              Note that the two bundles in the example do not define all the same\r
+               //              variants.  For a given locale, bundles will be loaded for that\r
+               //              locale and all more general locales above it, including a fallback\r
+               //              at the root directory.  For example, a declaration for the "de-at"\r
+               //              locale will first load nls/de-at/bundleone.js, then\r
+               //              nls/de/bundleone.js and finally nls/bundleone.js.  The data will be\r
+               //              flattened into a single Object so that lookups will follow this\r
+               //              cascading pattern.  An optional build step can preload the bundles\r
+               //              to avoid data redundancy and the multiple network hits normally\r
+               //              required to load these resources.\r
+\r
+               d.require("dojo.i18n");\r
+               d.i18n._requireLocalization.apply(d.hostenv, arguments);\r
+       };\r
+\r
+\r
+       var ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$");\r
+       var ire = new RegExp("^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$");\r
+\r
+       dojo._Url = function(/*dojo._Url||String...*/){\r
+               // summary: \r
+               //              Constructor to create an object representing a URL.\r
+               //              It is marked as private, since we might consider removing\r
+               //              or simplifying it.\r
+               // description: \r
+               //              Each argument is evaluated in order relative to the next until\r
+               //              a canonical uri is produced. To get an absolute Uri relative to\r
+               //              the current document use:\r
+               //              new dojo._Url(document.baseURI, url)\r
+\r
+               var n = null;\r
+\r
+               // TODO: support for IPv6, see RFC 2732\r
+               var _a = arguments;\r
+               var uri = _a[0];\r
+               // resolve uri components relative to each other\r
+               for(var i = 1; i<_a.length; i++){\r
+                       if(!_a[i]){ continue; }\r
+\r
+                       // Safari doesn't support this.constructor so we have to be explicit\r
+                       // FIXME: Tracked (and fixed) in Webkit bug 3537.\r
+                       //              http://bugs.webkit.org/show_bug.cgi?id=3537\r
+                       var relobj = new d._Url(_a[i]+"");\r
+                       var uriobj = new d._Url(uri+"");\r
+\r
+                       if(\r
+                               relobj.path == "" &&\r
+                               !relobj.scheme &&\r
+                               !relobj.authority &&\r
+                               !relobj.query\r
+                       ){\r
+                               if(relobj.fragment != n){\r
+                                       uriobj.fragment = relobj.fragment;\r
+                               }\r
+                               relobj = uriobj;\r
+                       }else if(!relobj.scheme){\r
+                               relobj.scheme = uriobj.scheme;\r
+\r
+                               if(!relobj.authority){\r
+                                       relobj.authority = uriobj.authority;\r
+\r
+                                       if(relobj.path.charAt(0) != "/"){\r
+                                               var path = uriobj.path.substring(0,\r
+                                                       uriobj.path.lastIndexOf("/") + 1) + relobj.path;\r
+\r
+                                               var segs = path.split("/");\r
+                                               for(var j = 0; j < segs.length; j++){\r
+                                                       if(segs[j] == "."){\r
+                                                               if(j == segs.length - 1){\r
+                                                                       segs[j] = "";\r
+                                                               }else{\r
+                                                                       segs.splice(j, 1);\r
+                                                                       j--;\r
+                                                               }\r
+                                                       }else if(j > 0 && !(j == 1 && segs[0] == "") &&\r
+                                                               segs[j] == ".." && segs[j-1] != ".."){\r
+\r
+                                                               if(j == (segs.length - 1)){\r
+                                                                       segs.splice(j, 1);\r
+                                                                       segs[j - 1] = "";\r
+                                                               }else{\r
+                                                                       segs.splice(j - 1, 2);\r
+                                                                       j -= 2;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               relobj.path = segs.join("/");\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       uri = "";\r
+                       if(relobj.scheme){ \r
+                               uri += relobj.scheme + ":";\r
+                       }\r
+                       if(relobj.authority){\r
+                               uri += "//" + relobj.authority;\r
+                       }\r
+                       uri += relobj.path;\r
+                       if(relobj.query){\r
+                               uri += "?" + relobj.query;\r
+                       }\r
+                       if(relobj.fragment){\r
+                               uri += "#" + relobj.fragment;\r
+                       }\r
+               }\r
+\r
+               this.uri = uri.toString();\r
+\r
+               // break the uri into its main components\r
+               var r = this.uri.match(ore);\r
+\r
+               this.scheme = r[2] || (r[1] ? "" : n);\r
+               this.authority = r[4] || (r[3] ? "" : n);\r
+               this.path = r[5]; // can never be undefined\r
+               this.query = r[7] || (r[6] ? "" : n);\r
+               this.fragment  = r[9] || (r[8] ? "" : n);\r
+\r
+               if(this.authority != n){\r
+                       // server based naming authority\r
+                       r = this.authority.match(ire);\r
+\r
+                       this.user = r[3] || n;\r
+                       this.password = r[4] || n;\r
+                       this.host = r[5];\r
+                       this.port = r[7] || n;\r
+               }\r
+       }\r
+\r
+       dojo._Url.prototype.toString = function(){ return this.uri; };\r
+\r
+       dojo.moduleUrl = function(/*String*/module, /*dojo._Url||String*/url){\r
+               // summary: \r
+               //              Returns a Url object relative to a module\r
+               //              \r
+               // example: \r
+               //      |       dojo.moduleUrl("dojo.widget","templates/template.html");\r
+               // example:\r
+               //      |       dojo.moduleUrl("acme","images/small.png")\r
+\r
+               var loc = d._getModuleSymbols(module).join('/');\r
+               if(!loc){ return null; }\r
+               if(loc.lastIndexOf("/") != loc.length-1){\r
+                       loc += "/";\r
+               }\r
+               \r
+               //If the path is an absolute path (starts with a / or is on another\r
+               //domain/xdomain) then don't add the baseUrl.\r
+               var colonIndex = loc.indexOf(":");\r
+               if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > loc.indexOf("/"))){\r
+                       loc = d.baseUrl + loc;\r
+               }\r
+\r
+               return new d._Url(loc, url); // String\r
+       }\r
+})();\r
+\r
+//Cross-domain resource loader.\r
+dojo.provide("dojo._base._loader.loader_xd");\r
+\r
+dojo._xdReset = function(){\r
+       //summary: Internal xd loader function. Resets the xd state.\r
+\r
+       //This flag indicates where or not we have crossed into xdomain territory. Once any resource says\r
+       //it is cross domain, then the rest of the resources have to be treated as xdomain because we need\r
+       //to evaluate resources in order. If there is a xdomain resource followed by a xhr resource, we can't load\r
+       //the xhr resource until the one before it finishes loading. The text of the xhr resource will be converted\r
+       //to match the format for a xd resource and put in the xd load queue.\r
+       this._isXDomain = djConfig.useXDomain || false;\r
+\r
+       this._xdTimer = 0;\r
+       this._xdInFlight = {};\r
+       this._xdOrderedReqs = [];\r
+       this._xdDepMap = {};\r
+       this._xdContents = [];\r
+       this._xdDefList = [];\r
+}\r
+\r
+//Call reset immediately to set the state.\r
+dojo._xdReset();\r
+\r
+dojo._xdCreateResource = function(/*String*/contents, /*String*/resourceName, /*String*/resourcePath){\r
+       //summary: Internal xd loader function. Creates an xd module source given an\r
+       //non-xd module contents.\r
+\r
+       //Remove comments. Not perfect, but good enough for dependency resolution.\r
+       var depContents = contents.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg , "");\r
+\r
+       //Find dependencies.\r
+       var deps = [];\r
+    var depRegExp = /dojo.(require|requireIf|provide|requireAfterIf|platformRequire|requireLocalization)\(([\w\W]*?)\)/mg;\r
+    var match;\r
+       while((match = depRegExp.exec(depContents)) != null){\r
+               if(match[1] == "requireLocalization"){\r
+                       //Need to load the local bundles asap, since they are not\r
+                       //part of the list of modules watched for loading.\r
+                       eval(match[0]);\r
+               }else{\r
+                       deps.push('"' + match[1] + '", ' + match[2]);\r
+               }\r
+       }\r
+\r
+       //Create resource object and the call to _xdResourceLoaded.\r
+       var output = [];\r
+       output.push("dojo._xdResourceLoaded({\n");\r
+\r
+       //Add dependencies\r
+       if(deps.length > 0){\r
+               output.push("depends: [");\r
+               for(var i = 0; i < deps.length; i++){\r
+                       if(i > 0){\r
+                               output.push(",\n");\r
+                       }\r
+                       output.push("[" + deps[i] + "]");\r
+               }\r
+               output.push("],");\r
+       }\r
+\r
+       //Add the contents of the file inside a function.\r
+       //Pass in dojo as an argument to the function to help with\r
+       //allowing multiple versions of dojo in a page.\r
+       output.push("\ndefineResource: function(dojo){");\r
+       \r
+       //Don't put in the contents in the debugAtAllCosts case\r
+       //since the contents may have syntax errors. Let those\r
+       //get pushed up when the script tags are added to the page\r
+       //in the debugAtAllCosts case.\r
+       if(!djConfig["debugAtAllCosts"] || resourceName == "dojo._base._loader.loader_debug"){\r
+               output.push(contents);\r
+       }\r
+       //Add isLocal property so we know if we have to do something different\r
+       //in debugAtAllCosts situations.\r
+       output.push("\n}, resourceName: '" + resourceName + "', resourcePath: '" + resourcePath + "'});");\r
+       \r
+       return output.join(""); //String\r
+}\r
+\r
+dojo._xdIsXDomainPath = function(/*string*/relpath) {\r
+    //summary: Figure out whether the path is local or x-domain\r
+       //If there is a colon before the first / then, we have a URL with a protocol.\r
+    \r
+       var colonIndex = relpath.indexOf(":");\r
+       var slashIndex = relpath.indexOf("/");\r
+\r
+       if(colonIndex > 0 && colonIndex < slashIndex){\r
+               return true;\r
+       }else{\r
+               //Is the base script URI-based URL a cross domain URL?\r
+               //If so, then the relpath will be evaluated relative to\r
+               //baseUrl, and therefore qualify as xdomain.\r
+               //Only treat it as xdomain if the page does not have a\r
+               //host (file:// url) or if the baseUrl does not match the\r
+               //current window's domain.\r
+               var url = this.baseUrl;\r
+               colonIndex = url.indexOf(":");\r
+               slashIndex = url.indexOf("/");\r
+               if(colonIndex > 0 && colonIndex < slashIndex && (!location.host || url.indexOf("http://" + location.host) != 0)){\r
+                       return true;\r
+               }\r
+       }\r
+    return false;     \r
+}\r
+\r
+dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){\r
+       //summary: Internal xd loader function. Overrides loadPath() from loader.js.\r
+       //xd loading requires slightly different behavior from loadPath().\r
+\r
+       var currentIsXDomain = this._xdIsXDomainPath(relpath);\r
+    this._isXDomain |= currentIsXDomain;\r
+\r
+       var uri = this.baseUrl + relpath;\r
+       if(currentIsXDomain){\r
+        // check whether the relpath is an absolute URL itself. If so, we \r
+        // ignore baseUrl\r
+       var colonIndex = relpath.indexOf(":");\r
+       var slashIndex = relpath.indexOf("/");\r
+        if(colonIndex > 0 && colonIndex < slashIndex){ \r
+                   uri = relpath;\r
+       }\r
+    }\r
+\r
+       if(djConfig.cacheBust && dojo.isBrowser) { uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,""); }\r
+       try{\r
+               return ((!module || this._isXDomain) ? this._loadUri(uri, cb, currentIsXDomain, module) : this._loadUriAndCheck(uri, module, cb)); //Boolean\r
+       }catch(e){\r
+               console.debug(e);\r
+               return false; //Boolean\r
+       }\r
+}\r
+\r
+dojo._loadUri = function(/*String*/uri, /*Function?*/cb, /*boolean*/currentIsXDomain, /*String?*/module){\r
+       //summary: Internal xd loader function. Overrides loadUri() from loader.js.\r
+       //              xd loading requires slightly different behavior from loadPath().\r
+       //description: Wanted to override getText(), but it is used by\r
+       //              the widget code in too many, synchronous ways right now.\r
+       if(this._loadedUrls[uri]){\r
+               return 1; //Boolean\r
+       }\r
+\r
+       //Add the module (resource) to the list of modules.\r
+       //Only do this work if we have a modlue name. Otherwise, \r
+       //it is a non-xd i18n bundle, which can load immediately and does not \r
+       //need to be tracked. Also, don't track dojo.i18n, since it is a prerequisite\r
+       //and will be loaded correctly if we load it right away: it has no dependencies.\r
+       if(this._isXDomain && module && module != "dojo.i18n"){\r
+               this._xdOrderedReqs.push(module);\r
+\r
+               //Add to waiting resources if it is an xdomain resource.\r
+               //Don't add non-xdomain i18n bundles, those get evaled immediately.\r
+               if(currentIsXDomain || uri.indexOf("/nls/") == -1){\r
+                       this._xdInFlight[module] = true;\r
+\r
+                       //Increment inFlightCount\r
+                       //This will stop the modulesLoaded from firing all the way.\r
+                       this._inFlightCount++;\r
+               }\r
+\r
+               //Start timer\r
+               if(!this._xdTimer){\r
+                       this._xdTimer = setInterval("dojo._xdWatchInFlight();", 100);\r
+               }\r
+               this._xdStartTime = (new Date()).getTime();\r
+       }\r
+\r
+       if (currentIsXDomain){\r
+               //Fix name to be a .xd.fileextension name.\r
+               var lastIndex = uri.lastIndexOf('.');\r
+               if(lastIndex <= 0){\r
+                       lastIndex = uri.length - 1;\r
+               }\r
+\r
+               var xdUri = uri.substring(0, lastIndex) + ".xd";\r
+               if(lastIndex != uri.length - 1){\r
+                       xdUri += uri.substring(lastIndex, uri.length);\r
+               }\r
+\r
+               //Add to script src\r
+               var element = document.createElement("script");\r
+               element.type = "text/javascript";\r
+               element.src = xdUri;\r
+               if(!this.headElement){\r
+                       this._headElement = document.getElementsByTagName("head")[0];\r
+\r
+                       //Head element may not exist, particularly in html\r
+                       //html 4 or tag soup cases where the page does not\r
+                       //have a head tag in it. Use html element, since that will exist.\r
+                       //Seems to be an issue mostly with Opera 9 and to lesser extent Safari 2\r
+                       if(!this._headElement){\r
+                               this._headElement = document.getElementsByTagName("html")[0];\r
+                       }\r
+               }\r
+               this._headElement.appendChild(element);\r
+       }else{\r
+               var contents = this._getText(uri, null, true);\r
+               if(contents == null){ return 0; /*boolean*/}\r
+               \r
+               //If this is not xdomain, or if loading a i18n resource bundle, then send it down\r
+               //the normal eval/callback path.\r
+               if(this._isXDomain\r
+                       && uri.indexOf("/nls/") == -1\r
+                       && module != "dojo.i18n"){\r
+                       var res = this._xdCreateResource(contents, module, uri);\r
+                       dojo.eval(res);\r
+               }else{\r
+                       if(cb){ contents = '('+contents+')'; }\r
+                       var value = dojo.eval(contents);\r
+                       if(cb){\r
+                               cb(value);\r
+                       }\r
+               }\r
+       }\r
+\r
+       //These steps are done in the non-xd loader version of this function.\r
+       //Maintain these steps to fit in with the existing system.\r
+       this._loadedUrls[uri] = true;\r
+       this._loadedUrls.push(uri);\r
+       return true; //Boolean\r
+}\r
+\r
+dojo._xdResourceLoaded = function(/*Object*/res){\r
+       //summary: Internal xd loader function. Called by an xd module resource when\r
+       //it has been loaded via a script tag.\r
+       var deps = res.depends;\r
+       var requireList = null;\r
+       var requireAfterList = null;\r
+       var provideList = [];\r
+       if(deps && deps.length > 0){\r
+               var dep = null;\r
+               var insertHint = 0;\r
+               var attachedResource = false;\r
+               for(var i = 0; i < deps.length; i++){\r
+                       dep = deps[i];\r
+\r
+                       //Look for specific dependency indicators.\r
+                       if (dep[0] == "provide"){\r
+                               provideList.push(dep[1]);\r
+                       }else{\r
+                               if(!requireList){\r
+                                       requireList = [];\r
+                               }\r
+                               if(!requireAfterList){\r
+                                       requireAfterList = [];\r
+                               }\r
+\r
+                               var unpackedDeps = this._xdUnpackDependency(dep);\r
+                               if(unpackedDeps.requires){\r
+                                       requireList = requireList.concat(unpackedDeps.requires);\r
+                               }\r
+                               if(unpackedDeps.requiresAfter){\r
+                                       requireAfterList = requireAfterList.concat(unpackedDeps.requiresAfter);\r
+                               }\r
+                       }\r
+\r
+                       //Call the dependency indicator to allow for the normal dojo setup.\r
+                       //Only allow for one dot reference, for the i18n._preloadLocalizations calls\r
+                       //(and maybe future, one-dot things).\r
+                       var depType = dep[0];\r
+                       var objPath = depType.split(".");\r
+                       if(objPath.length == 2){\r
+                               dojo[objPath[0]][objPath[1]].apply(dojo[objPath[0]], dep.slice(1));\r
+                       }else{\r
+                               dojo[depType].apply(dojo, dep.slice(1));\r
+                       }\r
+               }\r
+\r
+\r
+               //If loading the debugAtAllCosts module, eval it right away since we need\r
+               //its functions to properly load the other modules.\r
+               if(provideList.length == 1 && provideList[0] == "dojo._base._loader.loader_debug"){\r
+                       res.defineResource(dojo);\r
+               }else{\r
+                       //Save off the resource contents for definition later.\r
+                       var contentIndex = this._xdContents.push({\r
+                                       content: res.defineResource,\r
+                                       resourceName: res["resourceName"],\r
+                                       resourcePath: res["resourcePath"],\r
+                                       isDefined: false\r
+                               }) - 1;\r
+       \r
+                       //Add provide/requires to dependency map.\r
+                       for(var i = 0; i < provideList.length; i++){\r
+                               this._xdDepMap[provideList[i]] = { requires: requireList, requiresAfter: requireAfterList, contentIndex: contentIndex };\r
+                       }\r
+               }\r
+\r
+               //Now update the inflight status for any provided resources in this loaded resource.\r
+               //Do this at the very end (in a *separate* for loop) to avoid shutting down the \r
+               //inflight timer check too soon.\r
+               for(var i = 0; i < provideList.length; i++){\r
+                       this._xdInFlight[provideList[i]] = false;\r
+               }\r
+       }\r
+}\r
+\r
+dojo._xdLoadFlattenedBundle = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*Object*/bundleData){\r
+       //summary: Internal xd loader function. Used when loading\r
+       //a flattened localized bundle via a script tag.\r
+       locale = locale || "root";\r
+       var jsLoc = dojo.i18n.normalizeLocale(locale).replace('-', '_');\r
+       var bundleResource = [moduleName, "nls", bundleName].join(".");\r
+       var bundle = dojo["provide"](bundleResource);\r
+       bundle[jsLoc] = bundleData;\r
+       \r
+       //Assign the bundle for the original locale(s) we wanted.\r
+       var mapName = [moduleName, jsLoc, bundleName].join(".");\r
+       var bundleMap = dojo._xdBundleMap[mapName];\r
+       if(bundleMap){\r
+               for(var param in bundleMap){\r
+                       bundle[param] = bundleData;\r
+               }\r
+       }\r
+};\r
+\r
+\r
+dojo._xdInitExtraLocales = function(){\r
+       // Simulate the extra locale work that dojo.requireLocalization does.\r
+\r
+       var extra = djConfig.extraLocale;\r
+       if(extra){\r
+               if(!extra instanceof Array){\r
+                       extra = [extra];\r
+               }\r
+\r
+               dojo._xdReqLoc = dojo.xdRequireLocalization;\r
+               dojo.xdRequireLocalization = function(m, b, locale, fLocales){\r
+                       dojo._xdReqLoc(m,b,locale, fLocales);\r
+                       if(locale){return;}\r
+                       for(var i=0; i<extra.length; i++){\r
+                               dojo._xdReqLoc(m,b,extra[i], fLocales);\r
+                       }\r
+               };\r
+       }\r
+}\r
+\r
+dojo._xdBundleMap = {};\r
+\r
+dojo.xdRequireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String*/availableFlatLocales){\r
+       //summary: Internal xd loader function. The xd version of dojo.requireLocalization.\r
+       \r
+\r
+       //Account for allowing multiple extra locales. Do this here inside the function\r
+       //since dojo._xdInitExtraLocales() depends on djConfig being set up, but that only\r
+       //happens after hostenv_browser runs. loader_xd has to come before hostenv_browser\r
+       //though since hostenv_browser can do a dojo.require for the debug module.\r
+       if(dojo._xdInitExtraLocales){\r
+               dojo._xdInitExtraLocales();\r
+               dojo._xdInitExtraLocales = null;\r
+               dojo.xdRequireLocalization.apply(dojo, arguments);\r
+               return;\r
+       }\r
+\r
+       var locales = availableFlatLocales.split(",");\r
+       \r
+       //Find the best-match locale to load.\r
+       //Assumes dojo.i18n has already been loaded. This is true for xdomain builds,\r
+       //since it is included in dojo.xd.js.\r
+       var jsLoc = dojo.i18n.normalizeLocale(locale);\r
+\r
+       var bestLocale = "";\r
+       for(var i = 0; i < locales.length; i++){\r
+               //Locale must match from start of string.\r
+               if(jsLoc.indexOf(locales[i]) == 0){\r
+                       if(locales[i].length > bestLocale.length){\r
+                               bestLocale = locales[i];\r
+                       }\r
+               }\r
+       }\r
+\r
+       var fixedBestLocale = bestLocale.replace('-', '_');\r
+       //See if the bundle we are going to use is already loaded.\r
+       var bundleResource = dojo.getObject([moduleName, "nls", bundleName].join("."));\r
+       if(bundleResource && bundleResource[fixedBestLocale]){\r
+               bundle[jsLoc.replace('-', '_')] = bundleResource[fixedBestLocale];\r
+       }else{\r
+               //Need to remember what locale we wanted and which one we actually use.\r
+               //Then when we load the one we are actually using, use that bundle for the one\r
+               //we originally wanted.\r
+               var mapName = [moduleName, (fixedBestLocale||"root"), bundleName].join(".");\r
+               var bundleMap = dojo._xdBundleMap[mapName];\r
+               if(!bundleMap){\r
+                       bundleMap = dojo._xdBundleMap[mapName] = {};\r
+               }\r
+               bundleMap[jsLoc.replace('-', '_')] = true;\r
+               \r
+               //Do just a normal dojo.require so the resource tracking stuff works as usual.\r
+               dojo.require(moduleName + ".nls" + (bestLocale ? "." + bestLocale : "") + "." + bundleName);\r
+       }\r
+}\r
+\r
+// Replace dojo.requireLocalization with a wrapper\r
+dojo._xdRealRequireLocalization = dojo.requireLocalization;\r
+dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String*/availableFlatLocales){\r
+    // summary: loads a bundle intelligently based on whether the module is \r
+    // local or xd. Overrides the local-case implementation.\r
+    \r
+    var modulePath = this.moduleUrl(moduleName).toString();\r
+    if (this._xdIsXDomainPath(modulePath)) {\r
+        // call cross-domain loader\r
+        return dojo.xdRequireLocalization.apply(dojo, arguments);\r
+    } else {\r
+        // call local-loader\r
+        return dojo._xdRealRequireLocalization.apply(dojo, arguments);\r
+    }\r
+}\r
+\r
+//This is a bit brittle: it has to know about the dojo methods that deal with dependencies\r
+//It would be ideal to intercept the actual methods and do something fancy at that point,\r
+//but I have concern about knowing which provide to match to the dependency in that case,\r
+//since scripts can load whenever they want, and trigger new calls to dojo._xdResourceLoaded().\r
+dojo._xdUnpackDependency = function(/*Array*/dep){\r
+       //summary: Internal xd loader function. Determines what to do with a dependency\r
+       //that was listed in an xd version of a module contents.\r
+\r
+       //Extract the dependency(ies).\r
+       var newDeps = null;\r
+       var newAfterDeps = null;\r
+       switch(dep[0]){\r
+               case "requireIf":\r
+               case "requireAfterIf":\r
+                       //First arg (dep[1]) is the test. Depedency is dep[2].\r
+                       if(dep[1] === true){\r
+                               newDeps = [{name: dep[2], content: null}];\r
+                       }\r
+                       break;\r
+               case "platformRequire":\r
+                       var modMap = dep[1];\r
+                       var common = modMap["common"]||[];\r
+                       var newDeps = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);      \r
+                       //Flatten the array of arrays into a one-level deep array.\r
+                       //Each result could be an array of 3 elements  (the 3 arguments to dojo.require).\r
+                       //We only need the first one.\r
+                       if(newDeps){\r
+                               for(var i = 0; i < newDeps.length; i++){\r
+                                       if(newDeps[i] instanceof Array){\r
+                                               newDeps[i] = {name: newDeps[i][0], content: null};\r
+                                       }else{\r
+                                               newDeps[i] = {name: newDeps[i], content: null};\r
+                                       }\r
+                               }\r
+                       }\r
+                       break;\r
+               case "require":\r
+                       //Just worry about dep[1]\r
+                       newDeps = [{name: dep[1], content: null}];\r
+                       break;\r
+               case "i18n._preloadLocalizations":\r
+                       //We can eval these immediately, since they load i18n bundles.\r
+                       //Since i18n bundles have no dependencies, whenever they are loaded\r
+                       //in a script tag, they are evaluated immediately, so we do not have to\r
+                       //treat them has an explicit dependency for the dependency mapping.\r
+                       //We can call it immediately since dojo.i18n is part of dojo.xd.js.\r
+                       dojo.i18n._preloadLocalizations.apply(dojo.i18n._preloadLocalizations, dep.slice(1));\r
+                       break;\r
+       }\r
+\r
+       //The requireIf and requireAfterIf needs to be evaluated after the current resource is evaluated.\r
+       if(dep[0] == "requireAfterIf" || dep[0] == "requireIf"){\r
+               newAfterDeps = newDeps;\r
+               newDeps = null;\r
+       }\r
+       return {requires: newDeps, requiresAfter: newAfterDeps}; //Object\r
+}\r
+\r
+dojo._xdWalkReqs = function(){\r
+       //summary: Internal xd loader function. \r
+       //Walks the requires and evaluates module resource contents in\r
+       //the right order.\r
+       var reqChain = null;\r
+       var req;\r
+       for(var i = 0; i < this._xdOrderedReqs.length; i++){\r
+               req = this._xdOrderedReqs[i];\r
+               if(this._xdDepMap[req]){\r
+                       reqChain = [req];\r
+                       reqChain[req] = true; //Allow for fast lookup of the req in the array\r
+                       this._xdEvalReqs(reqChain);\r
+               }\r
+       }\r
+}\r
+\r
+dojo._xdEvalReqs = function(/*Array*/reqChain){\r
+       //summary: Internal xd loader function. \r
+       //Does a depth first, breadth second search and eval of required modules.\r
+       while(reqChain.length > 0){\r
+               var req = reqChain[reqChain.length - 1];\r
+               var res = this._xdDepMap[req];\r
+               if(res){\r
+                       //Trace down any requires for this resource.\r
+                       //START dojo._xdTraceReqs() inlining for small Safari 2.0 call stack\r
+                       var reqs = res.requires;\r
+                       if(reqs && reqs.length > 0){\r
+                               var nextReq;\r
+                               for(var i = 0; i < reqs.length; i++){\r
+                                       nextReq = reqs[i].name;\r
+                                       if(nextReq && !reqChain[nextReq]){\r
+                                               //New req depedency. Follow it down.\r
+                                               reqChain.push(nextReq);\r
+                                               reqChain[nextReq] = true;\r
+                                               this._xdEvalReqs(reqChain);\r
+                                       }\r
+                               }\r
+                       }\r
+                       //END dojo._xdTraceReqs() inlining for small Safari 2.0 call stack\r
+\r
+                       //Evaluate the resource.\r
+                       var contents = this._xdContents[res.contentIndex];\r
+                       if(!contents.isDefined){\r
+                               var content = contents.content;\r
+                               content["resourceName"] = contents["resourceName"];\r
+                               content["resourcePath"] = contents["resourcePath"];\r
+                               this._xdDefList.push(content);\r
+                               contents.isDefined = true;\r
+                       }\r
+                       this._xdDepMap[req] = null;\r
+\r
+                       //Trace down any requireAfters for this resource.\r
+                       //START dojo._xdTraceReqs() inlining for small Safari 2.0 call stack\r
+                       var reqs = res.requiresAfter;\r
+                       if(reqs && reqs.length > 0){\r
+                               var nextReq;\r
+                               for(var i = 0; i < reqs.length; i++){\r
+                                       nextReq = reqs[i].name;\r
+                                       if(nextReq && !reqChain[nextReq]){\r
+                                               //New req depedency. Follow it down.\r
+                                               reqChain.push(nextReq);\r
+                                               reqChain[nextReq] = true;\r
+                                               this._xdEvalReqs(reqChain);\r
+                                       }\r
+                               }\r
+                       }\r
+                       //END dojo._xdTraceReqs() inlining for small Safari 2.0 call stack\r
+               }\r
+\r
+               //Done with that require. Remove it and go to the next one.\r
+               reqChain.pop();\r
+       }\r
+}\r
+\r
+dojo._xdClearInterval = function(){\r
+       //summary: Internal xd loader function.\r
+       //Clears the interval timer used to check on the\r
+       //status of in-flight xd module resource requests.\r
+       clearInterval(this._xdTimer);\r
+       this._xdTimer = 0;\r
+}\r
+\r
+dojo._xdWatchInFlight = function(){\r
+       //summary: Internal xd loader function.\r
+       //Monitors in-flight requests for xd module resources.\r
+\r
+       var noLoads = "";\r
+       var waitInterval = (djConfig.xdWaitSeconds || 15) * 1000;\r
+       var expired = (this._xdStartTime + waitInterval) < (new Date()).getTime();\r
+\r
+       //If any xdInFlight are true, then still waiting for something to load.\r
+       //Come back later. If we timed out, report the things that did not load.\r
+       for(var param in this._xdInFlight){\r
+               if(this._xdInFlight[param] === true){\r
+                       if(expired){\r
+                               noLoads += param + " ";\r
+                       }else{\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+\r
+       //All done. Clean up and notify.\r
+       this._xdClearInterval();\r
+\r
+       if(expired){\r
+               throw "Could not load cross-domain resources: " + noLoads;\r
+       }\r
+\r
+       this._xdWalkReqs();\r
+       \r
+       var defLength = this._xdDefList.length;\r
+       for(var i= 0; i < defLength; i++){\r
+               var content = dojo._xdDefList[i];\r
+               if(djConfig["debugAtAllCosts"] && content["resourceName"]){\r
+                       if(!this["_xdDebugQueue"]){\r
+                               this._xdDebugQueue = [];\r
+                       }\r
+                       this._xdDebugQueue.push({resourceName: content.resourceName, resourcePath: content.resourcePath});\r
+               }else{\r
+                       //Evaluate the resource to bring it into being.\r
+                       //Pass dojo in so that later, to support multiple versions of dojo\r
+                       //in a page, we can pass which version of dojo to use.  \r
+                       content(dojo);\r
+               }\r
+       }\r
+\r
+       //Evaluate any resources that were not evaled before.\r
+       //This normally shouldn't happen with proper dojo.provide and dojo.require\r
+       //usage, but providing it just in case. Note that these may not be executed\r
+       //in the original order that the developer intended.\r
+       //Pass dojo in so that later, to support multiple versions of dojo\r
+       //in a page, we can pass which version of dojo to use.\r
+       for(var i = 0; i < this._xdContents.length; i++){\r
+               var current = this._xdContents[i];\r
+               if(current.content && !current.isDefined){\r
+                       current.content(dojo);\r
+               }\r
+       }\r
+\r
+       //Clean up for the next round of xd loading.\r
+       this._xdReset();\r
+\r
+       if(this["_xdDebugQueue"] && this._xdDebugQueue.length > 0){\r
+               this._xdDebugFileLoaded();\r
+       }else{\r
+               this._xdNotifyLoaded();\r
+       }\r
+}\r
+\r
+dojo._xdNotifyLoaded = function(){\r
+       //Clear inflight count so we will finally do finish work.\r
+       this._inFlightCount = 0; \r
+       \r
+       //Only trigger call loaded if dj_load_init has run. \r
+       if(this._initFired && !this._loadNotifying){ \r
+               this._callLoaded();\r
+       }\r
+}\r
+\r
+/*=====\r
+dojo.isBrowser = {\r
+       // summary: True if the client is a web-browser\r
+       // example:\r
+       //      if(dojo.isBrowser){ ... }\r
+};\r
+\r
+dojo.isFF = {\r
+       // summary: True if client is using FireFox browser. False otherwise.\r
+       // example:\r
+       //      if(dojo.isFF && dojo.isFF > 1){ ... }\r
+};\r
+\r
+dojo.isIE = {\r
+       // summary: The major version if client it using Internet Explorer (or false) \r
+       // example:\r
+       //      if(dojo.isIE && dojo.isIE > 6){\r
+       //              // we are IE7\r
+       //  }\r
+};\r
+\r
+dojo.isKhtml = {\r
+       // summary: True if client is  using Khtml browsers (konqueror, et al)\r
+};\r
+\r
+dojo.isMozilla = {\r
+       // summary: True if client is using a Mozilla-based browser. \r
+};\r
+\r
+dojo.isOpera = {\r
+       // summary: True if client is using the Opera web browser\r
+};\r
+\r
+dojo.isSafari = {\r
+       // summary: True if client is using the Safari web browser\r
+       // example:\r
+       //      |       if(dojo.isSafari){ ... }\r
+       //\r
+       // example: \r
+       //      Detect iPhone:\r
+       //      |       if(dojo.isSafari && (navigator.userAgent.indexOf("iPhone") < 0)){ \r
+       //      |               // we are iPhone. iPod touch reports "iPod" above\r
+       //      |       }\r
+};\r
+=====*/\r
+\r
+if(typeof window != 'undefined'){\r
+       dojo.isBrowser = true;\r
+       dojo._name = "browser";\r
+\r
+\r
+       // attempt to figure out the path to dojo if it isn't set in the config\r
+       (function(){\r
+               var d = dojo;\r
+               // this is a scope protection closure. We set browser versions and grab\r
+               // the URL we were loaded from here.\r
+\r
+               // grab the node we were loaded from\r
+               if(document && document.getElementsByTagName){\r
+                       var scripts = document.getElementsByTagName("script");\r
+                       var rePkg = /dojo(\.xd)?\.js([\?\.]|$)/i;\r
+                       for(var i = 0; i < scripts.length; i++){\r
+                               var src = scripts[i].getAttribute("src");\r
+                               if(!src){ continue; }\r
+                               var m = src.match(rePkg);\r
+                               if(m){\r
+                                       // find out where we came from\r
+                                       if(!djConfig["baseUrl"]){\r
+                                               djConfig["baseUrl"] = src.substring(0, m.index);\r
+                                       }\r
+                                       // and find out if we need to modify our behavior\r
+                                       var cfg = scripts[i].getAttribute("djConfig");\r
+                                       if(cfg){\r
+                                               var cfgo = eval("({ "+cfg+" })");\r
+                                               for(var x in cfgo){\r
+                                                       djConfig[x] = cfgo[x];\r
+                                               }\r
+                                       }\r
+                                       break; // "first Dojo wins"\r
+                               }\r
+                       }\r
+               }\r
+               d.baseUrl = djConfig["baseUrl"];\r
+\r
+               // fill in the rendering support information in dojo.render.*\r
+               var n = navigator;\r
+               var dua = n.userAgent;\r
+               var dav = n.appVersion;\r
+               var tv = parseFloat(dav);\r
+\r
+               d.isOpera = (dua.indexOf("Opera") >= 0) ? tv : 0;\r
+               d.isKhtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0) ? tv : 0;\r
+               if(dav.indexOf("Safari") >= 0){\r
+                       d.isSafari = parseFloat(dav.split("Version/")[1]) || 2;\r
+               }\r
+               var geckoPos = dua.indexOf("Gecko");\r
+               d.isMozilla = d.isMoz = ((geckoPos >= 0)&&(!d.isKhtml)) ? tv : 0;\r
+               d.isFF = 0;\r
+               d.isIE = 0;\r
+               try{\r
+                       if(d.isMoz){\r
+                               d.isFF = parseFloat(dua.split("Firefox/")[1].split(" ")[0]);\r
+                       }\r
+                       if((document.all)&&(!d.isOpera)){\r
+                               d.isIE = parseFloat(dav.split("MSIE ")[1].split(";")[0]);\r
+                       }\r
+               }catch(e){}\r
+\r
+               //Workaround to get local file loads of dojo to work on IE 7\r
+               //by forcing to not use native xhr.\r
+               if(dojo.isIE && (window.location.protocol === "file:")){\r
+                       djConfig.ieForceActiveXXhr=true;\r
+               }\r
+\r
+               var cm = document["compatMode"];\r
+               d.isQuirks = (cm == "BackCompat")||(cm == "QuirksMode")||(d.isIE < 6);\r
+\r
+               // TODO: is the HTML LANG attribute relevant?\r
+               d.locale = djConfig.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase();\r
+\r
+               d._println = console.debug;\r
+\r
+               // These are in order of decreasing likelihood; this will change in time.\r
+               d._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];\r
+\r
+               d._xhrObj= function(){\r
+                       // summary: \r
+                       //              does the work of portably generating a new XMLHTTPRequest\r
+                       //              object.\r
+                       var http = null;\r
+                       var last_e = null;\r
+                       if(!dojo.isIE || !djConfig.ieForceActiveXXhr){\r
+                               try{ http = new XMLHttpRequest(); }catch(e){}\r
+                       }\r
+                       if(!http){\r
+                               for(var i=0; i<3; ++i){\r
+                                       var progid = dojo._XMLHTTP_PROGIDS[i];\r
+                                       try{\r
+                                               http = new ActiveXObject(progid);\r
+                                       }catch(e){\r
+                                               last_e = e;\r
+                                       }\r
+\r
+                                       if(http){\r
+                                               dojo._XMLHTTP_PROGIDS = [progid];  // so faster next time\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if(!http){\r
+                               throw new Error("XMLHTTP not available: "+last_e);\r
+                       }\r
+\r
+                       return http; // XMLHTTPRequest instance\r
+               }\r
+\r
+               d._isDocumentOk = function(http){\r
+                       var stat = http.status || 0;\r
+                       return ( (stat>=200)&&(stat<300))||     // Boolean\r
+                               (stat==304)||                                           // allow any 2XX response code\r
+                               (stat==1223)||                                          // get it out of the cache\r
+                               (!stat && (location.protocol=="file:" || location.protocol=="chrome:") ); // Internet Explorer mangled the status code\r
+               }\r
+\r
+               //See if base tag is in use.\r
+               //This is to fix http://trac.dojotoolkit.org/ticket/3973,\r
+               //but really, we need to find out how to get rid of the dojo._Url reference\r
+               //below and still have DOH work with the dojo.i18n test following some other\r
+               //test that uses the test frame to load a document (trac #2757).\r
+               //Opera still has problems, but perhaps a larger issue of base tag support\r
+               //with XHR requests (hasBase is true, but the request is still made to document\r
+               //path, not base path).\r
+               var owloc = window.location+"";\r
+               var base = document.getElementsByTagName("base");\r
+               var hasBase = (base && base.length > 0);\r
+\r
+               d._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){\r
+                       // summary: Read the contents of the specified uri and return those contents.\r
+                       // uri:\r
+                       //              A relative or absolute uri. If absolute, it still must be in\r
+                       //              the same "domain" as we are.\r
+                       // fail_ok:\r
+                       //              Default false. If fail_ok and loading fails, return null\r
+                       //              instead of throwing.\r
+                       // returns: The response text. null is returned when there is a\r
+                       //              failure and failure is okay (an exception otherwise)\r
+\r
+                       // alert("_getText: " + uri);\r
+\r
+                       // NOTE: must be declared before scope switches ie. this._xhrObj()\r
+                       var http = this._xhrObj();\r
+\r
+                       if(!hasBase && dojo._Url){\r
+                               uri = (new dojo._Url(owloc, uri)).toString();\r
+                       }\r
+                       /*\r
+                       console.debug("_getText:", uri);\r
+                       console.debug(window.location+"");\r
+                       alert(uri);\r
+                       */\r
+\r
+                       http.open('GET', uri, false);\r
+                       try{\r
+                               http.send(null);\r
+                               // alert(http);\r
+                               if(!d._isDocumentOk(http)){\r
+                                       var err = Error("Unable to load "+uri+" status:"+ http.status);\r
+                                       err.status = http.status;\r
+                                       err.responseText = http.responseText;\r
+                                       throw err;\r
+                               }\r
+                       }catch(e){\r
+                               if(fail_ok){ return null; } // null\r
+                               // rethrow the exception\r
+                               throw e;\r
+                       }\r
+                       return http.responseText; // String\r
+               }\r
+       })();\r
+\r
+       dojo._initFired = false;\r
+       //      BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)\r
+       dojo._loadInit = function(e){\r
+               dojo._initFired = true;\r
+               // allow multiple calls, only first one will take effect\r
+               // A bug in khtml calls events callbacks for document for event which isnt supported\r
+               // for example a created contextmenu event calls DOMContentLoaded, workaround\r
+               var type = (e && e.type) ? e.type.toLowerCase() : "load";\r
+               if(arguments.callee.initialized || (type!="domcontentloaded" && type!="load")){ return; }\r
+               arguments.callee.initialized = true;\r
+               if(typeof dojo["_khtmlTimer"] != 'undefined'){\r
+                       clearInterval(dojo._khtmlTimer);\r
+                       delete dojo._khtmlTimer;\r
+               }\r
+\r
+               if(dojo._inFlightCount == 0){\r
+                       dojo._modulesLoaded();\r
+               }\r
+       }\r
+\r
+       //      START DOMContentLoaded\r
+       // Mozilla and Opera 9 expose the event we could use\r
+       if(document.addEventListener){\r
+               // NOTE: \r
+               //              due to a threading issue in Firefox 2.0, we can't enable\r
+               //              DOMContentLoaded on that platform. For more information, see:\r
+               //              http://trac.dojotoolkit.org/ticket/1704\r
+               if(dojo.isOpera|| (dojo.isMoz && (djConfig["enableMozDomContentLoaded"] === true))){\r
+                       document.addEventListener("DOMContentLoaded", dojo._loadInit, null);\r
+               }\r
+\r
+               //      mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.\r
+               //  also used for Mozilla because of trac #1640\r
+               window.addEventListener("load", dojo._loadInit, null);\r
+       }\r
+\r
+       if(/(WebKit|khtml)/i.test(navigator.userAgent)){ // sniff\r
+               dojo._khtmlTimer = setInterval(function(){\r
+                       if(/loaded|complete/.test(document.readyState)){\r
+                               dojo._loadInit(); // call the onload handler\r
+                       }\r
+               }, 10);\r
+       }\r
+       //      END DOMContentLoaded\r
+\r
+       (function(){\r
+\r
+               var _w = window;\r
+               var _handleNodeEvent = function(/*String*/evtName, /*Function*/fp){\r
+                       // summary:\r
+                       //              non-destructively adds the specified function to the node's\r
+                       //              evtName handler.\r
+                       // evtName: should be in the form "onclick" for "onclick" handlers.\r
+                       // Make sure you pass in the "on" part.\r
+                       var oldHandler = _w[evtName] || function(){};\r
+                       _w[evtName] = function(){\r
+                               fp.apply(_w, arguments);\r
+                               oldHandler.apply(_w, arguments);\r
+                       }\r
+               }\r
+\r
+               if(dojo.isIE){\r
+                       //      for Internet Explorer. readyState will not be achieved on init\r
+                       //      call, but dojo doesn't need it however, we'll include it\r
+                       //      because we don't know if there are other functions added that\r
+                       //      might.  Note that this has changed because the build process\r
+                       //      strips all comments -- including conditional ones.\r
+\r
+                       document.write('<scr'+'ipt defer src="//:" '\r
+                               + 'onreadystatechange="if(this.readyState==\'complete\'){dojo._loadInit();}">'\r
+                               + '</scr'+'ipt>'\r
+                       );\r
+\r
+                       // IE WebControl hosted in an application can fire "beforeunload" and "unload"\r
+                       // events when control visibility changes, causing Dojo to unload too soon. The\r
+                       // following code fixes the problem\r
+                       // Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155\r
+                       var _unloading = true;\r
+                       _handleNodeEvent("onbeforeunload", function(){\r
+                               _w.setTimeout(function(){ _unloading = false; }, 0);\r
+                       });\r
+                       _handleNodeEvent("onunload", function(){\r
+                               if(_unloading){ dojo.unloaded(); }\r
+                       });\r
+\r
+                       try{\r
+                               document.namespaces.add("v","urn:schemas-microsoft-com:vml");\r
+                               document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");\r
+                       }catch(e){}\r
+               }else{\r
+                       // FIXME: dojo.unloaded requires dojo scope, so using anon function wrapper.\r
+                       _handleNodeEvent("onbeforeunload", function() { dojo.unloaded(); });\r
+               }\r
+\r
+       })();\r
+\r
+       /*\r
+       OpenAjax.subscribe("OpenAjax", "onload", function(){\r
+               if(dojo._inFlightCount == 0){\r
+                       dojo._modulesLoaded();\r
+               }\r
+       });\r
+\r
+       OpenAjax.subscribe("OpenAjax", "onunload", function(){\r
+               dojo.unloaded();\r
+       });\r
+       */\r
+} //if (typeof window != 'undefined')\r
+\r
+//Load debug code if necessary.\r
+// dojo.requireIf((djConfig["isDebug"] || djConfig["debugAtAllCosts"]), "dojo.debug");\r
+\r
+//window.widget is for Dashboard detection\r
+//The full conditionals are spelled out to avoid issues during builds.\r
+//Builds may be looking for require/requireIf statements and processing them.\r
+// dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && !djConfig["useXDomain"], "dojo.browser_debug");\r
+// dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && djConfig["useXDomain"], "dojo.browser_debug_xd");\r
+\r
+if(djConfig.isDebug){\r
+               dojo.require("dojo._firebug.firebug");\r
+}\r
+\r
+if(djConfig.debugAtAllCosts){\r
+       djConfig.useXDomain = true;\r
+       dojo.require("dojo._base._loader.loader_xd");\r
+       dojo.require("dojo._base._loader.loader_debug");\r
+       \r
+}\r
+\r
+};\r
+\r
+if(!dojo._hasResource["dojo._base.lang"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.lang"] = true;\r
+dojo.provide("dojo._base.lang");\r
+\r
+// Crockford (ish) functions\r
+\r
+dojo.isString = function(/*anything*/ it){\r
+       // summary:     Return true if it is a String\r
+       return typeof it == "string" || it instanceof String; // Boolean\r
+}\r
+\r
+dojo.isArray = function(/*anything*/ it){\r
+       // summary: Return true if it is an Array\r
+       return it && it instanceof Array || typeof it == "array"; // Boolean\r
+}\r
+\r
+/*=====\r
+dojo.isFunction = function(it){\r
+       // summary: Return true if it is a Function\r
+       // it: anything\r
+}\r
+=====*/\r
+\r
+dojo.isFunction = (function(){\r
+       var _isFunction = function(/*anything*/ it){\r
+               return typeof it == "function" || it instanceof Function; // Boolean\r
+       };\r
+\r
+       return dojo.isSafari ?\r
+               // only slow this down w/ gratuitious casting in Safari since it's what's b0rken\r
+               function(/*anything*/ it){\r
+                       if(typeof it == "function" && it == "[object NodeList]"){ return false; }\r
+                       return _isFunction(it); // Boolean\r
+               } : _isFunction;\r
+})();\r
+\r
+dojo.isObject = function(/*anything*/ it){\r
+       // summary: \r
+       //              Returns true if it is a JavaScript object (or an Array, a Function or null)\r
+       return it !== undefined &&\r
+               (it === null || typeof it == "object" || dojo.isArray(it) || dojo.isFunction(it)); // Boolean\r
+}\r
+\r
+dojo.isArrayLike = function(/*anything*/ it){\r
+       //      summary:\r
+       //              similar to dojo.isArray() but more permissive\r
+       //      description:\r
+       //              Doesn't strongly test for "arrayness".  Instead, settles for "isn't\r
+       //              a string or number and has a length property". Arguments objects\r
+       //              and DOM collections will return true when passed to\r
+       //              dojo.isArrayLike(), but will return false when passed to\r
+       //              dojo.isArray().\r
+       //      return:\r
+       //              If it walks like a duck and quicks like a duck, return true\r
+       var d = dojo;\r
+       return it && it !== undefined &&\r
+               // keep out built-in constructors (Number, String, ...) which have length\r
+               // properties\r
+               !d.isString(it) && !d.isFunction(it) &&\r
+               !(it.tagName && it.tagName.toLowerCase() == 'form') &&\r
+               (d.isArray(it) || isFinite(it.length)); // Boolean\r
+}\r
+\r
+dojo.isAlien = function(/*anything*/ it){\r
+       // summary: \r
+       //              Returns true if it is a built-in function or some other kind of\r
+       //              oddball that *should* report as a function but doesn't\r
+       return it && !dojo.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean\r
+}\r
+\r
+dojo.extend = function(/*Object*/ constructor, /*Object...*/ props){\r
+       // summary:\r
+       //              Adds all properties and methods of props to constructor's\r
+       //              prototype, making them available to all instances created with\r
+       //              constructor.\r
+       for(var i=1, l=arguments.length; i<l; i++){\r
+               dojo._mixin(constructor.prototype, arguments[i]);\r
+       }\r
+       return constructor; // Object\r
+}\r
+\r
+dojo._hitchArgs = function(scope, method /*,...*/){\r
+       var pre = dojo._toArray(arguments, 2);\r
+       var named = dojo.isString(method);\r
+       return function(){\r
+               // arrayify arguments\r
+               var args = dojo._toArray(arguments);\r
+               // locate our method\r
+               var f = named ? (scope||dojo.global)[method] : method;\r
+               // invoke with collected args\r
+               return f && f.apply(scope || this, pre.concat(args)); // mixed\r
+       } // Function\r
+}\r
+\r
+dojo.hitch = function(/*Object*/scope, /*Function|String*/method /*,...*/){\r
+       // summary: \r
+       //              Returns a function that will only ever execute in the a given scope. \r
+       //              This allows for easy use of object member functions\r
+       //              in callbacks and other places in which the "this" keyword may\r
+       //              otherwise not reference the expected scope. \r
+       //              Any number of default positional arguments may be passed as parameters \r
+       //              beyond "method".\r
+       //              Each of these values will be used to "placehold" (similar to curry)\r
+       //              for the hitched function. \r
+       // scope: \r
+       //              The scope to use when method executes. If method is a string, \r
+       //              scope is also the object containing method.\r
+       // method:\r
+       //              A function to be hitched to scope, or the name of the method in\r
+       //              scope to be hitched.\r
+       // example:\r
+       //      |       dojo.hitch(foo, "bar")(); \r
+       //              runs foo.bar() in the scope of foo\r
+       // example:\r
+       //      |       dojo.hitch(foo, myFunction);\r
+       //              returns a function that runs myFunction in the scope of foo\r
+       if(arguments.length > 2){\r
+               return dojo._hitchArgs.apply(dojo, arguments); // Function\r
+       }\r
+       if(!method){\r
+               method = scope;\r
+               scope = null;\r
+       }\r
+       if(dojo.isString(method)){\r
+               scope = scope || dojo.global;\r
+               if(!scope[method]){ throw(['dojo.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); }\r
+               return function(){ return scope[method].apply(scope, arguments || []); }; // Function\r
+       }\r
+       return !scope ? method : function(){ return method.apply(scope, arguments || []); }; // Function\r
+}\r
+\r
+/*=====\r
+dojo.delegate = function(obj, props){\r
+       //      summary:\r
+       //              returns a new object which "looks" to obj for properties which it\r
+       //              does not have a value for. Optionally takes a bag of properties to\r
+       //              seed the returned object with initially. \r
+       //      description:\r
+       //              This is a small implementaton of the Boodman/Crockford delegation\r
+       //              pattern in JavaScript. An intermediate object constructor mediates\r
+       //              the prototype chain for the returned object, using it to delegate\r
+       //              down to obj for property lookup when object-local lookup fails.\r
+       //              This can be thought of similarly to ES4's "wrap", save that it does\r
+       //              not act on types but rather on pure objects.\r
+       //      obj:\r
+       //              The object to delegate to for properties not found directly on the\r
+       //              return object or in props.\r
+       //      props:\r
+       //              an object containing properties to assign to the returned object\r
+       //      returns:\r
+       //              an Object of anonymous type\r
+       //      example:\r
+       //      |       var foo = { bar: "baz" };\r
+       //      |       var thinger = dojo.delegate(foo, { thud: "xyzzy"});\r
+       //      |       thinger.bar == "baz"; // delegated to foo\r
+       //      |       foo.xyzzy == undefined; // by definition\r
+       //      |       thinger.xyzzy == "xyzzy"; // mixed in from props\r
+       //      |       foo.bar = "thonk";\r
+       //      |       thinger.bar == "thonk"; // still delegated to foo's bar\r
+}\r
+=====*/\r
+\r
+\r
+dojo.delegate = dojo._delegate = function(obj, props){\r
+\r
+       // boodman/crockford delegation\r
+       function TMP(){};\r
+       TMP.prototype = obj;\r
+       var tmp = new TMP();\r
+       if(props){\r
+               dojo.mixin(tmp, props);\r
+       }\r
+       return tmp; // Object\r
+}\r
+\r
+dojo.partial = function(/*Function|String*/method /*, ...*/){\r
+       // summary:\r
+       //              similar to hitch() except that the scope object is left to be\r
+       //              whatever the execution context eventually becomes.\r
+       //      description:\r
+       //              Calling dojo.partial is the functional equivalent of calling:\r
+       //              |       dojo.hitch(null, funcName, ...);\r
+       var arr = [ null ];\r
+       return dojo.hitch.apply(dojo, arr.concat(dojo._toArray(arguments))); // Function\r
+}\r
+\r
+dojo._toArray = function(/*Object*/obj, /*Number?*/offset, /*Array?*/ startWith){\r
+       // summary:\r
+       //              Converts an array-like object (i.e. arguments, DOMCollection)\r
+       //              to an array. Returns a new Array object.\r
+       // obj:\r
+       //              the object to "arrayify". We expect the object to have, at a\r
+       //              minimum, a length property which corresponds to integer-indexed\r
+       //              properties.\r
+       // offset:\r
+       //              the location in obj to start iterating from. Defaults to 0. Optional.\r
+       // startWith:\r
+       //              An array to pack with the properties of obj. If provided,\r
+       //              properties in obj are appended at the end of startWith and\r
+       //              startWith is the returned array.\r
+       var arr = startWith||[];\r
+       for(var x = offset || 0; x < obj.length; x++){\r
+               arr.push(obj[x]);\r
+       }\r
+       return arr; // Array\r
+}\r
+\r
+dojo.clone = function(/*anything*/ o){\r
+       // summary:\r
+       //              Clones objects (including DOM nodes) and all children.\r
+       //              Warning: do not clone cyclic structures.\r
+       if(!o){ return o; }\r
+       if(dojo.isArray(o)){\r
+               var r = [];\r
+               for(var i = 0; i < o.length; ++i){\r
+                       r.push(dojo.clone(o[i]));\r
+               }\r
+               return r; // Array\r
+       }\r
+       if(!dojo.isObject(o)){\r
+               return o;       /*anything*/\r
+       }\r
+       if(o.nodeType && o.cloneNode){ // isNode\r
+               return o.cloneNode(true); // Node\r
+       }\r
+       if(o instanceof Date){\r
+               return new Date(o.getTime());   // Date\r
+       }\r
+       // Generic objects\r
+       var r = new o.constructor(); // specific to dojo.declare()'d classes!\r
+       for(var i in o){\r
+               if(!(i in r) || r[i] != o[i]){\r
+                       r[i] = dojo.clone(o[i]);\r
+               }\r
+       }\r
+       return r; // Object\r
+}\r
+\r
+dojo.trim = function(/*String*/ str){\r
+       // summary: \r
+       //              trims whitespaces from both sides of the string\r
+       // description:\r
+       //              This version of trim() was selected for inclusion into the base due\r
+       //              to its compact size and relatively good performance (see Steven\r
+       //              Levithan's blog:\r
+       //              http://blog.stevenlevithan.com/archives/faster-trim-javascript).\r
+       //              The fastest but longest version of this function is located at\r
+       //              dojo.string.trim()\r
+       return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // String\r
+}\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.declare"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.declare"] = true;\r
+dojo.provide("dojo._base.declare");\r
+\r
+\r
+// this file courtesy of the TurboAjax group, licensed under a Dojo CLA\r
+\r
+dojo.declare = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){\r
+       //      summary: \r
+       //              Create a feature-rich constructor from compact notation\r
+       //      className:\r
+       //              The name of the constructor (loosely, a "class")\r
+       //              stored in the "declaredClass" property in the created prototype\r
+       //      superclass:\r
+       //              May be null, a Function, or an Array of Functions. If an array, \r
+       //              the first element is used as the prototypical ancestor and\r
+       //              any following Functions become mixin ancestors.\r
+       //      props:\r
+       //              An object whose properties are copied to the\r
+       //              created prototype.\r
+       //              Add an instance-initialization function by making it a property \r
+       //              named "constructor".\r
+       //      description:\r
+       //              Create a constructor using a compact notation for inheritance and\r
+       //              prototype extension. \r
+       //\r
+       //              All superclasses (including mixins) must be Functions (not simple Objects).\r
+       //\r
+       //              Mixin ancestors provide a type of multiple inheritance. Prototypes of mixin \r
+       //              ancestors are copied to the new class: changes to mixin prototypes will\r
+       //              not affect classes to which they have been mixed in.\r
+       //\r
+       //              "className" is cached in "declaredClass" property of the new class.\r
+       //\r
+       //      example:\r
+       //      |       dojo.declare("my.classes.bar", my.classes.foo, {\r
+       //      |               // properties to be added to the class prototype\r
+       //      |               someValue: 2,\r
+       //      |               // initialization function\r
+       //      |               constructor: function(){\r
+       //      |                       this.myComplicatedObject = new ReallyComplicatedObject(); \r
+       //      |               },\r
+       //      |               // other functions\r
+       //      |               someMethod: function(){ \r
+       //      |                       doStuff(); \r
+       //      |               }\r
+       //      |       );\r
+\r
+       // argument juggling (deprecated)\r
+       if(dojo.isFunction(props)||(arguments.length>3)){ \r
+               dojo.deprecated("dojo.declare: for class '" + className + "' pass initializer function as 'constructor' property instead of as a separate argument.", "", "1.0");\r
+               var c = props;\r
+               props = arguments[3] || {};\r
+               props.constructor = c;\r
+       }\r
+       // process superclass argument\r
+       // var dd=dojo.declare, mixins=null;\r
+       var dd=arguments.callee, mixins=null;\r
+       if(dojo.isArray(superclass)){\r
+               mixins = superclass;\r
+               superclass = mixins.shift();\r
+       }\r
+       // construct intermediate classes for mixins\r
+       if(mixins){\r
+               for(var i=0, m; i<mixins.length; i++){\r
+                       m = mixins[i];\r
+                       if(!m){throw("Mixin #" + i + " to declaration of " + className + " is null. It's likely a required module is not loaded.")};\r
+                       superclass = dd._delegate(superclass, m);\r
+               }\r
+       }\r
+       // prepare values\r
+       var init=(props||0).constructor, ctor=dd._delegate(superclass), fn;\r
+       // name methods (experimental)\r
+       for(var i in props){if(dojo.isFunction(fn=props[i])&&(!0[i])){fn.nom=i;}}\r
+       // decorate prototype\r
+       dojo.extend(ctor, {declaredClass: className, _constructor: init, preamble: null}, props||0); \r
+       // special help for IE\r
+       ctor.prototype.constructor = ctor;\r
+       // create named reference\r
+       return dojo.setObject(className, ctor); // Function\r
+}\r
+\r
+dojo.mixin(dojo.declare, {\r
+       _delegate: function(base, mixin){\r
+               var bp = (base||0).prototype, mp = (mixin||0).prototype;\r
+               // fresh constructor, fresh prototype\r
+               var ctor = dojo.declare._makeCtor();\r
+               // cache ancestry\r
+               dojo.mixin(ctor, {superclass: bp, mixin: mp, extend: dojo.declare._extend});\r
+               // chain prototypes\r
+               if(base){ctor.prototype = dojo._delegate(bp);}\r
+               // add mixin and core\r
+               dojo.extend(ctor, dojo.declare._core, mp||0, {_constructor: null, preamble: null});\r
+               // special help for IE\r
+               ctor.prototype.constructor = ctor;\r
+               // name this class for debugging\r
+               ctor.prototype.declaredClass = (bp||0).declaredClass + '_' + (mp||0).declaredClass;\r
+               return ctor;\r
+       },\r
+       _extend: function(props){\r
+               for(var i in props){if(dojo.isFunction(fn=props[i])&&(!0[i])){fn.nom=i;}}\r
+               dojo.extend(this, props);\r
+       },\r
+       _makeCtor: function(){\r
+               // we have to make a function, but don't want to close over anything\r
+               return function(){ this._construct(arguments); }\r
+       },\r
+       _core: { \r
+               _construct: function(args){\r
+                       var c=args.callee, s=c.superclass, ct=s&&s.constructor, m=c.mixin, mct=m&&m.constructor, a=args, ii, fn;\r
+                       // side-effect of = used on purpose here, lint may complain, don't try this at home\r
+                       if(a[0]){ \r
+                               // FIXME: preambles for each mixin should be allowed\r
+                               // FIXME: \r
+                               //              should we allow the preamble here NOT to modify the\r
+                               //              default args, but instead to act on each mixin\r
+                               //              independently of the class instance being constructed\r
+                               //              (for impdedence matching)?\r
+\r
+                               // allow any first argument w/ a "preamble" property to act as a\r
+                               // class preamble (not exclusive of the prototype preamble)\r
+                               if(/*dojo.isFunction*/(fn = a[0]["preamble"])){ \r
+                                       a = fn.apply(this, a) || a; \r
+                               }\r
+                       } \r
+                       // prototype preamble\r
+                       if(fn=c.prototype.preamble){a = fn.apply(this, a) || a;}\r
+                       // FIXME: \r
+                       //              need to provide an optional prototype-settable\r
+                       //              "_explicitSuper" property which disables this\r
+                       // initialize superclass\r
+                       if(ct&&ct.apply){ct.apply(this, a);}\r
+                       // initialize mixin\r
+                       if(mct&&mct.apply){mct.apply(this, a);}\r
+                       // initialize self\r
+                       if(ii=c.prototype._constructor){ii.apply(this, args);}\r
+                       // post construction\r
+                       if(this.constructor.prototype==c.prototype && (ct=this.postscript)){ct.apply(this, args)};\r
+               },\r
+               _findMixin: function(mixin){\r
+                       var c = this.constructor, p, m;\r
+                       while(c){\r
+                               p = c.superclass;\r
+                               m = c.mixin;\r
+                               if(m==mixin || (m instanceof mixin.constructor)){return p;}\r
+                               if(m && (m=m._findMixin(mixin))){return m;}\r
+                               c = p && p.constructor;\r
+                       }\r
+               },\r
+               _findMethod: function(name, method, ptype, has){\r
+                       // consciously trading readability for bytes and speed in this low-level method\r
+                       var p=ptype, c, m, f;\r
+                       do{\r
+                               c = p.constructor;\r
+                               m = c.mixin;\r
+                               // find method by name in our mixin ancestor\r
+                               if(m && (m=this._findMethod(name, method, m, has))){return m};\r
+                               // if we found a named method that either exactly-is or exactly-is-not 'method'\r
+                               if((f=p[name])&&(has==(f==method))){return p};\r
+                               // ascend chain\r
+                               p = c.superclass;\r
+                       }while(p);\r
+                       // if we couldn't find an ancestor in our primary chain, try a mixin chain\r
+                       return !has && (p=this._findMixin(ptype)) && this._findMethod(name, method, p, has);\r
+               },\r
+               inherited: function(name, args, newArgs){\r
+                       // optionalize name argument (experimental)\r
+                       var a = arguments;\r
+                       if(!dojo.isString(a[0])){newArgs=args; args=name; name=args.callee.nom;}\r
+                       var c=args.callee, p=this.constructor.prototype, a=newArgs||args, fn, mp;\r
+                       // if not an instance override \r
+                       if(this[name]!=c || p[name]==c){\r
+                               mp = this._findMethod(name, c, p, true);\r
+                               if(!mp){throw(this.declaredClass + ': name argument ("' + name + '") to inherited must match callee (declare.js)');}\r
+                               p = this._findMethod(name, c, mp, false);\r
+                       }\r
+                       fn = p && p[name];\r
+                       // FIXME: perhaps we should throw here? \r
+                       if(!fn){console.debug(mp.declaredClass + ': no inherited "' + name + '" was found (declare.js)'); return;}\r
+                       // if the function exists, invoke it in our scope\r
+                       return fn.apply(this, a);\r
+               }\r
+       }\r
+});\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.connect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.connect"] = true;\r
+dojo.provide("dojo._base.connect");\r
+\r
+\r
+// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA\r
+\r
+// low-level delegation machinery\r
+dojo._listener = {\r
+       // create a dispatcher function\r
+       getDispatcher: function(){\r
+               // following comments pulled out-of-line to prevent cloning them \r
+               // in the returned function.\r
+               // - indices (i) that are really in the array of listeners (ls) will \r
+               //   not be in Array.prototype. This is the 'sparse array' trick\r
+               //   that keeps us safe from libs that take liberties with built-in \r
+               //   objects\r
+               // - listener is invoked with current scope (this)\r
+               return function(){\r
+                       var ap=Array.prototype, c=arguments.callee, ls=c._listeners, t=c.target;\r
+                       // return value comes from original target function\r
+                       var r=t && t.apply(this, arguments);\r
+                       // invoke listeners after target function\r
+                       for(var i in ls){\r
+                               if(!(i in ap)){\r
+                                       ls[i].apply(this, arguments);\r
+                               }\r
+                       }\r
+                       // return value comes from original target function\r
+                       return r;\r
+               }\r
+       },\r
+       // add a listener to an object\r
+       add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){\r
+               // Whenever 'method' is invoked, 'listener' will have the same scope.\r
+               // Trying to supporting a context object for the listener led to \r
+               // complexity. \r
+               // Non trivial to provide 'once' functionality here\r
+               // because listener could be the result of a dojo.hitch call,\r
+               // in which case two references to the same hitch target would not\r
+               // be equivalent. \r
+               source = source || dojo.global;\r
+               // The source method is either null, a dispatcher, or some other function\r
+               var f = source[method];\r
+               // Ensure a dispatcher\r
+               if(!f||!f._listeners){\r
+                       var d = dojo._listener.getDispatcher();\r
+                       // original target function is special\r
+                       d.target = f;\r
+                       // dispatcher holds a list of listeners\r
+                       d._listeners = []; \r
+                       // redirect source to dispatcher\r
+                       f = source[method] = d;\r
+               }\r
+               // The contract is that a handle is returned that can \r
+               // identify this listener for disconnect. \r
+               //\r
+               // The type of the handle is private. Here is it implemented as Integer. \r
+               // DOM event code has this same contract but handle is Function \r
+               // in non-IE browsers.\r
+               //\r
+               // We could have separate lists of before and after listeners.\r
+               return f._listeners.push(listener) ; /*Handle*/\r
+       },\r
+       // remove a listener from an object\r
+       remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){\r
+               var f = (source||dojo.global)[method];\r
+               // remember that handle is the index+1 (0 is not a valid handle)\r
+               if(f && f._listeners && handle--){\r
+                       delete f._listeners[handle]; \r
+               }\r
+       }\r
+};\r
+\r
+// Multiple delegation for arbitrary methods.\r
+\r
+// This unit knows nothing about DOM, \r
+// but we include DOM aware \r
+// documentation and dontFix\r
+// argument here to help the autodocs.\r
+// Actual DOM aware code is in event.js.\r
+\r
+dojo.connect = function(/*Object|null*/ obj, \r
+                                               /*String*/ event, \r
+                                               /*Object|null*/ context, \r
+                                               /*String|Function*/ method,\r
+                                               /*Boolean*/ dontFix){\r
+       // summary:\r
+       //              Create a link that calls one function when another executes. \r
+       //\r
+       // description:\r
+       //              Connects method to event, so that after event fires, method\r
+       //              does too. All connected functions are passed the same arguments as\r
+       //              the event function was initially called with. You may connect as\r
+       //              many methods to event as needed.\r
+       //\r
+       //              event must be a string. If obj is null, dojo.global is used.\r
+       //\r
+       //              null arguments may simply be omitted.\r
+       //\r
+       //              obj[event] can resolve to a function or undefined (null). \r
+       //              If obj[event] is null, it is assigned a function.\r
+       //\r
+       //              The return value is a handle that is needed to \r
+       //              remove this connection with dojo.disconnect.\r
+       //\r
+       // obj: \r
+       //              The source object for the event function. \r
+       //              Defaults to dojo.global if null.\r
+       //              If obj is a DOM node, the connection is delegated \r
+       //              to the DOM event manager (unless dontFix is true).\r
+       //\r
+       // event:\r
+       //              String name of the event function in obj. \r
+       //              I.e. identifies a property obj[event].\r
+       //\r
+       // context: \r
+       //              The object that method will receive as "this".\r
+       //\r
+       //              If context is null and method is a function, then method\r
+       //              inherits the context of event.\r
+       //      \r
+       //              If method is a string then context must be the source \r
+       //              object object for method (context[method]). If context is null,\r
+       //              dojo.global is used.\r
+       //\r
+       // method:\r
+       //              A function reference, or name of a function in context. \r
+       //              The function identified by method fires after event does. \r
+       //              method receives the same arguments as the event.\r
+       //              See context argument comments for information on method's scope.\r
+       //\r
+       // dontFix:\r
+       //              If obj is a DOM node, set dontFix to true to prevent delegation \r
+       //              of this connection to the DOM event manager. \r
+       //\r
+       // example:\r
+       //              When obj.onchange(), do ui.update():\r
+       //      |       dojo.connect(obj, "onchange", ui, "update");\r
+       //      |       dojo.connect(obj, "onchange", ui, ui.update); // same\r
+       //\r
+       // example:\r
+       //              Using return value for disconnect:\r
+       //      |       var link = dojo.connect(obj, "onchange", ui, "update");\r
+       //      |       ...\r
+       //      |       dojo.disconnect(link);\r
+       //\r
+       // example:\r
+       //              When onglobalevent executes, watcher.handler is invoked:\r
+       //      |       dojo.connect(null, "onglobalevent", watcher, "handler");\r
+       //\r
+       // example:\r
+       //              When ob.onCustomEvent executes, customEventHandler is invoked:\r
+       //      |       dojo.connect(ob, "onCustomEvent", null, "customEventHandler");\r
+       //      |       dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same\r
+       //\r
+       // example:\r
+       //              When ob.onCustomEvent executes, customEventHandler is invoked\r
+       //              with the same scope (this):\r
+       //      |       dojo.connect(ob, "onCustomEvent", null, customEventHandler);\r
+       //      |       dojo.connect(ob, "onCustomEvent", customEventHandler); // same\r
+       //\r
+       // example:\r
+       //              When globalEvent executes, globalHandler is invoked\r
+       //              with the same scope (this):\r
+       //      |       dojo.connect(null, "globalEvent", null, globalHandler);\r
+       //      |       dojo.connect("globalEvent", globalHandler); // same\r
+\r
+       // normalize arguments\r
+       var a=arguments, args=[], i=0;\r
+       // if a[0] is a String, obj was ommited\r
+       args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]);\r
+       // if the arg-after-next is a String or Function, context was NOT omitted\r
+       var a1 = a[i+1];\r
+       args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]);\r
+       // absorb any additional arguments\r
+       for(var l=a.length; i<l; i++){  args.push(a[i]); }\r
+       // do the actual work\r
+       return dojo._connect.apply(this, args); /*Handle*/\r
+}\r
+\r
+// used by non-browser hostenvs. always overriden by event.js\r
+dojo._connect = function(obj, event, context, method){\r
+       var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method)); \r
+       return [obj, event, h, l]; // Handle\r
+}\r
+\r
+dojo.disconnect = function(/*Handle*/ handle){\r
+       // summary:\r
+       //              Remove a link created by dojo.connect.\r
+       // description:\r
+       //              Removes the connection between event and the method referenced by handle.\r
+       // handle:\r
+       //              the return value of the dojo.connect call that created the connection.\r
+       if(handle && handle[0] !== undefined){\r
+               dojo._disconnect.apply(this, handle);\r
+               // let's not keep this reference\r
+               delete handle[0];\r
+       }\r
+}\r
+\r
+dojo._disconnect = function(obj, event, handle, listener){\r
+       listener.remove(obj, event, handle);\r
+}\r
+\r
+// topic publish/subscribe\r
+\r
+dojo._topics = {};\r
+\r
+dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){\r
+       //      summary:\r
+       //              Attach a listener to a named topic. The listener function is invoked whenever the\r
+       //              named topic is published (see: dojo.publish).\r
+       //              Returns a handle which is needed to unsubscribe this listener.\r
+       //      context:\r
+       //              Scope in which method will be invoked, or null for default scope.\r
+       //      method:\r
+       //              The name of a function in context, or a function reference. This is the function that\r
+       //              is invoked when topic is published.\r
+       //      example:\r
+       //      |       dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };\r
+       //      |       dojo.publish("alerts", [ "read this", "hello world" ]);                                                                                                                                 \r
+\r
+       // support for 2 argument invocation (omitting context) depends on hitch\r
+       return [topic, dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method))]; /*Handle*/\r
+}\r
+\r
+dojo.unsubscribe = function(/*Handle*/ handle){\r
+       //      summary:\r
+       //              Remove a topic listener. \r
+       //      handle:\r
+       //              The handle returned from a call to subscribe.\r
+       //      example:\r
+       //      |       var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };\r
+       //      |       ...\r
+       //      |       dojo.unsubscribe(alerter);\r
+       if(handle){\r
+               dojo._listener.remove(dojo._topics, handle[0], handle[1]);\r
+       }\r
+}\r
+\r
+dojo.publish = function(/*String*/ topic, /*Array*/ args){\r
+       //      summary:\r
+       //              Invoke all listener method subscribed to topic.\r
+       //      topic:\r
+       //              The name of the topic to publish.\r
+       //      args:\r
+       //              An array of arguments. The arguments will be applied \r
+       //              to each topic subscriber (as first class parameters, via apply).\r
+       //      example:\r
+       //      |       dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };\r
+       //      |       dojo.publish("alerts", [ "read this", "hello world" ]); \r
+\r
+       // Note that args is an array, which is more efficient vs variable length\r
+       // argument list.  Ideally, var args would be implemented via Array\r
+       // throughout the APIs.\r
+       var f = dojo._topics[topic];\r
+       if(f){\r
+               f.apply(this, args||[]);\r
+       }\r
+}\r
+\r
+dojo.connectPublisher = function(      /*String*/ topic, \r
+                                                                       /*Object|null*/ obj, \r
+                                                                       /*String*/ event){\r
+       //      summary:\r
+       //              Ensure that everytime obj.event() is called, a message is published\r
+       //              on the topic. Returns a handle which can be passed to\r
+       //              dojo.disconnect() to disable subsequent automatic publication on\r
+       //              the topic.\r
+       //      topic:\r
+       //              The name of the topic to publish.\r
+       //      obj: \r
+       //              The source object for the event function. Defaults to dojo.global\r
+       //              if null.\r
+       //      event:\r
+       //              The name of the event function in obj. \r
+       //              I.e. identifies a property obj[event].\r
+       //      example:\r
+       //      |       dojo.connectPublisher("/ajax/start", dojo, "xhrGet"};\r
+       var pf = function(){ dojo.publish(topic, arguments); }\r
+       return (event) ? dojo.connect(obj, event, pf) : dojo.connect(obj, pf); //Handle\r
+};\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.Deferred"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.Deferred"] = true;\r
+dojo.provide("dojo._base.Deferred");\r
+\r
+\r
+dojo.Deferred = function(/*Function?*/ canceller){\r
+       // summary:\r
+       //              Encapsulates a sequence of callbacks in response to a value that\r
+       //              may not yet be available.  This is modeled after the Deferred class\r
+       //              from Twisted <http://twistedmatrix.com>.\r
+       // description:\r
+       //              JavaScript has no threads, and even if it did, threads are hard.\r
+       //              Deferreds are a way of abstracting non-blocking events, such as the\r
+       //              final response to an XMLHttpRequest. Deferreds create a promise to\r
+       //              return a response a some point in the future and an easy way to\r
+       //              register your interest in receiving that response.\r
+       //\r
+       //              The most important methods for Deffered users are:\r
+       //\r
+       //                      * addCallback(handler)\r
+       //                      * addErrback(handler)\r
+       //                      * callback(result)\r
+       //                      * errback(result)\r
+       //\r
+       //              In general, when a function returns a Deferred, users then "fill\r
+       //              in" the second half of the contract by registering callbacks and\r
+       //              error handlers. You may register as many callback and errback\r
+       //              handlers as you like and they will be executed in the order\r
+       //              registered when a result is provided. Usually this result is\r
+       //              provided as the result of an asynchronous operation. The code\r
+       //              "managing" the Deferred (the code that made the promise to provide\r
+       //              an answer later) will use the callback() and errback() methods to\r
+       //              communicate with registered listeners about the result of the\r
+       //              operation. At this time, all registered result handlers are called\r
+       //              *with the most recent result value*.\r
+       //\r
+       //              Deferred callback handlers are treated as a chain, and each item in\r
+       //              the chain is required to return a value that will be fed into\r
+       //              successive handlers. The most minimal callback may be registered\r
+       //              like this:\r
+       //\r
+       //              |       var d = new dojo.Deferred();\r
+       //              |       d.addCallback(function(result){ return result; });\r
+       //\r
+       //              Perhaps the most common mistake when first using Deferreds is to\r
+       //              forget to return a value (in most cases, the value you were\r
+       //              passed).\r
+       //\r
+       //              The sequence of callbacks is internally represented as a list of\r
+       //              2-tuples containing the callback/errback pair.  For example, the\r
+       //              following call sequence:\r
+       //              \r
+       //              |       var d = new dojo.Deferred();\r
+       //              |       d.addCallback(myCallback);\r
+       //              |       d.addErrback(myErrback);\r
+       //              |       d.addBoth(myBoth);\r
+       //              |       d.addCallbacks(myCallback, myErrback);\r
+       //\r
+       //              is translated into a Deferred with the following internal\r
+       //              representation:\r
+       //\r
+       //              |       [\r
+       //              |               [myCallback, null],\r
+       //              |               [null, myErrback],\r
+       //              |               [myBoth, myBoth],\r
+       //              |               [myCallback, myErrback]\r
+       //              |       ]\r
+       //\r
+       //              The Deferred also keeps track of its current status (fired).  Its\r
+       //              status may be one of three things:\r
+       //\r
+       //                      * -1: no value yet (initial condition)\r
+       //                      * 0: success\r
+       //                      * 1: error\r
+       //      \r
+       //              A Deferred will be in the error state if one of the following three\r
+       //              conditions are met:\r
+       //\r
+       //                      1. The result given to callback or errback is "instanceof" Error\r
+       //                      2. The previous callback or errback raised an exception while\r
+       //                         executing\r
+       //                      3. The previous callback or errback returned a value\r
+       //                         "instanceof" Error\r
+       //\r
+       //              Otherwise, the Deferred will be in the success state. The state of\r
+       //              the Deferred determines the next element in the callback sequence\r
+       //              to run.\r
+       //\r
+       //              When a callback or errback occurs with the example deferred chain,\r
+       //              something equivalent to the following will happen (imagine\r
+       //              that exceptions are caught and returned):\r
+       //\r
+       //              |       // d.callback(result) or d.errback(result)\r
+       //              |       if(!(result instanceof Error)){\r
+       //              |               result = myCallback(result);\r
+       //              |       }\r
+       //              |       if(result instanceof Error){\r
+       //              |               result = myErrback(result);\r
+       //              |       }\r
+       //              |       result = myBoth(result);\r
+       //              |       if(result instanceof Error){\r
+       //              |               result = myErrback(result);\r
+       //              |       }else{\r
+       //              |               result = myCallback(result);\r
+       //              |       }\r
+       //\r
+       //              The result is then stored away in case another step is added to the\r
+       //              callback sequence.      Since the Deferred already has a value\r
+       //              available, any new callbacks added will be called immediately.\r
+       //\r
+       //              There are two other "advanced" details about this implementation\r
+       //              that are useful:\r
+       //\r
+       //              Callbacks are allowed to return Deferred instances themselves, so\r
+       //              you can build complicated sequences of events with ease.\r
+       //\r
+       //              The creator of the Deferred may specify a canceller.  The canceller\r
+       //              is a function that will be called if Deferred.cancel is called\r
+       //              before the Deferred fires. You can use this to implement clean\r
+       //              aborting of an XMLHttpRequest, etc. Note that cancel will fire the\r
+       //              deferred with a CancelledError (unless your canceller returns\r
+       //              another kind of error), so the errbacks should be prepared to\r
+       //              handle that error for cancellable Deferreds.\r
+       // example:\r
+       //      |       var deferred = new dojo.Deferred();\r
+       //      |       setTimeout(function(){ deferred.callback({success: true}); }, 1000);\r
+       //      |       return deferred;\r
+       // example:\r
+       //              Deferred objects are often used when making code asynchronous. It\r
+       //              may be easiest to write functions in a synchronous manner and then\r
+       //              split code using a deferred to trigger a response to a long-lived\r
+       //              operation. For example, instead of register a callback function to\r
+       //              denote when a rendering operation completes, the function can\r
+       //              simply return a deferred:\r
+       //\r
+       //              |       // callback style:\r
+       //              |       function renderLotsOfData(data, callback){\r
+       //              |               var success = false\r
+       //              |               try{\r
+       //              |                       for(var x in data){\r
+       //              |                               renderDataitem(data[x]);\r
+       //              |                       }\r
+       //              |                       success = true;\r
+       //              |               }catch(e){ }\r
+       //              |               if(callback){\r
+       //              |                       callback(success);\r
+       //              |               }\r
+       //              |       }\r
+       //\r
+       //              |       // using callback style\r
+       //              |       renderLotsOfData(someDataObj, function(success){\r
+       //              |               // handles success or failure\r
+       //              |               if(!success){\r
+       //              |                       promptUserToRecover();\r
+       //              |               }\r
+       //              |       });\r
+       //              |       // NOTE: no way to add another callback here!!\r
+       // example:\r
+       //              Using a Deferred doesn't simplify the sending code any, but it\r
+       //              provides a standard interface for callers and senders alike,\r
+       //              providing both with a simple way to service multiple callbacks for\r
+       //              an operation and freeing both sides from worrying about details\r
+       //              such as "did this get called already?". With Deferreds, new\r
+       //              callbacks can be added at any time.\r
+       //\r
+       //              |       // Deferred style:\r
+       //              |       function renderLotsOfData(data){\r
+       //              |               var d = new dojo.Deferred();\r
+       //              |               try{\r
+       //              |                       for(var x in data){\r
+       //              |                               renderDataitem(data[x]);\r
+       //              |                       }\r
+       //              |                       d.callback(true);\r
+       //              |               }catch(e){ \r
+       //              |                       d.errback(new Error("rendering failed"));\r
+       //              |               }\r
+       //              |               return d;\r
+       //              |       }\r
+       //\r
+       //              |       // using Deferred style\r
+       //              |       renderLotsOfData(someDataObj).addErrback(function(){\r
+       //              |               promptUserToRecover();\r
+       //              |       });\r
+       //              |       // NOTE: addErrback and addCallback both return the Deferred\r
+       //              |       // again, so we could chain adding callbacks or save the\r
+       //              |       // deferred for later should we need to be notified again.\r
+       // example:\r
+       //              In this example, renderLotsOfData is syncrhonous and so both\r
+       //              versions are pretty artificial. Putting the data display on a\r
+       //              timeout helps show why Deferreds rock:\r
+       //\r
+       //              |       // Deferred style and async func\r
+       //              |       function renderLotsOfData(data){\r
+       //              |               var d = new dojo.Deferred();\r
+       //              |               setTimeout(function(){\r
+       //              |                       try{\r
+       //              |                               for(var x in data){\r
+       //              |                                       renderDataitem(data[x]);\r
+       //              |                               }\r
+       //              |                               d.callback(true);\r
+       //              |                       }catch(e){ \r
+       //              |                               d.errback(new Error("rendering failed"));\r
+       //              |                       }\r
+       //              |               }, 100);\r
+       //              |               return d;\r
+       //              |       }\r
+       //\r
+       //              |       // using Deferred style\r
+       //              |       renderLotsOfData(someDataObj).addErrback(function(){\r
+       //              |               promptUserToRecover();\r
+       //              |       });\r
+       //\r
+       //              Note that the caller doesn't have to change his code at all to\r
+       //              handle the asynchronous case.\r
+\r
+       this.chain = [];\r
+       this.id = this._nextId();\r
+       this.fired = -1;\r
+       this.paused = 0;\r
+       this.results = [null, null];\r
+       this.canceller = canceller;\r
+       this.silentlyCancelled = false;\r
+};\r
+\r
+dojo.extend(dojo.Deferred, {\r
+       /*\r
+       makeCalled: function(){\r
+               // summary:\r
+               //              returns a new, empty deferred, which is already in the called\r
+               //              state. Calling callback() or errback() on this deferred will\r
+               //              yeild an error and adding new handlers to it will result in\r
+               //              them being called immediately.\r
+               var deferred = new dojo.Deferred();\r
+               deferred.callback();\r
+               return deferred;\r
+       },\r
+\r
+       toString: function(){\r
+               var state;\r
+               if(this.fired == -1){\r
+                       state = 'unfired';\r
+               }else{\r
+                       state = this.fired ? 'success' : 'error';\r
+               }\r
+               return 'Deferred(' + this.id + ', ' + state + ')';\r
+       },\r
+       */\r
+\r
+       _nextId: (function(){\r
+               var n = 1;\r
+               return function(){ return n++; };\r
+       })(),\r
+\r
+       cancel: function(){\r
+               // summary:     \r
+               //              Cancels a Deferred that has not yet received a value, or is\r
+               //              waiting on another Deferred as its value.\r
+               // description:\r
+               //              If a canceller is defined, the canceller is called. If the\r
+               //              canceller did not return an error, or there was no canceller,\r
+               //              then the errback chain is started.\r
+               var err;\r
+               if(this.fired == -1){\r
+                       if(this.canceller){\r
+                               err = this.canceller(this);\r
+                       }else{\r
+                               this.silentlyCancelled = true;\r
+                       }\r
+                       if(this.fired == -1){\r
+                               if(!(err instanceof Error)){\r
+                                       var res = err;\r
+                                       err = new Error("Deferred Cancelled");\r
+                                       err.dojoType = "cancel";\r
+                                       err.cancelResult = res;\r
+                               }\r
+                               this.errback(err);\r
+                       }\r
+               }else if(       (this.fired == 0) &&\r
+                                       (this.results[0] instanceof dojo.Deferred)\r
+               ){\r
+                       this.results[0].cancel();\r
+               }\r
+       },\r
+                       \r
+\r
+       _resback: function(res){\r
+               // summary:\r
+               //              The private primitive that means either callback or errback\r
+               this.fired = ((res instanceof Error) ? 1 : 0);\r
+               this.results[this.fired] = res;\r
+               this._fire();\r
+       },\r
+\r
+       _check: function(){\r
+               if(this.fired != -1){\r
+                       if(!this.silentlyCancelled){\r
+                               throw new Error("already called!");\r
+                       }\r
+                       this.silentlyCancelled = false;\r
+                       return;\r
+               }\r
+       },\r
+\r
+       callback: function(res){\r
+               // summary:     Begin the callback sequence with a non-error value.\r
+               \r
+               /*\r
+               callback or errback should only be called once on a given\r
+               Deferred.\r
+               */\r
+               this._check();\r
+               this._resback(res);\r
+       },\r
+\r
+       errback: function(/*Error*/res){\r
+               // summary: \r
+               //              Begin the callback sequence with an error result.\r
+               this._check();\r
+               if(!(res instanceof Error)){\r
+                       res = new Error(res);\r
+               }\r
+               this._resback(res);\r
+       },\r
+\r
+       addBoth: function(/*Function||Object*/cb, /*Optional, String*/cbfn){\r
+               // summary:\r
+               //              Add the same function as both a callback and an errback as the\r
+               //              next element on the callback sequence.  This is useful for code\r
+               //              that you want to guarantee to run, e.g. a finalizer.\r
+               var enclosed = dojo.hitch(cb, cbfn);\r
+               if(arguments.length > 2){\r
+                       enclosed = dojo.partial(enclosed, arguments, 2);\r
+               }\r
+               return this.addCallbacks(enclosed, enclosed);\r
+       },\r
+\r
+       addCallback: function(cb, cbfn){\r
+               // summary: \r
+               //              Add a single callback to the end of the callback sequence.\r
+               var enclosed = dojo.hitch(cb, cbfn);\r
+               if(arguments.length > 2){\r
+                       enclosed = dojo.partial(enclosed, arguments, 2);\r
+               }\r
+               return this.addCallbacks(enclosed, null);\r
+       },\r
+\r
+       addErrback: function(cb, cbfn){\r
+               // summary: \r
+               //              Add a single callback to the end of the callback sequence.\r
+               var enclosed = dojo.hitch(cb, cbfn);\r
+               if(arguments.length > 2){\r
+                       enclosed = dojo.partial(enclosed, arguments, 2);\r
+               }\r
+               return this.addCallbacks(null, enclosed);\r
+       },\r
+\r
+       addCallbacks: function(cb, eb){\r
+               // summary: \r
+               //              Add separate callback and errback to the end of the callback\r
+               //              sequence.\r
+               this.chain.push([cb, eb])\r
+               if(this.fired >= 0){\r
+                       this._fire();\r
+               }\r
+               return this;\r
+       },\r
+\r
+       _fire: function(){\r
+               // summary: \r
+               //              Used internally to exhaust the callback sequence when a result\r
+               //              is available.\r
+               var chain = this.chain;\r
+               var fired = this.fired;\r
+               var res = this.results[fired];\r
+               var self = this;\r
+               var cb = null;\r
+               while(\r
+                       (chain.length > 0) &&\r
+                       (this.paused == 0)\r
+               ){\r
+                       // Array\r
+                       var f = chain.shift()[fired];\r
+                       if(!f){ continue; }\r
+                       try{\r
+                               res = f(res);\r
+                               fired = ((res instanceof Error) ? 1 : 0);\r
+                               if(res instanceof dojo.Deferred){\r
+                                       cb = function(res){\r
+                                               self._resback(res);\r
+                                               // inlined from _pause()\r
+                                               self.paused--;\r
+                                               if(\r
+                                                       (self.paused == 0) && \r
+                                                       (self.fired >= 0)\r
+                                               ){\r
+                                                       self._fire();\r
+                                               }\r
+                                       }\r
+                                       // inlined from _unpause\r
+                                       this.paused++;\r
+                               }\r
+                       }catch(err){\r
+                               console.debug(err);\r
+                               fired = 1;\r
+                               res = err;\r
+                       }\r
+               }\r
+               this.fired = fired;\r
+               this.results[fired] = res;\r
+               if((cb)&&(this.paused)){\r
+                       // this is for "tail recursion" in case the dependent\r
+                       // deferred is already fired\r
+                       res.addBoth(cb);\r
+               }\r
+       }\r
+});\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.json"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.json"] = true;\r
+dojo.provide("dojo._base.json");\r
+\r
+dojo.fromJson = function(/*String*/ json){\r
+       // summary:\r
+       //              evaluates the passed string-form of a JSON object\r
+       // json: \r
+       //              a string literal of a JSON item, for instance:\r
+       //                      '{ "foo": [ "bar", 1, { "baz": "thud" } ] }'\r
+       // return:\r
+       //              An object, the result of the evaluation\r
+\r
+       // FIXME: should this accept mozilla's optional second arg?\r
+       try {\r
+               return eval("(" + json + ")");\r
+       }catch(e){\r
+               console.debug(e);\r
+               return json; //FIXME: peller: we should document this behavior and/or throw instead.  I prefer the latter.\r
+       }\r
+}\r
+\r
+dojo._escapeString = function(/*String*/str){\r
+       //summary:\r
+       //              Adds escape sequences for non-visual characters, double quote and\r
+       //              backslash and surrounds with double quotes to form a valid string\r
+       //              literal.\r
+       return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'\r
+               ).replace(/[\f]/g, "\\f"\r
+               ).replace(/[\b]/g, "\\b"\r
+               ).replace(/[\n]/g, "\\n"\r
+               ).replace(/[\t]/g, "\\t"\r
+               ).replace(/[\r]/g, "\\r"); // string\r
+}\r
+\r
+dojo.toJsonIndentStr = "\t";\r
+dojo.toJson = function(/*Object*/ it, /*Boolean?*/ prettyPrint, /*String?*/ _indentStr){\r
+       // summary:\r
+       //              Create a JSON serialization of an object. \r
+       //              Note that this doesn't check for infinite recursion, so don't do that!\r
+       //\r
+       // it:\r
+       //              an object to be serialized. Objects may define their own\r
+       //              serialization via a special "__json__" or "json" function\r
+       //              property. If a specialized serializer has been defined, it will\r
+       //              be used as a fallback.\r
+       //\r
+       // prettyPrint:\r
+       //              if true, we indent objects and arrays to make the output prettier.\r
+       //              The variable dojo.toJsonIndentStr is used as the indent string \r
+       //              -- to use something other than the default (tab), \r
+       //              change that variable before calling dojo.toJson().\r
+       //\r
+       // _indentStr:\r
+       //              private variable for recursive calls when pretty printing, do not use.\r
+       //              \r
+       // return:\r
+       //              a String representing the serialized version of the passed object.\r
+\r
+       _indentStr = _indentStr || "";\r
+       var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : "";\r
+       var newLine = prettyPrint ? "\n" : "";\r
+       var objtype = typeof it;\r
+       if(objtype == "undefined"){\r
+               return "undefined";\r
+       }\r
+       if(objtype == "number" || objtype == "boolean"){\r
+               return it + "";\r
+       }\r
+       if(it === null){\r
+               return "null";\r
+       }\r
+       if(dojo.isString(it)){ \r
+               return dojo._escapeString(it); \r
+       }\r
+       if(it.nodeType && it.cloneNode){ // isNode\r
+               return ""; // FIXME: would something like outerHTML be better here?\r
+       }\r
+       // recurse\r
+       var recurse = arguments.callee;\r
+       // short-circuit for objects that support "json" serialization\r
+       // if they return "self" then just pass-through...\r
+       var newObj;\r
+       if(typeof it.__json__ == "function"){\r
+               newObj = it.__json__();\r
+               if(it !== newObj){\r
+                       return recurse(newObj, prettyPrint, nextIndent);\r
+               }\r
+       }\r
+       if(typeof it.json == "function"){\r
+               newObj = it.json();\r
+               if(it !== newObj){\r
+                       return recurse(newObj, prettyPrint, nextIndent);\r
+               }\r
+       }\r
+\r
+       var sep = prettyPrint ? " " : "";\r
+\r
+       // array\r
+       if(dojo.isArray(it)){\r
+               var res = dojo.map(it, function(obj){\r
+                       var val = recurse(obj, prettyPrint, nextIndent);\r
+                       if(typeof val != "string"){\r
+                               val = "undefined";\r
+                       }\r
+                       return newLine + nextIndent + val;\r
+               });\r
+               return "[" + res.join("," + sep) + newLine + _indentStr + "]";\r
+       }\r
+       /*\r
+       // look in the registry\r
+       try {\r
+               window.o = it;\r
+               newObj = dojo.json.jsonRegistry.match(it);\r
+               return recurse(newObj, prettyPrint, nextIndent);\r
+       }catch(e){\r
+               // console.debug(e);\r
+       }\r
+       // it's a function with no adapter, skip it\r
+       */\r
+       if(objtype == "function"){\r
+               return null;\r
+       }\r
+       // generic object code path\r
+       var output = [];\r
+       for(var key in it){\r
+               var keyStr;\r
+               if(typeof key == "number"){\r
+                       keyStr = '"' + key + '"';\r
+               }else if(typeof key == "string"){\r
+                       keyStr = dojo._escapeString(key);\r
+               }else{\r
+                       // skip non-string or number keys\r
+                       continue;\r
+               }\r
+               val = recurse(it[key], prettyPrint, nextIndent);\r
+               if(typeof val != "string"){\r
+                       // skip non-serializable values\r
+                       continue;\r
+               }\r
+               // FIXME: use += on Moz!!\r
+               //       MOW NOTE: using += is a pain because you have to account for the dangling comma...\r
+               output.push(newLine + nextIndent + keyStr + ":" + sep + val);\r
+       }\r
+       return "{" + output.join("," + sep) + newLine + _indentStr + "}";\r
+}\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.array"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.array"] = true;\r
+\r
+dojo.provide("dojo._base.array");\r
+\r
+(function(){\r
+       var _getParts = function(arr, obj, cb){\r
+               return [ \r
+                       (dojo.isString(arr) ? arr.split("") : arr), \r
+                       (obj||dojo.global),\r
+                       // FIXME: cache the anonymous functions we create here?\r
+                       (dojo.isString(cb) ? (new Function("item", "index", "array", cb)) : cb)\r
+               ];\r
+       }\r
+\r
+       dojo.mixin(dojo, {\r
+               indexOf: function(      /*Array*/               array, \r
+                                                       /*Object*/              value,\r
+                                                       /*Integer?*/    fromIndex,\r
+                                                       /*Boolean?*/    findLast){\r
+                       // summary:\r
+                       //              locates the first index of the provided value in the\r
+                       //              passed array. If the value is not found, -1 is returned.\r
+                       // description:\r
+                       //              For details on this method, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf\r
+\r
+                       var i = 0, step = 1, end = array.length;\r
+                       if(findLast){\r
+                               i = end - 1;\r
+                               step = end = -1;\r
+                       }\r
+                       for(i = fromIndex || i; i != end; i += step){\r
+                               if(array[i] == value){ return i; }\r
+                       }\r
+                       return -1;      // Number\r
+               },\r
+\r
+               lastIndexOf: function(/*Array*/array, /*Object*/value, /*Integer?*/fromIndex){\r
+                       // summary:\r
+                       //              locates the last index of the provided value in the passed array. \r
+                       //              If the value is not found, -1 is returned.\r
+                       // description:\r
+                       //              For details on this method, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf\r
+                       return dojo.indexOf(array, value, fromIndex, true); // Number\r
+               },\r
+\r
+               forEach: function(/*Array*/arr, /*Function*/callback, /*Object?*/obj){\r
+                       // summary:\r
+                       //              for every item in arr, call callback with that item as its\r
+                       //              only parameter.\r
+                       // description:\r
+                       //              Return values are ignored. This function\r
+                       //              corresponds (and wraps) the JavaScript 1.6 forEach method. For\r
+                       //              more details, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach\r
+\r
+                       // match the behavior of the built-in forEach WRT empty arrs\r
+                       if(!arr || !arr.length){ return; }\r
+\r
+                       // FIXME: there are several ways of handilng thisObject. Is\r
+                       // dojo.global always the default context?\r
+                       var _p = _getParts(arr, obj, callback); arr = _p[0];\r
+                       for(var i=0,l=_p[0].length; i<l; i++){ \r
+                               _p[2].call(_p[1], arr[i], i, arr);\r
+                       }\r
+               },\r
+\r
+               _everyOrSome: function(/*Boolean*/every, /*Array*/arr, /*Function*/callback, /*Object?*/obj){\r
+                       var _p = _getParts(arr, obj, callback); arr = _p[0];\r
+                       for(var i = 0, l = arr.length; i < l; i++){\r
+                               var result = !!_p[2].call(_p[1], arr[i], i, arr);\r
+                               if(every ^ result){\r
+                                       return result; // Boolean\r
+                               }\r
+                       }\r
+                       return every; // Boolean\r
+               },\r
+\r
+               every: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){\r
+                       // summary:\r
+                       //              Determines whether or not every item in the array satisfies the\r
+                       //              condition implemented by callback.\r
+                       // description:\r
+                       //              The parameter thisObject may be used to\r
+                       //              scope the call to callback. The function signature is derived\r
+                       //              from the JavaScript 1.6 Array.every() function. More\r
+                       //              information on this can be found here:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every\r
+                       // example:\r
+                       //      |       dojo.every([1, 2, 3, 4], function(item){ return item>1; });\r
+                       //              returns false\r
+                       // example:\r
+                       //      |       dojo.every([1, 2, 3, 4], function(item){ return item>0; });\r
+                       //              returns true \r
+                       return this._everyOrSome(true, arr, callback, thisObject); // Boolean\r
+               },\r
+\r
+               some: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){\r
+                       // summary:\r
+                       //              Determines whether or not any item in the array satisfies the\r
+                       //              condition implemented by callback.\r
+                       // description:\r
+                       //              The parameter thisObject may be used to\r
+                       //              scope the call to callback. The function signature is derived\r
+                       //              from the JavaScript 1.6 Array.some() function. More\r
+                       //              information on this can be found here:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some\r
+                       // example:\r
+                       //      |       dojo.some([1, 2, 3, 4], function(item){ return item>1; });\r
+                       //              returns true\r
+                       // example:\r
+                       //      |       dojo.some([1, 2, 3, 4], function(item){ return item<1; });\r
+                       //              returns false\r
+                       return this._everyOrSome(false, arr, callback, thisObject); // Boolean\r
+               },\r
+\r
+               map: function(/*Array*/arr, /*Function*/func, /*Function?*/obj){\r
+                       // summary:\r
+                       //              applies a function to each element of an Array and creates\r
+                       //              an Array with the results\r
+                       // description:\r
+                       //              Returns a new array constituted from the return values of\r
+                       //              passing each element of arr into unary_func. The obj parameter\r
+                       //              may be passed to enable the passed function to be called in\r
+                       //              that scope.  In environments that support JavaScript 1.6, this\r
+                       //              function is a passthrough to the built-in map() function\r
+                       //              provided by Array instances. For details on this, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map\r
+                       // example:\r
+                       //      |       dojo.map([1, 2, 3, 4], function(item){ return item+1 });\r
+                       //              returns [2, 3, 4, 5]\r
+                       var _p = _getParts(arr, obj, func); arr = _p[0];\r
+                       var outArr = ((arguments[3]) ? (new arguments[3]()) : []);\r
+                       for(var i=0;i<arr.length;++i){\r
+                               outArr.push(_p[2].call(_p[1], arr[i], i, arr));\r
+                       }\r
+                       return outArr; // Array\r
+               },\r
+\r
+               filter: function(/*Array*/arr, /*Function*/callback, /*Object?*/obj){\r
+                       // summary:\r
+                       //              Returns a new Array with those items from arr that match the\r
+                       //              condition implemented by callback. ob may be used to\r
+                       //              scope the call to callback. The function signature is derived\r
+                       //              from the JavaScript 1.6 Array.filter() function.\r
+                       //\r
+                       //              More information on the JS 1.6 API can be found here:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter\r
+                       // example:\r
+                       //      |       dojo.filter([1, 2, 3, 4], function(item){ return item>1; });\r
+                       //              returns [2, 3, 4]\r
+\r
+                       var _p = _getParts(arr, obj, callback); arr = _p[0];\r
+                       var outArr = [];\r
+                       for(var i = 0; i < arr.length; i++){\r
+                               if(_p[2].call(_p[1], arr[i], i, arr)){\r
+                                       outArr.push(arr[i]);\r
+                               }\r
+                       }\r
+                       return outArr; // Array\r
+               }\r
+       });\r
+})();\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.Color"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.Color"] = true;\r
+dojo.provide("dojo._base.Color");\r
+\r
+\r
+\r
+dojo.Color = function(/*Array|String|Object*/ color){\r
+       // summary:\r
+       //              takes a named string, hex string, array of rgb or rgba values,\r
+       //              an object with r, g, b, and a properties, or another dojo.Color object\r
+       if(color){ this.setColor(color); }\r
+};\r
+\r
+// FIXME: there's got to be a more space-efficient way to encode or discover these!!  Use hex?\r
+dojo.Color.named = {\r
+       black:      [0,0,0],\r
+       silver:     [192,192,192],\r
+       gray:       [128,128,128],\r
+       white:      [255,255,255],\r
+       maroon:         [128,0,0],\r
+       red:        [255,0,0],\r
+       purple:         [128,0,128],\r
+       fuchsia:        [255,0,255],\r
+       green:      [0,128,0],\r
+       lime:       [0,255,0],\r
+       olive:          [128,128,0],\r
+       yellow:         [255,255,0],\r
+       navy:       [0,0,128],\r
+       blue:       [0,0,255],\r
+       teal:           [0,128,128],\r
+       aqua:           [0,255,255]\r
+};\r
+\r
+\r
+dojo.extend(dojo.Color, {\r
+       r: 255, g: 255, b: 255, a: 1,\r
+       _set: function(r, g, b, a){\r
+               var t = this; t.r = r; t.g = g; t.b = b; t.a = a;\r
+       },\r
+       setColor: function(/*Array|String|Object*/ color){\r
+               // summary:\r
+               //              takes a named string, hex string, array of rgb or rgba values,\r
+               //              an object with r, g, b, and a properties, or another dojo.Color object\r
+               var d = dojo;\r
+               if(d.isString(color)){\r
+                       d.colorFromString(color, this);\r
+               }else if(d.isArray(color)){\r
+                       d.colorFromArray(color, this);\r
+               }else{\r
+                       this._set(color.r, color.g, color.b, color.a);\r
+                       if(!(color instanceof d.Color)){ this.sanitize(); }\r
+               }\r
+               return this;    // dojo.Color\r
+       },\r
+       sanitize: function(){\r
+               // summary:\r
+               //              makes sure that the object has correct attributes\r
+               // description: \r
+               //              the default implementation does nothing, include dojo.colors to\r
+               //              augment it to real checks\r
+               return this;    // dojo.Color\r
+       },\r
+       toRgb: function(){\r
+               // summary: returns 3 component array of rgb values\r
+               var t = this;\r
+               return [t.r, t.g, t.b]; // Array\r
+       },\r
+       toRgba: function(){\r
+               // summary: returns a 4 component array of rgba values\r
+               var t = this;\r
+               return [t.r, t.g, t.b, t.a];    // Array\r
+       },\r
+       toHex: function(){\r
+               // summary: returns a css color string in hexadecimal representation\r
+               var arr = dojo.map(["r", "g", "b"], function(x){\r
+                       var s = this[x].toString(16);\r
+                       return s.length < 2 ? "0" + s : s;\r
+               }, this);\r
+               return "#" + arr.join("");      // String\r
+       },\r
+       toCss: function(/*Boolean?*/ includeAlpha){\r
+               // summary: returns a css color string in rgb(a) representation\r
+               var t = this, rgb = t.r + ", " + t.g + ", " + t.b;\r
+               return (includeAlpha ? "rgba(" + rgb + ", " + t.a : "rgb(" + rgb) + ")";        // String\r
+       },\r
+       toString: function(){\r
+               // summary: returns a visual representation of the color\r
+               return this.toCss(true); // String\r
+       }\r
+});\r
+\r
+dojo.blendColors = function(\r
+       /*dojo.Color*/ start, \r
+       /*dojo.Color*/ end, \r
+       /*Number*/ weight,\r
+       /*dojo.Color?*/ obj\r
+){\r
+       // summary: \r
+       //              blend colors end and start with weight from 0 to 1, 0.5 being a 50/50 blend,\r
+       //              can reuse a previously allocated dojo.Color object for the result\r
+       var d = dojo, t = obj || new dojo.Color();\r
+       d.forEach(["r", "g", "b", "a"], function(x){\r
+               t[x] = start[x] + (end[x] - start[x]) * weight;\r
+               if(x != "a"){ t[x] = Math.round(t[x]); }\r
+       });\r
+       return t.sanitize();    // dojo.Color\r
+};\r
+\r
+dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){\r
+       // summary: get rgb(a) array from css-style color declarations\r
+       var m = color.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);\r
+       return m && dojo.colorFromArray(m[1].split(/\s*,\s*/), obj);    // dojo.Color\r
+};\r
+\r
+dojo.colorFromHex = function(/*String*/ color, /*dojo.Color?*/ obj){\r
+       // summary: converts a hex string with a '#' prefix to a color object.\r
+       //      Supports 12-bit #rgb shorthand.\r
+       var d = dojo, t = obj || new d.Color(),\r
+               bits = (color.length == 4) ? 4 : 8,\r
+               mask = (1 << bits) - 1;\r
+       color = Number("0x" + color.substr(1));\r
+       if(isNaN(color)){\r
+               return null; // dojo.Color\r
+       }\r
+       d.forEach(["b", "g", "r"], function(x){\r
+               var c = color & mask;\r
+               color >>= bits;\r
+               t[x] = bits == 4 ? 17 * c : c;\r
+       });\r
+       t.a = 1;\r
+       return t;       // dojo.Color\r
+};\r
+\r
+dojo.colorFromArray = function(/*Array*/ a, /*dojo.Color?*/ obj){\r
+       // summary: builds a color from 1, 2, 3, or 4 element array\r
+       var t = obj || new dojo.Color();\r
+       t._set(Number(a[0]), Number(a[1]), Number(a[2]), Number(a[3]));\r
+       if(isNaN(t.a)){ t.a = 1; }\r
+       return t.sanitize();    // dojo.Color\r
+};\r
+\r
+dojo.colorFromString = function(/*String*/ str, /*dojo.Color?*/ obj){\r
+       //      summary:\r
+       //              parses str for a color value.\r
+       //      description:\r
+       //              Acceptable input values for str may include arrays of any form\r
+       //              accepted by dojo.colorFromArray, hex strings such as "#aaaaaa", or\r
+       //              rgb or rgba strings such as "rgb(133, 200, 16)" or "rgba(10, 10,\r
+       //              10, 50)"\r
+       //      returns:\r
+       //              a dojo.Color object. If obj is passed, it will be the return value.\r
+       var a = dojo.Color.named[str];\r
+       return a && dojo.colorFromArray(a, obj) || dojo.colorFromRgb(str, obj) || dojo.colorFromHex(str, obj);\r
+};\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base"] = true;\r
+dojo.provide("dojo._base");\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+(function(){\r
+       if(djConfig.require){\r
+               for(var x=0; x<djConfig.require.length; x++){\r
+                       dojo["require"](djConfig.require[x]);\r
+               }\r
+       }\r
+})();\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.window"] = true;\r
+dojo.provide("dojo._base.window");\r
+\r
+dojo._gearsObject = function(){\r
+       // summary: \r
+       //              factory method to get a Google Gears plugin instance to\r
+       //              expose in the browser runtime environment, if present\r
+       var factory;\r
+       var results;\r
+       \r
+       var gearsObj = dojo.getObject("google.gears");\r
+       if(gearsObj){ return gearsObj; } // already defined elsewhere\r
+       \r
+       if(typeof GearsFactory != "undefined"){ // Firefox\r
+               factory = new GearsFactory();\r
+       }else{\r
+               if(dojo.isIE){\r
+                       // IE\r
+                       try{\r
+                               factory = new ActiveXObject("Gears.Factory");\r
+                       }catch(e){\r
+                               // ok to squelch; there's no gears factory.  move on.\r
+                       }\r
+               }else if(navigator.mimeTypes["application/x-googlegears"]){\r
+                       // Safari?\r
+                       factory = document.createElement("object");\r
+                       factory.setAttribute("type", "application/x-googlegears");\r
+                       factory.setAttribute("width", 0);\r
+                       factory.setAttribute("height", 0);\r
+                       factory.style.display = "none";\r
+                       document.documentElement.appendChild(factory);\r
+               }\r
+       }\r
+\r
+       // still nothing?\r
+       if(!factory){ return null; }\r
+       \r
+       // define the global objects now; don't overwrite them though if they\r
+       // were somehow set internally by the Gears plugin, which is on their\r
+       // dev roadmap for the future\r
+       dojo.setObject("google.gears.factory", factory);\r
+       return dojo.getObject("google.gears");\r
+};\r
+\r
+/*=====\r
+dojo.isGears = {\r
+       // summary: True if client is using Google Gears\r
+};\r
+=====*/\r
+// see if we have Google Gears installed, and if\r
+// so, make it available in the runtime environment\r
+// and in the Google standard 'google.gears' global object\r
+dojo.isGears = (!!dojo._gearsObject())||0;\r
+\r
+/*=====\r
+dojo.doc = {\r
+       // summary:\r
+       //              Alias for the current document. 'dojo.doc' can be modified\r
+       //              for temporary context shifting. Also see dojo.withDoc().\r
+       // description:\r
+       //    Refer to dojo.doc rather\r
+       //    than referring to 'window.document' to ensure your code runs\r
+       //    correctly in managed contexts.\r
+       // example:\r
+       //      n.appendChild(dojo.doc.createElement('div'));\r
+}\r
+=====*/\r
+dojo.doc = window["document"] || null;\r
+\r
+dojo.body = function(){\r
+       // summary:\r
+       //              Return the body element of the document\r
+       //              return the body object associated with dojo.doc\r
+       // example:\r
+       //      dojo.body().appendChild(dojo.doc.createElement('div'));\r
+\r
+       // Note: document.body is not defined for a strict xhtml document\r
+       // Would like to memoize this, but dojo.doc can change vi dojo.withDoc().\r
+       return dojo.doc.body || dojo.doc.getElementsByTagName("body")[0]; // Node\r
+}\r
+\r
+dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){\r
+       // summary:\r
+       //              changes the behavior of many core Dojo functions that deal with\r
+       //              namespace and DOM lookup, changing them to work in a new global\r
+       //              context. The varibles dojo.global and dojo.doc\r
+       //              are modified as a result of calling this function.\r
+       dojo.global = globalObject;\r
+       dojo.doc = globalDocument;\r
+};\r
+\r
+dojo._fireCallback = function(callback, context, cbArguments){\r
+       // FIXME: should migrate to using "dojo.isString"!\r
+       if(context && dojo.isString(callback)){\r
+               callback = context[callback];\r
+       }\r
+       return (context ? callback.apply(context, cbArguments || [ ]) : callback());\r
+}\r
+\r
+dojo.withGlobal = function(    /*Object*/globalObject, \r
+                                                       /*Function*/callback, \r
+                                                       /*Object?*/thisObject, \r
+                                                       /*Array?*/cbArguments){\r
+       // summary:\r
+       //              Call callback with globalObject as dojo.global and\r
+       //              globalObject.document as dojo.doc. If provided, globalObject\r
+       //              will be executed in the context of object thisObject\r
+       // description:\r
+       //              When callback() returns or throws an error, the dojo.global\r
+       //              and dojo.doc will be restored to its previous state.\r
+       var rval;\r
+       var oldGlob = dojo.global;\r
+       var oldDoc = dojo.doc;\r
+       try{\r
+               dojo.setContext(globalObject, globalObject.document);\r
+               rval = dojo._fireCallback(callback, thisObject, cbArguments);\r
+       }finally{\r
+               dojo.setContext(oldGlob, oldDoc);\r
+       }\r
+       return rval;\r
+}\r
+\r
+dojo.withDoc = function(       /*Object*/documentObject, \r
+                                                       /*Function*/callback, \r
+                                                       /*Object?*/thisObject, \r
+                                                       /*Array?*/cbArguments){\r
+       // summary:\r
+       //              Call callback with documentObject as dojo.doc. If provided,\r
+       //              callback will be executed in the context of object thisObject\r
+       // description:\r
+       //              When callback() returns or throws an error, the dojo.doc will\r
+       //              be restored to its previous state.\r
+       var rval;\r
+       var oldDoc = dojo.doc;\r
+       try{\r
+               dojo.doc = documentObject;\r
+               rval = dojo._fireCallback(callback, thisObject, cbArguments);\r
+       }finally{\r
+               dojo.doc = oldDoc;\r
+       }\r
+       return rval;\r
+};\r
+\r
+//Register any module paths set up in djConfig. Need to do this\r
+//in the hostenvs since hostenv_browser can read djConfig from a\r
+//script tag's attribute.\r
+(function(){\r
+       var mp = djConfig["modulePaths"];\r
+       if(mp){\r
+               for(var param in mp){\r
+                       dojo.registerModulePath(param, mp[param]);\r
+               }\r
+       }\r
+})();\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.event"] = true;\r
+dojo.provide("dojo._base.event");\r
+\r
+\r
+// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA\r
+\r
+(function(){\r
+       // DOM event listener machinery\r
+       var del = (dojo._event_listener = {\r
+               add: function(/*DOMNode*/node, /*String*/name, /*Function*/fp){\r
+                       if(!node){return;} \r
+                       name = del._normalizeEventName(name);\r
+\r
+                       fp = del._fixCallback(name, fp);\r
+\r
+                       var oname = name;\r
+                       if(!dojo.isIE && (name == "mouseenter")||(name == "mouseleave")){\r
+                               var oname = name;\r
+                               var ofp = fp;\r
+                               name = (name == "mouseenter") ? "mouseover" : "mouseout";\r
+                               fp = function(e){\r
+                                       // thanks ben!\r
+                                       var id = dojo.isDescendant(e.relatedTarget, node);\r
+                                       if(id == false){\r
+                                               // e.type = oname; // FIXME: doesn't take?\r
+                                               return ofp.call(this, e);\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       node.addEventListener(name, fp, false);\r
+                       return fp; /*Handle*/\r
+               },\r
+               remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){\r
+                       // summary:\r
+                       //              clobbers the listener from the node\r
+                       // node:\r
+                       //              DOM node to attach the event to\r
+                       // event:\r
+                       //              the name of the handler to remove the function from\r
+                       // handle:\r
+                       //              the handle returned from add\r
+                       node && node.removeEventListener(del._normalizeEventName(event), handle, false);\r
+               },\r
+               _normalizeEventName: function(/*String*/name){\r
+                       // Generally, name should be lower case, unless it is special\r
+                       // somehow (e.g. a Mozilla DOM event).\r
+                       // Remove 'on'.\r
+                       return name.slice(0,2) =="on" ? name.slice(2) : name;\r
+               },\r
+               _fixCallback: function(/*String*/name, fp){\r
+                       // By default, we only invoke _fixEvent for 'keypress'\r
+                       // If code is added to _fixEvent for other events, we have\r
+                       // to revisit this optimization.\r
+                       // This also applies to _fixEvent overrides for Safari and Opera\r
+                       // below.\r
+                       return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); };\r
+               },\r
+               _fixEvent: function(evt, sender){\r
+                       // _fixCallback only attaches us to keypress.\r
+                       // Switch on evt.type anyway because we might \r
+                       // be called directly from dojo.fixEvent.\r
+                       switch(evt.type){\r
+                               case "keypress":\r
+                                       del._setKeyChar(evt);\r
+                                       break;\r
+                       }\r
+                       return evt;\r
+               },\r
+               _setKeyChar: function(evt){\r
+                       evt.keyChar = (evt.charCode ? String.fromCharCode(evt.charCode) : '');\r
+               }\r
+       });\r
+\r
+       // DOM events\r
+       \r
+       dojo.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){\r
+               // summary:\r
+               //              normalizes properties on the event object including event\r
+               //              bubbling methods, keystroke normalization, and x/y positions\r
+               // evt: Event\r
+               //              native event object\r
+               // sender: DOMNode\r
+               //              node to treat as "currentTarget"\r
+               return del._fixEvent(evt, sender);\r
+       }\r
+\r
+       dojo.stopEvent = function(/*Event*/evt){\r
+               // summary:\r
+               //              prevents propagation and clobbers the default action of the\r
+               //              passed event\r
+               // evt: Event\r
+               //              The event object. If omitted, window.event is used on IE.\r
+               evt.preventDefault();\r
+               evt.stopPropagation();\r
+               // NOTE: below, this method is overridden for IE\r
+       }\r
+\r
+       // the default listener to use on dontFix nodes, overriden for IE\r
+       var node_listener = dojo._listener;\r
+       \r
+       // Unify connect and event listeners\r
+       dojo._connect = function(obj, event, context, method, dontFix){\r
+               // FIXME: need a more strict test\r
+               var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener);\r
+               // choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node\r
+               // we need the third option to provide leak prevention on broken browsers (IE)\r
+               var lid = !isNode ? 0 : (!dontFix ? 1 : 2), l = [dojo._listener, del, node_listener][lid];\r
+               // create a listener\r
+               var h = l.add(obj, event, dojo.hitch(context, method));\r
+               // formerly, the disconnect package contained "l" directly, but if client code\r
+               // leaks the disconnect package (by connecting it to a node), referencing "l" \r
+               // compounds the problem.\r
+               // instead we return a listener id, which requires custom _disconnect below.\r
+               // return disconnect package\r
+               return [ obj, event, h, lid ];\r
+       }\r
+\r
+       dojo._disconnect = function(obj, event, handle, listener){\r
+               ([dojo._listener, del, node_listener][listener]).remove(obj, event, handle);\r
+       }\r
+\r
+       // Constants\r
+\r
+       // Public: client code should test\r
+       // keyCode against these named constants, as the\r
+       // actual codes can vary by browser.\r
+       dojo.keys = {\r
+               // summary: definitions for common key values\r
+               BACKSPACE: 8,\r
+               TAB: 9,\r
+               CLEAR: 12,\r
+               ENTER: 13,\r
+               SHIFT: 16,\r
+               CTRL: 17,\r
+               ALT: 18,\r
+               PAUSE: 19,\r
+               CAPS_LOCK: 20,\r
+               ESCAPE: 27,\r
+               SPACE: 32,\r
+               PAGE_UP: 33,\r
+               PAGE_DOWN: 34,\r
+               END: 35,\r
+               HOME: 36,\r
+               LEFT_ARROW: 37,\r
+               UP_ARROW: 38,\r
+               RIGHT_ARROW: 39,\r
+               DOWN_ARROW: 40,\r
+               INSERT: 45,\r
+               DELETE: 46,\r
+               HELP: 47,\r
+               LEFT_WINDOW: 91,\r
+               RIGHT_WINDOW: 92,\r
+               SELECT: 93,\r
+               NUMPAD_0: 96,\r
+               NUMPAD_1: 97,\r
+               NUMPAD_2: 98,\r
+               NUMPAD_3: 99,\r
+               NUMPAD_4: 100,\r
+               NUMPAD_5: 101,\r
+               NUMPAD_6: 102,\r
+               NUMPAD_7: 103,\r
+               NUMPAD_8: 104,\r
+               NUMPAD_9: 105,\r
+               NUMPAD_MULTIPLY: 106,\r
+               NUMPAD_PLUS: 107,\r
+               NUMPAD_ENTER: 108,\r
+               NUMPAD_MINUS: 109,\r
+               NUMPAD_PERIOD: 110,\r
+               NUMPAD_DIVIDE: 111,\r
+               F1: 112,\r
+               F2: 113,\r
+               F3: 114,\r
+               F4: 115,\r
+               F5: 116,\r
+               F6: 117,\r
+               F7: 118,\r
+               F8: 119,\r
+               F9: 120,\r
+               F10: 121,\r
+               F11: 122,\r
+               F12: 123,\r
+               F13: 124,\r
+               F14: 125,\r
+               F15: 126,\r
+               NUM_LOCK: 144,\r
+               SCROLL_LOCK: 145\r
+       };\r
+       \r
+       // IE event normalization\r
+       if(dojo.isIE){ \r
+               var _trySetKeyCode = function(e, code){\r
+                       try{\r
+                               // squelch errors when keyCode is read-only\r
+                               // (e.g. if keyCode is ctrl or shift)\r
+                               return (e.keyCode = code);\r
+                       }catch(e){\r
+                               return 0;\r
+                       }\r
+               }\r
+\r
+               // by default, use the standard listener\r
+               var iel = dojo._listener;\r
+               // dispatcher tracking property\r
+               if(!djConfig._allow_leaks){\r
+                       // custom listener that handles leak protection for DOM events\r
+                       node_listener = iel = dojo._ie_listener = {\r
+                               // support handler indirection: event handler functions are \r
+                               // referenced here. Event dispatchers hold only indices.\r
+                               handlers: [],\r
+                               // add a listener to an object\r
+                               add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){\r
+                                       source = source || dojo.global;\r
+                                       var f = source[method];\r
+                                       if(!f||!f._listeners){\r
+                                               var d = dojo._getIeDispatcher();\r
+                                               // original target function is special\r
+                                               d.target = f && (ieh.push(f) - 1);\r
+                                               // dispatcher holds a list of indices into handlers table\r
+                                               d._listeners = [];\r
+                                               // redirect source to dispatcher\r
+                                               f = source[method] = d;\r
+                                       }\r
+                                       return f._listeners.push(ieh.push(listener) - 1) ; /*Handle*/\r
+                               },\r
+                               // remove a listener from an object\r
+                               remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){\r
+                                       var f = (source||dojo.global)[method], l = f&&f._listeners;\r
+                                       if(f && l && handle--){\r
+                                               delete ieh[l[handle]];\r
+                                               delete l[handle]; \r
+                                       }\r
+                               }\r
+                       };\r
+                       // alias used above\r
+                       var ieh = iel.handlers;\r
+               }\r
+\r
+               dojo.mixin(del, {\r
+                       add: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){\r
+                               if(!node){return;} // undefined\r
+                               event = del._normalizeEventName(event);\r
+                               if(event=="onkeypress"){\r
+                                       // we need to listen to onkeydown to synthesize \r
+                                       // keypress events that otherwise won't fire\r
+                                       // on IE\r
+                                       var kd = node.onkeydown;\r
+                                       if(!kd||!kd._listeners||!kd._stealthKeydown){\r
+                                               // we simply ignore this connection when disconnecting\r
+                                               // because it's side-effects are harmless \r
+                                               del.add(node, "onkeydown", del._stealthKeyDown);\r
+                                               // we only want one stealth listener per node\r
+                                               node.onkeydown._stealthKeydown = true;\r
+                                       } \r
+                               }\r
+                               return iel.add(node, event, del._fixCallback(fp));\r
+                       },\r
+                       remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){\r
+                               iel.remove(node, del._normalizeEventName(event), handle); \r
+                       },\r
+                       _normalizeEventName: function(/*String*/eventName){\r
+                               // Generally, eventName should be lower case, unless it is\r
+                               // special somehow (e.g. a Mozilla event)\r
+                               // ensure 'on'\r
+                               return (eventName.slice(0,2)!="on" ? "on"+eventName : eventName);\r
+                       },\r
+                       _nop: function(){},\r
+                       _fixEvent: function(/*Event*/evt, /*DOMNode*/sender){\r
+                               // summary:\r
+                               //              normalizes properties on the event object including event\r
+                               //              bubbling methods, keystroke normalization, and x/y positions\r
+                               // evt: native event object\r
+                               // sender: node to treat as "currentTarget"\r
+                               if(!evt){\r
+                                       var w = (sender)&&((sender.ownerDocument || sender.document || sender).parentWindow)||window;\r
+                                       evt = w.event; \r
+                               }\r
+                               if(!evt){return(evt);}\r
+                               evt.target = evt.srcElement; \r
+                               evt.currentTarget = (sender || evt.srcElement); \r
+                               evt.layerX = evt.offsetX;\r
+                               evt.layerY = evt.offsetY;\r
+                               // FIXME: scroll position query is duped from dojo.html to\r
+                               // avoid dependency on that entire module. Now that HTML is in\r
+                               // Base, we should convert back to something similar there.\r
+                               var se = evt.srcElement, doc = (se && se.ownerDocument) || document;\r
+                               // DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used\r
+                               // here rather than document.body\r
+                               var docBody = ((dojo.isIE<6)||(doc["compatMode"]=="BackCompat")) ? doc.body : doc.documentElement;\r
+                               var offset = dojo._getIeDocumentElementOffset();\r
+                               evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x;\r
+                               evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y;\r
+                               if(evt.type == "mouseover"){ \r
+                                       evt.relatedTarget = evt.fromElement;\r
+                               }\r
+                               if(evt.type == "mouseout"){ \r
+                                       evt.relatedTarget = evt.toElement;\r
+                               }\r
+                               evt.stopPropagation = del._stopPropagation;\r
+                               evt.preventDefault = del._preventDefault;\r
+                               return del._fixKeys(evt);\r
+                       },\r
+                       _fixKeys: function(evt){\r
+                               switch(evt.type){\r
+                                       case "keypress":\r
+                                               var c = ("charCode" in evt ? evt.charCode : evt.keyCode);\r
+                                               if (c==10){\r
+                                                       // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla\r
+                                                       c=0;\r
+                                                       evt.keyCode = 13;\r
+                                               }else if(c==13||c==27){\r
+                                                       c=0; // Mozilla considers ENTER and ESC non-printable\r
+                                               }else if(c==3){\r
+                                                       c=99; // Mozilla maps CTRL-BREAK to CTRL-c\r
+                                               }\r
+                                               // Mozilla sets keyCode to 0 when there is a charCode\r
+                                               // but that stops the event on IE.\r
+                                               evt.charCode = c;\r
+                                               del._setKeyChar(evt);\r
+                                               break;\r
+                               }\r
+                               return evt;\r
+                       },\r
+                       // some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE\r
+                       // we map those virtual key codes to ascii here\r
+                       // not valid for all (non-US) keyboards, so maybe we shouldn't bother\r
+                       _punctMap: { \r
+                               106:42, \r
+                               111:47, \r
+                               186:59, \r
+                               187:43, \r
+                               188:44, \r
+                               189:45, \r
+                               190:46, \r
+                               191:47, \r
+                               192:96, \r
+                               219:91, \r
+                               220:92, \r
+                               221:93, \r
+                               222:39 \r
+                       },\r
+                       _stealthKeyDown: function(evt){\r
+                               // IE doesn't fire keypress for most non-printable characters.\r
+                               // other browsers do, we simulate it here.\r
+                               var kp = evt.currentTarget.onkeypress;\r
+                               // only works if kp exists and is a dispatcher\r
+                               if(!kp || !kp._listeners){ return; }\r
+                               // munge key/charCode\r
+                               var k=evt.keyCode;\r
+                               // These are Windows Virtual Key Codes\r
+                               // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp\r
+                               var unprintable = (k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);\r
+                               // synthesize keypress for most unprintables and CTRL-keys\r
+                               if(unprintable||evt.ctrlKey){\r
+                                       var c = (unprintable ? 0 : k);\r
+                                       if(evt.ctrlKey){\r
+                                               if(k==3 || k==13){\r
+                                                       return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively \r
+                                               }else if(c>95 && c<106){ \r
+                                                       c -= 48; // map CTRL-[numpad 0-9] to ASCII\r
+                                               }else if((!evt.shiftKey)&&(c>=65&&c<=90)){ \r
+                                                       c += 32; // map CTRL-[A-Z] to lowercase\r
+                                               }else{ \r
+                                                       c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII\r
+                                               }\r
+                                       }\r
+                                       // simulate a keypress event\r
+                                       var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c});\r
+                                       kp.call(evt.currentTarget, faux);\r
+                                       evt.cancelBubble = faux.cancelBubble;\r
+                                       evt.returnValue = faux.returnValue;\r
+                                       _trySetKeyCode(evt, faux.keyCode);\r
+                               }\r
+                       },\r
+                       // Called in Event scope\r
+                       _stopPropagation: function(){\r
+                               this.cancelBubble = true; \r
+                       },\r
+                       _preventDefault: function(){\r
+                               // Setting keyCode to 0 is the only way to prevent certain keypresses (namely\r
+                               // ctrl-combinations that correspond to menu accelerator keys).\r
+                               // Otoh, it prevents upstream listeners from getting this information\r
+                               // Try to split the difference here by clobbering keyCode only for ctrl \r
+                               // combinations. If you still need to access the key upstream, bubbledKeyCode is\r
+                               // provided as a workaround.\r
+                               this.bubbledKeyCode = this.keyCode;\r
+                               if(this.ctrlKey){_trySetKeyCode(this, 0);}\r
+                               this.returnValue = false;\r
+                       }\r
+               });\r
+                               \r
+               // override stopEvent for IE\r
+               dojo.stopEvent = function(evt){\r
+                       evt = evt || window.event;\r
+                       del._stopPropagation.call(evt);\r
+                       del._preventDefault.call(evt);\r
+               }\r
+       }\r
+\r
+       del._synthesizeEvent = function(evt, props){\r
+                       var faux = dojo.mixin({}, evt, props);\r
+                       del._setKeyChar(faux);\r
+                       // FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault); \r
+                       // but it throws an error when preventDefault is invoked on Safari\r
+                       // does Event.preventDefault not support "apply" on Safari?\r
+                       faux.preventDefault = function(){ evt.preventDefault(); }; \r
+                       faux.stopPropagation = function(){ evt.stopPropagation(); }; \r
+                       return faux;\r
+       }\r
+       \r
+       // Opera event normalization\r
+       if(dojo.isOpera){\r
+               dojo.mixin(del, {\r
+                       _fixEvent: function(evt, sender){\r
+                               switch(evt.type){\r
+                                       case "keypress":\r
+                                               var c = evt.which;\r
+                                               if(c==3){\r
+                                                       c=99; // Mozilla maps CTRL-BREAK to CTRL-c\r
+                                               }\r
+                                               // can't trap some keys at all, like INSERT and DELETE\r
+                                               // there is no differentiating info between DELETE and ".", or INSERT and "-"\r
+                                               c = ((c<41)&&(!evt.shiftKey) ? 0 : c);\r
+                                               if((evt.ctrlKey)&&(!evt.shiftKey)&&(c>=65)&&(c<=90)){\r
+                                                       // lowercase CTRL-[A-Z] keys\r
+                                                       c += 32;\r
+                                               }\r
+                                               return del._synthesizeEvent(evt, { charCode: c });\r
+                               }\r
+                               return evt;\r
+                       }\r
+               });\r
+       }\r
+\r
+       // Safari event normalization\r
+       if(dojo.isSafari){\r
+               dojo.mixin(del, {\r
+                       _fixEvent: function(evt, sender){\r
+                               switch(evt.type){\r
+                                       case "keypress":\r
+                                               var c = evt.charCode, s = evt.shiftKey, k = evt.keyCode;\r
+                                               // FIXME: This is a hack, suggest we rethink keyboard strategy.\r
+                                               // Arrow and page keys have 0 "keyCode" in keypress events.on Safari for Windows\r
+                                               k = k || identifierMap[evt.keyIdentifier] || 0;\r
+                                               if(evt.keyIdentifier=="Enter"){\r
+                                                       c = 0; // differentiate Enter from CTRL-m (both code 13)\r
+                                               }else if((evt.ctrlKey)&&(c>0)&&(c<27)){\r
+                                                       c += 96; // map CTRL-[A-Z] codes to ASCII\r
+                                               } else if (c==dojo.keys.SHIFT_TAB) {\r
+                                                       c = dojo.keys.TAB; // morph SHIFT_TAB into TAB + shiftKey: true\r
+                                                       s = true;\r
+                                               } else {\r
+                                                       c = (c>=32 && c<63232 ? c : 0); // avoid generating keyChar for non-printables\r
+                                               }\r
+                                               return del._synthesizeEvent(evt, {charCode: c, shiftKey: s, keyCode: k});\r
+                               }\r
+                               return evt;\r
+                       }\r
+               });\r
+               \r
+               dojo.mixin(dojo.keys, {\r
+                       SHIFT_TAB: 25,\r
+                       UP_ARROW: 63232,\r
+                       DOWN_ARROW: 63233,\r
+                       LEFT_ARROW: 63234,\r
+                       RIGHT_ARROW: 63235,\r
+                       F1: 63236,\r
+                       F2: 63237,\r
+                       F3: 63238,\r
+                       F4: 63239,\r
+                       F5: 63240,\r
+                       F6: 63241,\r
+                       F7: 63242,\r
+                       F8: 63243,\r
+                       F9: 63244,\r
+                       F10: 63245,\r
+                       F11: 63246,\r
+                       F12: 63247,\r
+                       PAUSE: 63250,\r
+                       DELETE: 63272,\r
+                       HOME: 63273,\r
+                       END: 63275,\r
+                       PAGE_UP: 63276,\r
+                       PAGE_DOWN: 63277,\r
+                       INSERT: 63302,\r
+                       PRINT_SCREEN: 63248,\r
+                       SCROLL_LOCK: 63249,\r
+                       NUM_LOCK: 63289\r
+               });\r
+               var dk = dojo.keys, identifierMap = { "Up": dk.UP_ARROW, "Down": dk.DOWN_ARROW, "Left": dk.LEFT_ARROW, "Right": dk.RIGHT_ARROW, "PageUp": dk.PAGE_UP, "PageDown": dk.PAGE_DOWN }; \r
+       }\r
+})();\r
+\r
+if(dojo.isIE){\r
+       // keep this out of the closure\r
+       // closing over 'iel' or 'ieh' b0rks leak prevention\r
+       // ls[i] is an index into the master handler array\r
+       dojo._getIeDispatcher = function(){\r
+               return function(){\r
+                       var ap=Array.prototype, h=dojo._ie_listener.handlers, c=arguments.callee, ls=c._listeners, t=h[c.target];\r
+                       // return value comes from original target function\r
+                       var r = t && t.apply(this, arguments);\r
+                       // invoke listeners after target function\r
+                       for(var i in ls){\r
+                               if(!(i in ap)){\r
+                                       h[ls[i]].apply(this, arguments);\r
+                               }\r
+                       }\r
+                       return r;\r
+               }\r
+       }\r
+       // keep this out of the closure to reduce RAM allocation\r
+       dojo._event_listener._fixCallback = function(fp){\r
+               var f = dojo._event_listener._fixEvent;\r
+               return function(e){ return fp.call(this, f(e, this)); };\r
+       }\r
+}\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.html"] = true;\r
+\r
+dojo.provide("dojo._base.html");\r
+\r
+// FIXME: need to add unit tests for all the semi-public methods\r
+\r
+try{\r
+       document.execCommand("BackgroundImageCache", false, true);\r
+}catch(e){\r
+       // sane browsers don't have cache "issues"\r
+}\r
+\r
+// =============================\r
+// DOM Functions\r
+// =============================\r
+\r
+/*=====\r
+dojo.byId = function(id, doc){\r
+       //      summary:\r
+       //              similar to other library's "$" function, takes a\r
+       //              string representing a DOM id or a DomNode\r
+       //              and returns the corresponding DomNode. If a Node is\r
+       //              passed, this function is a no-op. Returns a\r
+       //              single DOM node or null, working around several\r
+       //              browser-specific bugs to do so.\r
+       //      id: String|DOMNode\r
+       //              DOM id or DOM Node\r
+       //      doc: DocumentElement\r
+       //              Document to work in. Defaults to the current value of\r
+       //              dojo.doc.  Can be used to retreive\r
+       //              node references from other documents.\r
+=====*/\r
+if(dojo.isIE || dojo.isOpera){\r
+       dojo.byId = function(id, doc){\r
+               if(dojo.isString(id)){\r
+                       var _d = doc || dojo.doc;\r
+                       var te = _d.getElementById(id);\r
+                       // attributes.id.value is better than just id in case the \r
+                       // user has a name=id inside a form\r
+                       if(te && te.attributes.id.value == id){\r
+                               return te;\r
+                       }else{\r
+                               var eles = _d.all[id];\r
+                               if(!eles){ return; }\r
+                               if(!eles.length){ return eles; }\r
+                               // if more than 1, choose first with the correct id\r
+                               var i=0;\r
+                               while((te=eles[i++])){\r
+                                       if(te.attributes.id.value == id){ return te; }\r
+                               }\r
+                       }\r
+               }else{\r
+                       return id; // DomNode\r
+               }\r
+       }\r
+}else{\r
+       dojo.byId = function(id, doc){\r
+               if(dojo.isString(id)){\r
+                       return (doc || dojo.doc).getElementById(id);\r
+               }else{\r
+                       return id; // DomNode\r
+               }\r
+       }\r
+}\r
+/*=====\r
+}\r
+=====*/\r
+\r
+(function(){\r
+       /*\r
+       dojo.createElement = function(obj, parent, position){\r
+               // TODO: need to finish this!\r
+       }\r
+       */\r
+\r
+       var _destroyContainer = null;\r
+       dojo._destroyElement = function(/*String||DomNode*/node){\r
+               // summary:\r
+               //              removes node from its parent, clobbers it and all of its\r
+               //              children.\r
+               //      node:\r
+               //              the element to be destroyed, either as an ID or a reference\r
+\r
+               node = dojo.byId(node);\r
+               try{\r
+                       if(!_destroyContainer){\r
+                               _destroyContainer = document.createElement("div");\r
+                       }\r
+                       _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node);\r
+                       // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature\r
+                       _destroyContainer.innerHTML = ""; \r
+               }catch(e){\r
+                       /* squelch */\r
+               }\r
+       };\r
+\r
+       dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){\r
+               //      summary:\r
+               //              Returns true if node is a descendant of ancestor\r
+               //      node: id or node reference to test\r
+               //      ancestor: id or node reference of potential parent to test against\r
+               try{\r
+                       node = dojo.byId(node);\r
+                       ancestor = dojo.byId(ancestor);\r
+                       while(node){\r
+                               if(node === ancestor){\r
+                                       return true; // Boolean\r
+                               }\r
+                               node = node.parentNode;\r
+                       }\r
+               }catch(e){ return -1; /* squelch */ }\r
+               return false; // Boolean\r
+       };\r
+\r
+       dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){\r
+               //      summary: enable or disable selection on a node\r
+               //      node:\r
+               //              id or reference to node\r
+               //      selectable:\r
+               node = dojo.byId(node);\r
+               if(dojo.isMozilla){\r
+                       node.style.MozUserSelect = selectable ? "" : "none";\r
+               }else if(dojo.isKhtml){\r
+                       node.style.KhtmlUserSelect = selectable ? "auto" : "none";\r
+               }else if(dojo.isIE){\r
+                       node.unselectable = selectable ? "" : "on";\r
+                       dojo.query("*", node).forEach(function(descendant){\r
+                               descendant.unselectable = selectable ? "" : "on";\r
+                       });\r
+               }\r
+               //FIXME: else?  Opera?\r
+       };\r
+\r
+       var _insertBefore = function(/*Node*/node, /*Node*/ref){\r
+               ref.parentNode.insertBefore(node, ref);\r
+               return true;    //      boolean\r
+       }\r
+\r
+       var _insertAfter = function(/*Node*/node, /*Node*/ref){\r
+               //      summary:\r
+               //              Try to insert node after ref\r
+               var pn = ref.parentNode;\r
+               if(ref == pn.lastChild){\r
+                       pn.appendChild(node);\r
+               }else{\r
+                       return _insertBefore(node, ref.nextSibling);    //      boolean\r
+               }\r
+               return true;    //      boolean\r
+       }\r
+\r
+       dojo.place = function(/*String|DomNode*/node, /*String|DomNode*/refNode, /*String|Number*/position){\r
+               //      summary:\r
+               //              attempt to insert node in relation to ref based on position\r
+               //      node: \r
+               //              id or reference to node to place relative to refNode\r
+               //      refNode: \r
+               //              id or reference of node to use as basis for placement\r
+               //      position:\r
+               //              string noting the position of node relative to refNode or a\r
+               //              number indicating the location in the childNodes collection of\r
+               //              refNode. Accepted string values are:\r
+               //                      * before\r
+               //                      * after\r
+               //                      * first\r
+               //                      * last\r
+               //              "first" and "last" indicate positions as children of refNode.\r
+\r
+               // FIXME: need to write tests for this!!!!\r
+               if(!node || !refNode || position === undefined){ \r
+                       return false;   //      boolean \r
+               }\r
+               node = dojo.byId(node);\r
+               refNode = dojo.byId(refNode);\r
+               if(typeof position == "number"){\r
+                       var cn = refNode.childNodes;\r
+                       if((position == 0 && cn.length == 0) ||\r
+                               cn.length == position){\r
+                               refNode.appendChild(node); return true;\r
+                       }\r
+                       if(position == 0){\r
+                               return _insertBefore(node, refNode.firstChild);\r
+                       }\r
+                       return _insertAfter(node, cn[position-1]);\r
+               }\r
+               switch(position.toLowerCase()){\r
+                       case "before":\r
+                               return _insertBefore(node, refNode);    //      boolean\r
+                       case "after":\r
+                               return _insertAfter(node, refNode);             //      boolean\r
+                       case "first":\r
+                               if(refNode.firstChild){\r
+                                       return _insertBefore(node, refNode.firstChild); //      boolean\r
+                               }else{\r
+                                       refNode.appendChild(node);\r
+                                       return true;    //      boolean\r
+                               }\r
+                               break;\r
+                       default: // aka: last\r
+                               refNode.appendChild(node);\r
+                               return true;    //      boolean\r
+               }\r
+       }\r
+\r
+       // Box functions will assume this model.\r
+       // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.\r
+       // Can be set to change behavior of box setters.\r
+       \r
+       // can be either:\r
+       //      "border-box"\r
+       //      "content-box" (default)\r
+       dojo.boxModel = "content-box";\r
+       \r
+       // We punt per-node box mode testing completely.\r
+       // If anybody cares, we can provide an additional (optional) unit \r
+       // that overrides existing code to include per-node box sensitivity.\r
+\r
+       // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.\r
+       // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.\r
+       // IIRC, earlier versions of Opera did in fact use border-box.\r
+       // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.\r
+\r
+       if(dojo.isIE /*|| dojo.isOpera*/){\r
+               var _dcm = document.compatMode;\r
+               // client code may have to adjust if compatMode varies across iframes\r
+               dojo.boxModel = (_dcm=="BackCompat")||(_dcm=="QuirksMode")||(dojo.isIE<6) ? "border-box" : "content-box";\r
+       }\r
+\r
+       // =============================\r
+       // Style Functions\r
+       // =============================\r
+       \r
+       // getComputedStyle drives most of the style code.\r
+       // Wherever possible, reuse the returned object.\r
+       //\r
+       // API functions below that need to access computed styles accept an \r
+       // optional computedStyle parameter.\r
+       //\r
+       // If this parameter is omitted, the functions will call getComputedStyle themselves.\r
+       //\r
+       // This way, calling code can access computedStyle once, and then pass the reference to \r
+       // multiple API functions. \r
+       //\r
+       // This is a faux declaration to take pity on the doc tool\r
+\r
+/*=====\r
+       dojo.getComputedStyle = function(node){\r
+               //      summary:\r
+               //              Returns a "computed style" object.\r
+               //      description:\r
+               //              get "computed style" object which can be used to gather\r
+               //              information about the current state of the rendered node. \r
+               //\r
+               //              Note that this may behave differently on different browsers.\r
+               //              Values may have different formats and value encodings across\r
+               //              browsers. \r
+               //\r
+               //              Use the dojo.style() method for more consistent (pixelized)\r
+               //              return values.\r
+               //      node: DOMNode\r
+               //              a reference to a DOM node. Does NOT support taking an\r
+               //              ID string for speed reasons.\r
+               //      example:\r
+               //      |       dojo.getComputedStyle(dojo.byId('foo')).borderWidth;\r
+               return; // CSS2Properties\r
+       }\r
+=====*/\r
+\r
+       var gcs, dv = document.defaultView;\r
+       if(dojo.isSafari){\r
+               gcs = function(/*DomNode*/node){\r
+                       var s = dv.getComputedStyle(node, null);\r
+                       if(!s && node.style){ \r
+                               node.style.display = ""; \r
+                               s = dv.getComputedStyle(node, null);\r
+                       }\r
+                       return s || {};\r
+               }; \r
+       }else if(dojo.isIE){\r
+               gcs = function(node){\r
+                       return node.currentStyle;\r
+               };\r
+       }else{\r
+               gcs = function(node){\r
+                       return dv.getComputedStyle(node, null);\r
+               };\r
+       }\r
+       dojo.getComputedStyle = gcs;\r
+\r
+       if(!dojo.isIE){\r
+               dojo._toPixelValue = function(element, value){\r
+                       // style values can be floats, client code may want\r
+                       // to round for integer pixels.\r
+                       return parseFloat(value) || 0; \r
+               }\r
+       }else{\r
+               dojo._toPixelValue = function(element, avalue){\r
+                       if(!avalue){ return 0; }\r
+                       // on IE7, medium is usually 4 pixels\r
+                       if(avalue=="medium"){ return 4; }\r
+                       // style values can be floats, client code may\r
+                       // want to round this value for integer pixels.\r
+                       if(avalue.slice && (avalue.slice(-2)=='px')){ return parseFloat(avalue); }\r
+                       with(element){\r
+                               var sLeft = style.left;\r
+                               var rsLeft = runtimeStyle.left;\r
+                               runtimeStyle.left = currentStyle.left;\r
+                               try{\r
+                                       // 'avalue' may be incompatible with style.left, which can cause IE to throw\r
+                                       // this has been observed for border widths using "thin", "medium", "thick" constants\r
+                                       // those particular constants could be trapped by a lookup\r
+                                       // but perhaps there are more\r
+                                       style.left = avalue;\r
+                                       avalue = style.pixelLeft;\r
+                               }catch(e){\r
+                                       avalue = 0;\r
+                               }\r
+                               style.left = sLeft;\r
+                               runtimeStyle.left = rsLeft;\r
+                       }\r
+                       return avalue;\r
+               }\r
+       }\r
+\r
+       // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.\r
+       /*=====\r
+       dojo._getOpacity = function(node){\r
+                       //      summary:\r
+                       //              Returns the current opacity of the passed node as a\r
+                       //              floating-point value between 0 and 1.\r
+                       //      node: DomNode\r
+                       //              a reference to a DOM node. Does NOT support taking an\r
+                       //              ID string for speed reasons.\r
+                       //      return: Number between 0 and 1\r
+       }\r
+       =====*/\r
+\r
+       dojo._getOpacity = (dojo.isIE ? function(node){\r
+                       try{\r
+                               return (node.filters.alpha.opacity / 100); // Number\r
+                       }catch(e){\r
+                               return 1; // Number\r
+                       }\r
+               } : function(node){\r
+                       return dojo.getComputedStyle(node).opacity;\r
+               }\r
+       );\r
+\r
+       /*=====\r
+       dojo._setOpacity = function(node, opacity){\r
+                       //      summary:\r
+                       //              set the opacity of the passed node portably. Returns the\r
+                       //              new opacity of the node.\r
+                       //      node: DOMNode\r
+                       //              a reference to a DOM node. Does NOT support taking an\r
+                       //              ID string for performance reasons.\r
+                       //      opacity: Number\r
+                       //              A Number between 0 and 1. 0 specifies transparent.\r
+                       //      return: Number between 0 and 1\r
+       }\r
+       =====*/\r
+\r
+       dojo._setOpacity = (dojo.isIE ? function(/*DomNode*/node, /*Number*/opacity){\r
+                       if(opacity == 1){\r
+                               // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so remove it altogether (bug #2661)\r
+                               node.style.cssText = node.style.cssText.replace(/FILTER:[^;]*;?/i, "");\r
+                               if(node.nodeName.toLowerCase() == "tr"){\r
+                                       dojo.query("> td", node).forEach(function(i){\r
+                                               i.style.cssText = i.style.cssText.replace(/FILTER:[^;]*;?/i, "");\r
+                                       });\r
+                               }\r
+                       }else{\r
+                               var o = "Alpha(Opacity="+(opacity*100)+")";\r
+                               node.style.filter = o;\r
+                       }\r
+                       if(node.nodeName.toLowerCase() == "tr"){\r
+                               dojo.query("> td", node).forEach(function(i){\r
+                                       i.style.filter = o;\r
+                               });\r
+                       }\r
+                       return opacity;\r
+               } : function(node, opacity){\r
+                       return node.style.opacity = opacity;\r
+               }\r
+       );\r
+\r
+       var _pixelNamesCache = {\r
+               width: true, height: true, left: true, top: true\r
+       };\r
+       var _toStyleValue = function(node, type, value){\r
+               type = type.toLowerCase();\r
+               if(_pixelNamesCache[type] === true){\r
+                       return dojo._toPixelValue(node, value)\r
+               }else if(_pixelNamesCache[type] === false){\r
+                       return value;\r
+               }else{\r
+                       if(dojo.isOpera && type == "cssText"){\r
+                               // FIXME: add workaround for #2855 here\r
+                       }\r
+                       if(\r
+                               (type.indexOf("margin") >= 0) ||\r
+                               // (type.indexOf("border") >= 0) ||\r
+                               (type.indexOf("padding") >= 0) ||\r
+                               (type.indexOf("width") >= 0) ||\r
+                               (type.indexOf("height") >= 0) ||\r
+                               (type.indexOf("max") >= 0) ||\r
+                               (type.indexOf("min") >= 0) ||\r
+                               (type.indexOf("offset") >= 0)\r
+                       ){\r
+                               _pixelNamesCache[type] = true;\r
+                               return dojo._toPixelValue(node, value)\r
+                       }else{\r
+                               _pixelNamesCache[type] = false;\r
+                               return value;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // public API\r
+       \r
+       dojo.style = function(/*DomNode|String*/ node, /*String*/style, /*String?*/value){\r
+               //      summary:\r
+               //              gets or sets a style property on node. If 2 arguments are\r
+               //              passed, acts as a getter. If value is passed, acts as a setter\r
+               //              for the property.\r
+               //      node:\r
+               //              id or reference to node to get/set style for\r
+               //      style:\r
+               //              the style property to set in DOM-accessor format\r
+               //              ("borderWidth", not "border-width").\r
+               //      value:\r
+               //              optional. If passed, sets value on the node for style, handling\r
+               //              cross-browser concerns.\r
+               var n=dojo.byId(node), args=arguments.length, op=(style=="opacity");\r
+               if(args==3){\r
+                       return op ? dojo._setOpacity(n, value) : n.style[style] = value; /*Number*/\r
+               }\r
+               if(args==2 && op){\r
+                       return dojo._getOpacity(n);\r
+               }\r
+               var s = dojo.getComputedStyle(n);\r
+               return (args == 1) ? s : _toStyleValue(n, style, s[style]); /* CSS2Properties||String||Number */\r
+       }\r
+\r
+       // =============================\r
+       // Box Functions\r
+       // =============================\r
+\r
+       dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){\r
+               //      summary:\r
+               //              Returns object with special values specifically useful for node\r
+               //              fitting.\r
+               //                      l/t = left/top padding (respectively)\r
+               //                      w = the total of the left and right padding \r
+               //                      h = the total of the top and bottom padding\r
+               //              If 'node' has position, l/t forms the origin for child nodes. \r
+               //              The w/h are used for calculating boxes.\r
+               //              Normally application code will not need to invoke this\r
+               //              directly, and will use the ...box... functions instead.\r
+               var \r
+                       s=computedStyle||gcs(n), \r
+                       px=dojo._toPixelValue,\r
+                       l=px(n, s.paddingLeft), \r
+                       t=px(n, s.paddingTop);\r
+               return { \r
+                       l: l,\r
+                       t: t,\r
+                       w: l+px(n, s.paddingRight),\r
+                       h: t+px(n, s.paddingBottom)\r
+               };\r
+       }\r
+\r
+       dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){\r
+               //      summary:\r
+               //              returns an object with properties useful for noting the border\r
+               //              dimensions.\r
+               //                      l/t = the sum of left/top border (respectively)\r
+               //                      w = the sum of the left and right border\r
+               //                      h = the sum of the top and bottom border\r
+               //              The w/h are used for calculating boxes.\r
+               //              Normally application code will not need to invoke this\r
+               //              directly, and will use the ...box... functions instead.\r
+               var \r
+                       ne='none',\r
+                       px=dojo._toPixelValue, \r
+                       s=computedStyle||gcs(n), \r
+                       bl=(s.borderLeftStyle!=ne ? px(n, s.borderLeftWidth) : 0),\r
+                       bt=(s.borderTopStyle!=ne ? px(n, s.borderTopWidth) : 0);\r
+               return { \r
+                       l: bl,\r
+                       t: bt,\r
+                       w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),\r
+                       h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)\r
+               };\r
+       }\r
+\r
+       dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){\r
+               //      summary:\r
+               //              returns object with properties useful for box fitting with\r
+               //              regards to padding.\r
+               //                      l/t = the sum of left/top padding and left/top border (respectively)\r
+               //                      w = the sum of the left and right padding and border\r
+               //                      h = the sum of the top and bottom padding and border\r
+               //              The w/h are used for calculating boxes.\r
+               //              Normally application code will not need to invoke this\r
+               //              directly, and will use the ...box... functions instead.\r
+               var \r
+                       s=computedStyle||gcs(n), \r
+                       p=dojo._getPadExtents(n, s),\r
+                       b=dojo._getBorderExtents(n, s);\r
+               return { \r
+                       l: p.l + b.l,\r
+                       t: p.t + b.t,\r
+                       w: p.w + b.w,\r
+                       h: p.h + b.h\r
+               };\r
+       }\r
+\r
+       dojo._getMarginExtents = function(n, computedStyle){\r
+               //      summary:\r
+               //              returns object with properties useful for box fitting with\r
+               //              regards to box margins (i.e., the outer-box).\r
+               //                      l/t = marginLeft, marginTop, respectively\r
+               //                      w = total width, margin inclusive\r
+               //                      h = total height, margin inclusive\r
+               //              The w/h are used for calculating boxes.\r
+               //              Normally application code will not need to invoke this\r
+               //              directly, and will use the ...box... functions instead.\r
+               var \r
+                       s=computedStyle||gcs(n), \r
+                       px=dojo._toPixelValue,\r
+                       l=px(n, s.marginLeft),\r
+                       t=px(n, s.marginTop),\r
+                       r=px(n, s.marginRight),\r
+                       b=px(n, s.marginBottom);\r
+               if (dojo.isSafari && (s.position != "absolute")){\r
+                       // FIXME: Safari's version of the computed right margin\r
+                       // is the space between our right edge and the right edge \r
+                       // of our offsetParent. \r
+                       // What we are looking for is the actual margin value as \r
+                       // determined by CSS.\r
+                       // Hack solution is to assume left/right margins are the same.\r
+                       r = l;\r
+               }\r
+               return { \r
+                       l: l,\r
+                       t: t,\r
+                       w: l+r,\r
+                       h: t+b\r
+               };\r
+       }\r
+\r
+       // Box getters work in any box context because offsetWidth/clientWidth\r
+       // are invariant wrt box context\r
+       //\r
+       // They do *not* work for display: inline objects that have padding styles\r
+       // because the user agent ignores padding (it's bogus styling in any case)\r
+       //\r
+       // Be careful with IMGs because they are inline or block depending on \r
+       // browser and browser mode.\r
+\r
+       // Although it would be easier to read, there are not separate versions of \r
+       // _getMarginBox for each browser because:\r
+       // 1. the branching is not expensive\r
+       // 2. factoring the shared code wastes cycles (function call overhead)\r
+       // 3. duplicating the shared code wastes bytes\r
+       \r
+       dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){\r
+               // summary:\r
+               //              returns an object that encodes the width, height, left and top\r
+               //              positions of the node's margin box.\r
+               var s = computedStyle||gcs(node), me = dojo._getMarginExtents(node, s);\r
+               var     l = node.offsetLeft - me.l,     t = node.offsetTop - me.t;\r
+               if(dojo.isMoz){\r
+                       // Mozilla:\r
+                       // If offsetParent has a computed overflow != visible, the offsetLeft is decreased\r
+                       // by the parent's border.\r
+                       // We don't want to compute the parent's style, so instead we examine node's\r
+                       // computed left/top which is more stable.\r
+                       var sl = parseFloat(s.left), st = parseFloat(s.top);\r
+                       if(!isNaN(sl) && !isNaN(st)){\r
+                               l = sl, t = st;\r
+                       }else{\r
+                               // If child's computed left/top are not parseable as a number (e.g. "auto"), we\r
+                               // have no choice but to examine the parent's computed style.\r
+                               var p = node.parentNode;\r
+                               if(p && p.style){\r
+                                       var pcs = gcs(p);\r
+                                       if(pcs.overflow != "visible"){\r
+                                               var be = dojo._getBorderExtents(p, pcs);\r
+                                               l += be.l, t += be.t;\r
+                                       }\r
+                               }\r
+                       }\r
+               }else if(dojo.isOpera){\r
+                       // On Opera, offsetLeft includes the parent's border\r
+                       var p = node.parentNode;\r
+                       if(p){\r
+                               var be = dojo._getBorderExtents(p);\r
+                               l -= be.l, t -= be.t;\r
+                       }\r
+               }\r
+               return { \r
+                       l: l, \r
+                       t: t, \r
+                       w: node.offsetWidth + me.w, \r
+                       h: node.offsetHeight + me.h \r
+               };\r
+       }\r
+       \r
+       dojo._getContentBox = function(node, computedStyle){\r
+               // summary:\r
+               //              Returns an object that encodes the width, height, left and top\r
+               //              positions of the node's content box, irrespective of the\r
+               //              current box model.\r
+\r
+               // clientWidth/Height are important since the automatically account for scrollbars\r
+               // fallback to offsetWidth/Height for special cases (see #3378)\r
+               var s=computedStyle||gcs(node), pe=dojo._getPadExtents(node, s), be=dojo._getBorderExtents(node, s), w=node.clientWidth, h;\r
+               if(!w){\r
+                       w=node.offsetWidth, h=node.offsetHeight;\r
+               }else{\r
+                       h=node.clientHeight, be.w = be.h = 0; \r
+               }\r
+               // On Opera, offsetLeft includes the parent's border\r
+               if(dojo.isOpera){ pe.l += be.l; pe.t += be.t; };\r
+               return { \r
+                       l: pe.l, \r
+                       t: pe.t, \r
+                       w: w - pe.w - be.w, \r
+                       h: h - pe.h - be.h\r
+               };\r
+       }\r
+\r
+       dojo._getBorderBox = function(node, computedStyle){\r
+               var s=computedStyle||gcs(node), pe=dojo._getPadExtents(node, s), cb=dojo._getContentBox(node, s);\r
+               return { \r
+                       l: cb.l - pe.l, \r
+                       t: cb.t - pe.t, \r
+                       w: cb.w + pe.w, \r
+                       h: cb.h + pe.h\r
+               };\r
+       }\r
+\r
+       // Box setters depend on box context because interpretation of width/height styles\r
+       // vary wrt box context.\r
+       //\r
+       // The value of dojo.boxModel is used to determine box context.\r
+       // dojo.boxModel can be set directly to change behavior.\r
+       //\r
+       // Beware of display: inline objects that have padding styles\r
+       // because the user agent ignores padding (it's a bogus setup anyway)\r
+       //\r
+       // Be careful with IMGs because they are inline or block depending on \r
+       // browser and browser mode.\r
+       // \r
+       // Elements other than DIV may have special quirks, like built-in\r
+       // margins or padding, or values not detectable via computedStyle.\r
+       // In particular, margins on TABLE do not seems to appear \r
+       // at all in computedStyle on Mozilla.\r
+       \r
+       dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){\r
+               //      summary:\r
+               //              sets width/height/left/top in the current (native) box-model\r
+               //              dimentions. Uses the unit passed in u.\r
+               //      node: DOM Node reference. Id string not supported for performance reasons.\r
+               //      l: optional. left offset from parent.\r
+               //      t: optional. top offset from parent.\r
+               //      w: optional. width in current box model.\r
+               //      h: optional. width in current box model.\r
+               //      u: optional. unit measure to use for other measures. Defaults to "px".\r
+               u = u || "px";\r
+               with(node.style){\r
+                       if(!isNaN(l)){ left = l+u; }\r
+                       if(!isNaN(t)){ top = t+u; }\r
+                       if(w>=0){ width = w+u; }\r
+                       if(h>=0){ height = h+u; }\r
+               }\r
+       }\r
+\r
+       dojo._usesBorderBox = function(/*DomNode*/node){\r
+               //      summary: \r
+               //              True if the node uses border-box layout.\r
+\r
+               // We could test the computed style of node to see if a particular box\r
+               // has been specified, but there are details and we choose not to bother.\r
+               var n = node.tagName;\r
+               // For whatever reason, TABLE and BUTTON are always border-box by default.\r
+               // If you have assigned a different box to either one via CSS then\r
+               // box functions will break.\r
+               return dojo.boxModel=="border-box" || n=="TABLE" || n=="BUTTON"; // boolean\r
+       }\r
+\r
+       dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){\r
+               //      summary:\r
+               //              Sets the size of the node's contents, irrespective of margins,\r
+               //              padding, or borders.\r
+               var bb = dojo._usesBorderBox(node);\r
+               if(bb){\r
+                       var pb = dojo._getPadBorderExtents(node, computedStyle);\r
+                       if(widthPx>=0){ widthPx += pb.w; }\r
+                       if(heightPx>=0){ heightPx += pb.h; }\r
+               }\r
+               dojo._setBox(node, NaN, NaN, widthPx, heightPx);\r
+       }\r
+\r
+       dojo._setMarginBox = function(/*DomNode*/node,  /*Number?*/leftPx, /*Number?*/topPx, \r
+                                                                                                       /*Number?*/widthPx, /*Number?*/heightPx, \r
+                                                                                                       /*Object*/computedStyle){\r
+               //      summary:\r
+               //              sets the size of the node's margin box and placement\r
+               //              (left/top), irrespective of box model. Think of it as a\r
+               //              passthrough to dojo._setBox that handles box-model vagaries for\r
+               //              you.\r
+\r
+               var s = computedStyle || dojo.getComputedStyle(node);\r
+               // Some elements have special padding, margin, and box-model settings. \r
+               // To use box functions you may need to set padding, margin explicitly.\r
+               // Controlling box-model is harder, in a pinch you might set dojo.boxModel.\r
+               var bb=dojo._usesBorderBox(node),\r
+                               pb=bb ? _nilExtents : dojo._getPadBorderExtents(node, s),\r
+                               mb=dojo._getMarginExtents(node, s);\r
+               if(widthPx>=0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); }\r
+               if(heightPx>=0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); }\r
+               dojo._setBox(node, leftPx, topPx, widthPx, heightPx);\r
+       }\r
+       \r
+       var _nilExtents = { l:0, t:0, w:0, h:0 };\r
+\r
+       // public API\r
+       \r
+       dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){\r
+               //      summary:\r
+               //              getter/setter for the margin-box of node.\r
+               //      description: \r
+               //              Returns an object in the expected format of box (regardless\r
+               //              if box is passed). The object might look like:\r
+               //                      { l: 50, t: 200, w: 300: h: 150 }\r
+               //              for a node offset from its parent 50px to the left, 200px from\r
+               //              the top with a margin width of 300px and a margin-height of\r
+               //              150px.\r
+               //      node:\r
+               //              id or reference to DOM Node to get/set box for\r
+               //      box:\r
+               //              optional. If passed, denotes that dojo.marginBox() should\r
+               //              update/set the margin box for node. Box is an object in the\r
+               //              above format. All properties are optional if passed.\r
+               var n=dojo.byId(node), s=gcs(n), b=box;\r
+               return !b ? dojo._getMarginBox(n, s) : dojo._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object\r
+       }\r
+\r
+       dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){\r
+               //      summary:\r
+               //              getter/setter for the content-box of node.\r
+               //      description:\r
+               //              Returns an object in the expected format of box (regardless if box is passed).\r
+               //              The object might look like:\r
+               //                      { l: 50, t: 200, w: 300: h: 150 }\r
+               //              for a node offset from its parent 50px to the left, 200px from\r
+               //              the top with a content width of 300px and a content-height of\r
+               //              150px. Note that the content box may have a much larger border\r
+               //              or margin box, depending on the box model currently in use and\r
+               //              CSS values set/inherited for node.\r
+               //      node:\r
+               //              id or reference to DOM Node to get/set box for\r
+               //      box:\r
+               //              optional. If passed, denotes that dojo.contentBox() should\r
+               //              update/set the content box for node. Box is an object in the\r
+               //              above format. All properties are optional if passed.\r
+               var n=dojo.byId(node), s=gcs(n), b=box;\r
+               return !b ? dojo._getContentBox(n, s) : dojo._setContentSize(n, b.w, b.h, s); // Object\r
+       }\r
+       \r
+       // =============================\r
+       // Positioning \r
+       // =============================\r
+       \r
+       var _sumAncestorProperties = function(node, prop){\r
+               if(!(node = (node||0).parentNode)){return 0};\r
+               var val, retVal = 0, _b = dojo.body();\r
+               while(node && node.style){\r
+                       if(gcs(node).position == "fixed"){\r
+                               return 0;\r
+                       }\r
+                       val = node[prop];\r
+                       if(val){\r
+                               retVal += val - 0;\r
+                               // opera and khtml #body & #html has the same values, we only\r
+                               // need one value\r
+                               if(node == _b){ break; }\r
+                       }\r
+                       node = node.parentNode;\r
+               }\r
+               return retVal;  //      integer\r
+       }\r
+\r
+       dojo._docScroll = function(){\r
+               var _b = dojo.body();\r
+               var _w = dojo.global;\r
+               var de = dojo.doc.documentElement;\r
+               return {\r
+                       y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0),\r
+                       x: (_w.pageXOffset || dojo._fixIeBiDiScrollLeft(de.scrollLeft) || _b.scrollLeft || 0)\r
+               };\r
+       };\r
+       \r
+       dojo._isBodyLtr = function(){\r
+               //FIXME: could check html and body tags directly instead of computed style?  need to ignore case, accept empty values\r
+               return !("_bodyLtr" in dojo) ? \r
+                       dojo._bodyLtr = dojo.getComputedStyle(dojo.body()).direction == "ltr" :\r
+                       dojo._bodyLtr; // Boolean \r
+       }\r
+       \r
+       dojo._getIeDocumentElementOffset = function(){\r
+               // summary\r
+               // The following values in IE contain an offset:\r
+               //     event.clientX \r
+               //     event.clientY \r
+               //     node.getBoundingClientRect().left\r
+               //     node.getBoundingClientRect().top\r
+               // But other position related values do not contain this offset, such as\r
+               // node.offsetLeft, node.offsetTop, node.style.left and node.style.top.\r
+               // The offset is always (2, 2) in LTR direction. When the body is in RTL\r
+               // direction, the offset counts the width of left scroll bar's width.\r
+               // This function computes the actual offset.\r
+\r
+               //NOTE: assumes we're being called in an IE browser\r
+\r
+               var de = dojo.doc.documentElement;\r
+               if(dojo.isIE >= 7){\r
+                       return {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}; // Object\r
+               }else{\r
+                       // IE 6.0\r
+                       return {x: dojo._isBodyLtr() || window.parent == window ?\r
+                               de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft, \r
+                               y: de.clientTop}; // Object\r
+               }\r
+       };\r
+       \r
+       dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){\r
+               // In RTL direction, scrollLeft should be a negative value, but IE \r
+               // returns a positive one. All codes using documentElement.scrollLeft\r
+               // must call this function to fix this error, otherwise the position\r
+               // will offset to right when there is a horizontal scrollbar.\r
+               if(dojo.isIE && !dojo._isBodyLtr()){\r
+                       var de = dojo.doc.documentElement;\r
+                       return scrollLeft + de.clientWidth - de.scrollWidth; // Integer\r
+               }\r
+               return scrollLeft; // Integer\r
+       }\r
+       \r
+       dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll){\r
+               //      summary:\r
+               //              Gets the absolute position of the passed element based on the\r
+               //              document itself. Returns an object of the form:\r
+               //                      { x: 100, y: 300 }\r
+               //              if includeScroll is passed, the x and y values will include any\r
+               //              document offsets that may affect the position relative to the\r
+               //              viewport.\r
+\r
+               // FIXME: need to decide in the brave-new-world if we're going to be\r
+               // margin-box or border-box.\r
+               var ownerDocument = node.ownerDocument;\r
+               var ret = {\r
+                       x: 0,\r
+                       y: 0\r
+               };\r
+               var hasScroll = false;\r
+\r
+               // targetBoxType == "border-box"\r
+               var db = dojo.body();\r
+               if(dojo.isIE){\r
+                       var client = node.getBoundingClientRect();\r
+                       var offset = dojo._getIeDocumentElementOffset();\r
+                       ret.x = client.left - offset.x;\r
+                       ret.y = client.top - offset.y;\r
+               }else if(ownerDocument["getBoxObjectFor"]){\r
+                       // mozilla\r
+                       var bo = ownerDocument.getBoxObjectFor(node);\r
+                       ret.x = bo.x - _sumAncestorProperties(node, "scrollLeft");\r
+                       ret.y = bo.y - _sumAncestorProperties(node, "scrollTop");\r
+               }else{\r
+                       if(node["offsetParent"]){\r
+                               hasScroll = true;\r
+                               var endNode;\r
+                               // in Safari, if the node is an absolutely positioned child of\r
+                               // the body and the body has a margin the offset of the child\r
+                               // and the body contain the body's margins, so we need to end\r
+                               // at the body\r
+                               // FIXME: getting contrary results to the above in latest WebKit.\r
+                               if(dojo.isSafari &&\r
+                                       //(node.style.getPropertyValue("position") == "absolute") &&\r
+                                       (gcs(node).position == "absolute") &&\r
+                                       (node.parentNode == db)){\r
+                                       endNode = db;\r
+                               }else{\r
+                                       endNode = db.parentNode;\r
+                               }\r
+                               if(node.parentNode != db){\r
+                                       var nd = node;\r
+                                       if(dojo.isOpera || (dojo.isSafari >= 3)){ nd = db; }\r
+                                       ret.x -= _sumAncestorProperties(nd, "scrollLeft");\r
+                                       ret.y -= _sumAncestorProperties(nd, "scrollTop");\r
+                               }\r
+                               var curnode = node;\r
+                               do{\r
+                                       var n = curnode["offsetLeft"];\r
+                                       //FIXME: ugly hack to workaround the submenu in \r
+                                       //popupmenu2 does not shown up correctly in opera. \r
+                                       //Someone have a better workaround?\r
+                                       if(!dojo.isOpera || n>0){\r
+                                               ret.x += isNaN(n) ? 0 : n;\r
+                                       }\r
+                                       var m = curnode["offsetTop"];\r
+                                       ret.y += isNaN(m) ? 0 : m;\r
+                                       curnode = curnode.offsetParent;\r
+                               }while((curnode != endNode)&&curnode);\r
+                       }else if(node["x"]&&node["y"]){\r
+                               ret.x += isNaN(node.x) ? 0 : node.x;\r
+                               ret.y += isNaN(node.y) ? 0 : node.y;\r
+                       }\r
+               }\r
+               // account for document scrolling\r
+               // if offsetParent is used, ret value already includes scroll position\r
+               // so we may have to actually remove that value if !includeScroll\r
+               if(hasScroll || includeScroll){\r
+                       var scroll = dojo._docScroll();\r
+                       var m = hasScroll ? (!includeScroll ? -1 : 0) : 1;\r
+                       ret.y += m*scroll.y;\r
+                       ret.x += m*scroll.x;\r
+               }\r
+\r
+               return ret; // object\r
+       }\r
+\r
+       // FIXME: need a setter for coords or a moveTo!!\r
+       dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){\r
+               //      summary:\r
+               //              Returns an object that measures margin box width/height and\r
+               //              absolute positioning data from dojo._abs(). Return value will\r
+               //              be in the form:\r
+               //                      { l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }\r
+               //              does not act as a setter. If includeScroll is passed, the x and\r
+               //              y params are affected as one would expect in dojo._abs().\r
+               var n=dojo.byId(node), s=gcs(n), mb=dojo._getMarginBox(n, s);\r
+               var abs = dojo._abs(n, includeScroll);\r
+               mb.x = abs.x;\r
+               mb.y = abs.y;\r
+               return mb;\r
+       }\r
+\r
+       // =============================\r
+       // Element attribute Functions\r
+       // =============================\r
+\r
+       var _fixAttrName = function(/*String*/name){\r
+               switch(name.toLowerCase()){\r
+                       case "tabindex":\r
+                               // Internet Explorer will only set or remove tabindex\r
+                               // if it is spelled "tabIndex"\r
+                               return dojo.isIE ? "tabIndex" : "tabindex";\r
+                       default:\r
+                               return name;\r
+               }\r
+       }\r
+\r
+       // non-deprecated HTML4 attributes with default values\r
+       // http://www.w3.org/TR/html401/index/attributes.html\r
+       // FF and Safari will return the default values if you\r
+       // access the attributes via a property but not\r
+       // via getAttribute()\r
+       var _attrProps = {\r
+               colspan: "colSpan",\r
+               enctype: "enctype",\r
+               frameborder: "frameborder",\r
+               method: "method",\r
+               rowspan: "rowSpan",\r
+               scrolling: "scrolling",\r
+               shape: "shape",\r
+               span: "span",\r
+               type: "type",\r
+               valuetype: "valueType"\r
+       }\r
+\r
+       var _hasAttr = function(/*DomNode*/node, /*String*/name){\r
+               var attr = node.getAttributeNode(name);\r
+               return attr ? attr.specified : false;\r
+       }\r
+\r
+       dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){\r
+               //      summary:\r
+               //              Returns true if the requested attribute is specified on the\r
+               //              given element, and false otherwise.\r
+               //      node:\r
+               //              id or reference to the element to check\r
+               //      name:\r
+               //              the name of the attribute\r
+               //      returns:\r
+               //              true if the requested attribute is specified on the\r
+               //              given element, and false otherwise\r
+               return _hasAttr(dojo.byId(node), _fixAttrName(name)); // boolean\r
+       }\r
+\r
+       dojo.attr = function(/*DomNode|String*/node, /*String*/name, /*String?*/value){\r
+               //      summary:\r
+               //              Gets or sets an attribute on an HTML element.\r
+               //      description:\r
+               //              If 2 arguments are passed, acts as a getter.\r
+               //              If a third argument is passed, acts as a setter.\r
+               //      node:\r
+               //              id or reference to the element to get or set the attribute on\r
+               //      name:\r
+               //              the name of the attribute to get or set\r
+               //      value:\r
+               //              the value for the attribute (optional)\r
+               //      returns:\r
+               //              when used as a getter, the value of the requested attribute\r
+               //              or null if that attribute does not have a specified or\r
+               //              default value;\r
+               //              when user as a setter, undefined\r
+               node = dojo.byId(node);\r
+               name = _fixAttrName(name);\r
+               if(arguments.length == 3){\r
+                       node.setAttribute(name, value);\r
+                       return undefined;\r
+               }else{\r
+                       // should we access this attribute via a property or\r
+                       // via getAttribute()?\r
+                       var prop = _attrProps[name.toLowerCase()];\r
+                       if(prop){\r
+                               return node[prop];\r
+                       }else{\r
+                               return _hasAttr(node, name) ? node.getAttribute(name) : null;\r
+                       }\r
+               }\r
+       }\r
+\r
+       dojo.removeAttr = function(/*DomNode|String*/node, /*String*/name){\r
+               //      summary:\r
+               //              Removes an attribute from an HTML element.\r
+               //      node:\r
+               //              id or reference to the element to remove the attribute from\r
+               //      name:\r
+               //              the name of the attribute to remove\r
+               dojo.byId(node).removeAttribute(_fixAttrName(name));\r
+       }\r
+})();\r
+\r
+// =============================\r
+// (CSS) Class Functions\r
+// =============================\r
+\r
+dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){\r
+       //      summary:\r
+       //              Returns whether or not the specified classes are a portion of the\r
+       //              class list currently applied to the node. \r
+       return ((" "+dojo.byId(node).className+" ").indexOf(" "+classStr+" ") >= 0);  // Boolean\r
+};\r
+\r
+dojo.addClass = function(/*DomNode|String*/node, /*String*/classStr){\r
+       //      summary:\r
+       //              Adds the specified classes to the end of the class list on the\r
+       //              passed node.\r
+       node = dojo.byId(node);\r
+       var cls = node.className;\r
+       if((" "+cls+" ").indexOf(" "+classStr+" ") < 0){\r
+               node.className = cls + (cls ? ' ' : '') + classStr;\r
+       }\r
+};\r
+\r
+dojo.removeClass = function(/*DomNode|String*/node, /*String*/classStr){\r
+       // summary: Removes the specified classes from node.\r
+       node = dojo.byId(node);\r
+       var t = dojo.trim((" " + node.className + " ").replace(" " + classStr + " ", " "));\r
+       if(node.className != t){ node.className = t; }\r
+};\r
+\r
+dojo.toggleClass = function(/*DomNode|String*/node, /*String*/classStr, /*Boolean?*/condition){\r
+       //      summary:        \r
+       //              Adds a class to node if not present, or removes if present.\r
+       //              Pass a boolean condition if you want to explicitly add or remove.\r
+       //      condition:\r
+       //              If passed, true means to add the class, false means to remove.\r
+       if(condition === undefined){\r
+               condition = !dojo.hasClass(node, classStr);\r
+       }\r
+       dojo[condition ? "addClass" : "removeClass"](node, classStr);\r
+};\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.NodeList"] = true;\r
+dojo.provide("dojo._base.NodeList");\r
+\r
+\r
+\r
+(function(){\r
+\r
+       var d = dojo;\r
+\r
+       var tnl = function(arr){\r
+               arr.constructor = dojo.NodeList;\r
+               dojo._mixin(arr, dojo.NodeList.prototype);\r
+               return arr;\r
+       }\r
+\r
+       dojo.NodeList = function(){\r
+               //      summary:\r
+               //              dojo.NodeList is as subclass of Array which adds syntactic \r
+               //              sugar for chaining, common iteration operations, animation, \r
+               //              and node manipulation. NodeLists are most often returned as\r
+               //              the result of dojo.query() calls.\r
+               //      example:\r
+               //              create a node list from a node\r
+               //              |       new dojo.NodeList(dojo.byId("foo"));\r
+\r
+               return tnl(Array.apply(null, arguments));\r
+       }\r
+\r
+       dojo.NodeList._wrap = tnl;\r
+\r
+       dojo.extend(dojo.NodeList, {\r
+               // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods\r
+\r
+               // FIXME: handle return values for #3244\r
+               //              http://trac.dojotoolkit.org/ticket/3244\r
+               \r
+               // FIXME:\r
+               //              need to wrap or implement:\r
+               //                      join (perhaps w/ innerHTML/outerHTML overload for toString() of items?)\r
+               //                      reduce\r
+               //                      reduceRight\r
+\r
+               slice: function(/*===== begin, end =====*/){\r
+                       // summary:\r
+                       //              Returns a new NodeList, maintaining this one in place\r
+                       // description:\r
+                       //              This method behaves exactly like the Array.slice method\r
+                       //              with the caveat that it returns a dojo.NodeList and not a\r
+                       //              raw Array. For more details, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice\r
+                       // begin: Integer\r
+                       //              Can be a positive or negative integer, with positive\r
+                       //              integers noting the offset to begin at, and negative\r
+                       //              integers denoting an offset from the end (i.e., to the left\r
+                       //              of the end)\r
+                       // end: Integer?\r
+                       //              Optional parameter to describe what position relative to\r
+                       //              the NodeList's zero index to end the slice at. Like begin,\r
+                       //              can be positive or negative.\r
+                       var a = dojo._toArray(arguments);\r
+                       return tnl(a.slice.apply(this, a));\r
+               },\r
+\r
+               splice: function(/*===== index, howmany, item =====*/){\r
+                       // summary:\r
+                       //              Returns a new NodeList, manipulating this NodeList based on\r
+                       //              the arguments passed, potentially splicing in new elements\r
+                       //              at an offset, optionally deleting elements\r
+                       // description:\r
+                       //              This method behaves exactly like the Array.splice method\r
+                       //              with the caveat that it returns a dojo.NodeList and not a\r
+                       //              raw Array. For more details, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice\r
+                       // index: Integer\r
+                       //              begin can be a positive or negative integer, with positive\r
+                       //              integers noting the offset to begin at, and negative\r
+                       //              integers denoting an offset from the end (i.e., to the left\r
+                       //              of the end)\r
+                       // howmany: Integer?\r
+                       //              Optional parameter to describe what position relative to\r
+                       //              the NodeList's zero index to end the slice at. Like begin,\r
+                       //              can be positive or negative.\r
+                       // item: Object...?\r
+                       //              Any number of optional parameters may be passed in to be\r
+                       //              spliced into the NodeList\r
+                       // returns:\r
+                       //              dojo.NodeList\r
+                       var a = dojo._toArray(arguments);\r
+                       return tnl(a.splice.apply(this, a));\r
+               },\r
+\r
+               concat: function(/*===== item =====*/){\r
+                       // summary:\r
+                       //              Returns a new NodeList comprised of items in this NodeList\r
+                       //              as well as items passed in as parameters\r
+                       // description:\r
+                       //              This method behaves exactly like the Array.concat method\r
+                       //              with the caveat that it returns a dojo.NodeList and not a\r
+                       //              raw Array. For more details, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat\r
+                       // item: Object...?\r
+                       //              Any number of optional parameters may be passed in to be\r
+                       //              spliced into the NodeList\r
+                       // returns:\r
+                       //              dojo.NodeList\r
+                       var a = dojo._toArray(arguments, 0, [this]);\r
+                       return tnl(a.concat.apply([], a));\r
+               },\r
+               \r
+               indexOf: function(/*Object*/ value, /*Integer?*/ fromIndex){\r
+                       //      summary:\r
+                       //              see dojo.indexOf(). The primary difference is that the acted-on \r
+                       //              array is implicitly this NodeList\r
+                       // value:\r
+                       //              The value to search for.\r
+                       // fromIndex:\r
+                       //              The loction to start searching from. Optional. Defaults to 0.\r
+                       //      description:\r
+                       //              For more details on the behavior of indexOf, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf\r
+                       //      returns:\r
+                       //              Positive Integer or 0 for a match, -1 of not found.\r
+                       return d.indexOf(this, value, fromIndex); // Integer\r
+               },\r
+\r
+               lastIndexOf: function(/*===== value, fromIndex =====*/){\r
+                       // summary:\r
+                       //              see dojo.lastIndexOf(). The primary difference is that the\r
+                       //              acted-on array is implicitly this NodeList\r
+                       //      description:\r
+                       //              For more details on the behavior of lastIndexOf, see:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf\r
+                       // value: Object\r
+                       //              The value to search for.\r
+                       // fromIndex: Integer?\r
+                       //              The loction to start searching from. Optional. Defaults to 0.\r
+                       // returns:\r
+                       //              Positive Integer or 0 for a match, -1 of not found.\r
+                       return d.lastIndexOf.apply(d, d._toArray(arguments, 0, [this])); // Integer\r
+               },\r
+\r
+               every: function(/*Function*/callback, /*Object?*/thisObject){\r
+                       //      summary:\r
+                       //              see dojo.every() and:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every\r
+                       //              Takes the same structure of arguments and returns as\r
+                       //              dojo.every() with the caveat that the passed array is\r
+                       //              implicitly this NodeList\r
+                       return d.every(this, callback, thisObject); // Boolean\r
+               },\r
+\r
+               some: function(/*Function*/callback, /*Object?*/thisObject){\r
+                       //      summary:\r
+                       //              see dojo.some() and:\r
+                       //                      http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some\r
+                       //              Takes the same structure of arguments and returns as\r
+                       //              dojo.some() with the caveat that the passed array is\r
+                       //              implicitly this NodeList\r
+                       return d.some(this, callback, thisObject); // Boolean\r
+               },\r
+\r
+               map: function(/*Function*/ func, /*Function?*/ obj){\r
+                       //      summary:\r
+                       //              see dojo.map(). The primary difference is that the acted-on\r
+                       //              array is implicitly this NodeList and the return is a\r
+                       //              dojo.NodeList (a subclass of Array)\r
+\r
+                       return d.map(this, func, obj, d.NodeList); // dojo.NodeList\r
+               },\r
+\r
+               forEach: function(callback, thisObj){\r
+                       //      summary:\r
+                       //              see dojo.forEach(). The primary difference is that the acted-on \r
+                       //              array is implicitly this NodeList\r
+\r
+                       d.forEach(this, callback, thisObj);\r
+                       return this; // dojo.NodeList non-standard return to allow easier chaining\r
+               },\r
+\r
+               // custom methods\r
+               \r
+               coords: function(){\r
+                       //      summary:\r
+                       //              Returns the box objects all elements in a node list as\r
+                       //              an Array (*not* a NodeList)\r
+                       \r
+                       return d.map(this, d.coords);\r
+               },\r
+\r
+               style: function(/*===== property, value =====*/){\r
+                       //      summary:\r
+                       //              gets or sets the CSS property for every element in the NodeList\r
+                       //      property: String\r
+                       //              the CSS property to get/set, in JavaScript notation\r
+                       //              ("lineHieght" instead of "line-height") \r
+                       //      value: String?\r
+                       //              optional. The value to set the property to\r
+                       //      return:\r
+                       //              if no value is passed, the result is an array of strings.\r
+                       //              If a value is passed, the return is this NodeList\r
+                       var aa = d._toArray(arguments, 0, [null]);\r
+                       var s = this.map(function(i){\r
+                               aa[0] = i;\r
+                               return d.style.apply(d, aa);\r
+                       });\r
+                       return (arguments.length > 1) ? this : s; // String||dojo.NodeList\r
+               },\r
+\r
+               styles: function(/*===== property, value =====*/){\r
+                       //      summary:\r
+                       //              Deprecated. Use NodeList.style instead. Will be removed in\r
+                       //              Dojo 1.1. Gets or sets the CSS property for every element\r
+                       //              in the NodeList\r
+                       //      property: String\r
+                       //              the CSS property to get/set, in JavaScript notation\r
+                       //              ("lineHieght" instead of "line-height") \r
+                       //      value: String?\r
+                       //              optional. The value to set the property to\r
+                       //      return:\r
+                       //              if no value is passed, the result is an array of strings.\r
+                       //              If a value is passed, the return is this NodeList\r
+                       d.deprecated("NodeList.styles", "use NodeList.style instead", "1.1");\r
+                       return this.style.apply(this, arguments);\r
+               },\r
+\r
+               addClass: function(/*String*/ className){\r
+                       // summary:\r
+                       //              adds the specified class to every node in the list\r
+                       //\r
+                       this.forEach(function(i){ d.addClass(i, className); });\r
+                       return this;\r
+               },\r
+\r
+               removeClass: function(/*String*/ className){\r
+                       this.forEach(function(i){ d.removeClass(i, className); });\r
+                       return this;\r
+               },\r
+\r
+               // FIXME: toggleClass()? connectPublisher()? connectRunOnce()?\r
+\r
+               place: function(/*String||Node*/ queryOrNode, /*String*/ position){\r
+                       //      summary:\r
+                       //              places elements of this node list relative to the first element matched\r
+                       //              by queryOrNode. Returns the original NodeList.\r
+                       //      queryOrNode:\r
+                       //              may be a string representing any valid CSS3 selector or a DOM node.\r
+                       //              In the selector case, only the first matching element will be used \r
+                       //              for relative positioning.\r
+                       //      position:\r
+                       //              can be one of:\r
+                       //                      "last"||"end" (default)\r
+                       //                      "first||"start"\r
+                       //                      "before"\r
+                       //                      "after"\r
+                       //              or an offset in the childNodes property\r
+                       var item = d.query(queryOrNode)[0];\r
+                       position = position||"last";\r
+\r
+                       for(var x=0; x<this.length; x++){\r
+                               d.place(this[x], item, position);\r
+                       }\r
+                       return this; // dojo.NodeList\r
+               },\r
+\r
+               connect: function(/*String*/ methodName, /*Object||Function||String*/ objOrFunc, /*String?*/ funcName){\r
+                       //      summary:\r
+                       //              attach event handlers to every item of the NodeList. Uses dojo.connect()\r
+                       //              so event properties are normalized\r
+                       //      methodName:\r
+                       //              the name of the method to attach to. For DOM events, this should be\r
+                       //              the lower-case name of the event\r
+                       //      objOrFunc:\r
+                       //              if 2 arguments are passed (methodName, objOrFunc), objOrFunc should\r
+                       //              reference a function or be the name of the function in the global\r
+                       //              namespace to attach. If 3 arguments are provided\r
+                       //              (methodName, objOrFunc, funcName), objOrFunc must be the scope to \r
+                       //              locate the bound function in\r
+                       //      funcName:\r
+                       //              optional. A string naming the function in objOrFunc to bind to the\r
+                       //              event. May also be a function reference.\r
+                       //      example:\r
+                       //              add an onclick handler to every button on the page\r
+                       //              |       dojo.query("onclick", function(e){\r
+                       //              |               console.debug("clicked!");\r
+                       //              |       });\r
+                       // example:\r
+                       //              attach foo.bar() to every odd div's onmouseover\r
+                       //              |       dojo.query("div:nth-child(odd)").onclick("onmouseover", foo, "bar");\r
+                       this.forEach(function(item){\r
+                               d.connect(item, methodName, objOrFunc, funcName);\r
+                       });\r
+                       return this; // dojo.NodeList\r
+               },\r
+\r
+               orphan: function(/*String?*/ simpleFilter){\r
+                       //      summary:\r
+                       //              removes elements in this list that match the simple\r
+                       //              filter from their parents and returns them as a new\r
+                       //              NodeList.\r
+                       //      simpleFilter: single-expression CSS filter\r
+                       //      return: a dojo.NodeList of all of the elements orpahned\r
+                       var orphans = (simpleFilter) ? d._filterQueryResult(this, simpleFilter) : this;\r
+                       orphans.forEach(function(item){\r
+                               if(item["parentNode"]){\r
+                                       item.parentNode.removeChild(item);\r
+                               }\r
+                       });\r
+                       return orphans; // dojo.NodeList\r
+               },\r
+\r
+               adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){\r
+                       //      summary:\r
+                       //              places any/all elements in queryOrListOrNode at a\r
+                       //              position relative to the first element in this list.\r
+                       //              Returns a dojo.NodeList of the adopted elements.\r
+                       //      queryOrListOrNode:\r
+                       //              a DOM node or a query string or a query result.\r
+                       //              Represents the nodes to be adopted relative to the\r
+                       //              first element of this NodeList.\r
+                       //      position:\r
+                       //              optional. One of:\r
+                       //                      "last"||"end" (default)\r
+                       //                      "first||"start"\r
+                       //                      "before"\r
+                       //                      "after"\r
+                       //              or an offset in the childNodes property\r
+                       var item = this[0];\r
+                       return d.query(queryOrListOrNode).forEach(function(ai){ d.place(ai, item, (position||"last")); }); // dojo.NodeList\r
+               },\r
+\r
+               // FIXME: do we need this?\r
+               query: function(/*String*/ queryStr){\r
+                       //      summary:\r
+                       //              Returns a new, flattened NodeList. Elements of the new list\r
+                       //              satisfy the passed query but use elements of the\r
+                       //              current NodeList as query roots.\r
+\r
+                       queryStr = queryStr||"";\r
+\r
+                       // FIXME: probably slow\r
+                       var ret = d.NodeList();\r
+                       this.forEach(function(item){\r
+                               d.query(queryStr, item).forEach(function(subItem){\r
+                                       if(typeof subItem != "undefined"){\r
+                                               ret.push(subItem);\r
+                                       }\r
+                               });\r
+                       });\r
+                       return ret; // dojo.NodeList\r
+               },\r
+\r
+               filter: function(/*String*/ simpleQuery){\r
+                       //      summary:\r
+                       //              "masks" the built-in javascript filter() method to support\r
+                       //              passing a simple string filter in addition to supporting\r
+                       //              filtering function objects.\r
+                       //      example:\r
+                       //              "regular" JS filter syntax as exposed in dojo.filter:\r
+                       //              |       dojo.query("*").filter(function(item){\r
+                       //              |               // highlight every paragraph\r
+                       //              |               return (item.nodeName == "p");\r
+                       //              |       }).styles("backgroundColor", "yellow");\r
+                       // example:\r
+                       //              the same filtering using a CSS selector\r
+                       //              |       dojo.query("*").filter("p").styles("backgroundColor", "yellow");\r
+\r
+                       var items = this;\r
+                       var _a = arguments;\r
+                       var r = d.NodeList();\r
+                       var rp = function(t){ \r
+                               if(typeof t != "undefined"){\r
+                                       r.push(t); \r
+                               }\r
+                       }\r
+                       if(d.isString(simpleQuery)){\r
+                               items = d._filterQueryResult(this, _a[0]);\r
+                               if(_a.length == 1){\r
+                                       // if we only got a string query, pass back the filtered results\r
+                                       return items; // dojo.NodeList\r
+                               }\r
+                               // if we got a callback, run it over the filtered items\r
+                               d.forEach(d.filter(items, _a[1], _a[2]), rp);\r
+                               return r; // dojo.NodeList\r
+                       }\r
+                       // handle the (callback, [thisObject]) case\r
+                       d.forEach(d.filter(items, _a[0], _a[1]), rp);\r
+                       return r; // dojo.NodeList\r
+\r
+               },\r
+               \r
+               /*\r
+               // FIXME: should this be "copyTo" and include parenting info?\r
+               clone: function(){\r
+                       // summary:\r
+                       //              creates node clones of each element of this list\r
+                       //              and returns a new list containing the clones\r
+               },\r
+               */\r
+\r
+               addContent: function(/*String*/ content, /*String||Integer?*/ position){\r
+                       //      summary:\r
+                       //              add a node or some HTML as a string to every item in the list. \r
+                       //              Returns the original list.\r
+                       //      content:\r
+                       //              the HTML in string format to add at position to every item\r
+                       //      position:\r
+                       //              One of:\r
+                       //                      "last"||"end" (default)\r
+                       //                      "first||"start"\r
+                       //                      "before"\r
+                       //                      "after"\r
+                       //              or an integer offset in the childNodes property\r
+                       var ta = d.doc.createElement("span");\r
+                       if(d.isString(content)){\r
+                               ta.innerHTML = content;\r
+                       }else{\r
+                               ta.appendChild(content);\r
+                       }\r
+                       var ct = ((position == "first")||(position == "after")) ? "lastChild" : "firstChild";\r
+                       this.forEach(function(item){\r
+                               var tn = ta.cloneNode(true);\r
+                               while(tn[ct]){\r
+                                       d.place(tn[ct], item, position);\r
+                               }\r
+                       });\r
+                       return this; // dojo.NodeList\r
+               }\r
+       });\r
+\r
+       // syntactic sugar for DOM events\r
+       d.forEach([\r
+               "blur", "click", "keydown", "keypress", "keyup", "mousedown", "mouseenter",\r
+               "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup"\r
+               ], function(evt){\r
+                       var _oe = "on"+evt;\r
+                       dojo.NodeList.prototype[_oe] = function(a, b){\r
+                               return this.connect(_oe, a, b);\r
+                       }\r
+                               // FIXME: should these events trigger publishes?\r
+                               /*\r
+                               return (a ? this.connect(_oe, a, b) : \r
+                                                       this.forEach(function(n){  \r
+                                                               // FIXME:\r
+                                                               //              listeners get buried by\r
+                                                               //              addEventListener and can't be dug back\r
+                                                               //              out to be triggered externally.\r
+                                                               // see:\r
+                                                               //              http://developer.mozilla.org/en/docs/DOM:element\r
+\r
+                                                               console.debug(n, evt, _oe);\r
+\r
+                                                               // FIXME: need synthetic event support!\r
+                                                               var _e = { target: n, faux: true, type: evt };\r
+                                                               // dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt });\r
+                                                               try{ n[evt](_e); }catch(e){ console.debug(e); }\r
+                                                               try{ n[_oe](_e); }catch(e){ console.debug(e); }\r
+                                                       })\r
+                               );\r
+                       }\r
+                       */\r
+               }\r
+       );\r
+\r
+})();\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.query"] = true;\r
+dojo.provide("dojo._base.query");\r
+\r
+\r
+/*\r
+       dojo.query() architectural overview:\r
+\r
+               dojo.query is a relatively full-featured CSS3 query library. It is\r
+               designed to take any valid CSS3 selector and return the nodes matching\r
+               the selector. To do this quickly, it processes queries in several\r
+               steps, applying caching where profitable.\r
+               \r
+               The steps (roughly in reverse order of the way they appear in the code):\r
+                       1.) check to see if we already have a "query dispatcher"\r
+                               - if so, use that with the given parameterization. Skip to step 4.\r
+                       2.) attempt to determine which branch to dispatch the query to:\r
+                               - JS (optimized DOM iteration)\r
+                               - xpath (for browsers that support it and where it's fast)\r
+                               - native (not available in any browser yet)\r
+                       3.) tokenize and convert to executable "query dispatcher"\r
+                               - this is where the lion's share of the complexity in the\r
+                                 system lies. In the DOM version, the query dispatcher is\r
+                                 assembled as a chain of "yes/no" test functions pertaining to\r
+                                 a section of a simple query statement (".blah:nth-child(odd)"\r
+                                 but not "div div", which is 2 simple statements). Individual\r
+                                 statement dispatchers are cached (to prevent re-definition)\r
+                                 as are entire dispatch chains (to make re-execution of the\r
+                                 same query fast)\r
+                               - in the xpath path, tokenization yeilds a concatenation of\r
+                                 parameterized xpath selectors. As with the DOM version, both\r
+                                 simple selector blocks and overall evaluators are cached to\r
+                                 prevent re-defintion\r
+                       4.) the resulting query dispatcher is called in the passed scope (by default the top-level document)\r
+                               - for DOM queries, this results in a recursive, top-down\r
+                                 evaluation of nodes based on each simple query section\r
+                               - xpath queries can, thankfully, be executed in one shot\r
+                       5.) matched nodes are pruned to ensure they are unique\r
+*/\r
+\r
+/*=====\r
+               //      summary:\r
+               //              returns nodes which match the given CSS3 selector, searching the\r
+               //              entire document by default but optionally taking a node to scope\r
+               //              the search by. Returns an instance of dojo.NodeList.\r
+               //      description:\r
+               //              dojo.query() is the swiss army knife of DOM node manipulation in\r
+               //              Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's\r
+               //              "$" function, dojo.query provides robust, high-performance\r
+               //              CSS-based node selector support with the option of scoping searches\r
+               //              to a particular sub-tree of a document.\r
+               //\r
+               //              Supported Selectors:\r
+               //              --------------------\r
+               //\r
+               //              dojo.query() supports a rich set of CSS3 selectors, including:\r
+               //\r
+               //                      * class selectors (e.g., ".foo")\r
+               //                      * node type selectors like "span"\r
+               //                      * " " descendant selectors\r
+               //                      * ">" child element selectors \r
+               //                      * "#foo" style ID selectors\r
+               //                      * "*" universal selector\r
+               //                      * attribute queries:\r
+               //                              * "[foo]" attribute presence selector\r
+               //                              * "[foo='bar']" attribute value exact match\r
+               //                              * "[foo~='bar']" attribute value list item match\r
+               //                              * "[foo^='bar']" attribute start match\r
+               //                              * "[foo$='bar']" attribute end match\r
+               //                              * "[foo*='bar']" attribute substring match\r
+               //                      * ":first-child", ":last-child" positional selectors\r
+               //                      * ":nth-child(n)", ":nth-child(2n+1)" style positional calculations\r
+               //                      * ":nth-child(even)", ":nth-child(odd)" positional selectors\r
+               //                      * ":not(...)" negation pseudo selectors\r
+               //\r
+               //              Any legal combination of those selector types as per the CSS 3 sepc\r
+               //              will work with dojo.query(), including compound selectors (","\r
+               //              delimited). Very complex and useful searches can be constructed\r
+               //              with this palette of selectors and when combined with functions for\r
+               //              maniplation presented by dojo.NodeList, many types of DOM\r
+               //              manipulation operations become very straightforward.\r
+               //              \r
+               //              Unsupported Selectors:\r
+               //              --------------------\r
+               //\r
+               //              While dojo.query handles many CSS3 selectors, some fall outside of\r
+               //              what's resaonable for a programmatic node querying engine to\r
+               //              handle. Currently unsupported selectors include:\r
+               //              \r
+               //                      * namespace-differentiated selectors of any form\r
+               //                      * "~", the immediately preceeded-by sibling selector\r
+               //                      * "+", the preceeded-by sibling selector\r
+               //                      * all "::" pseduo-element selectors\r
+               //                      * certain pseduo-selectors which don't get a lot of day-to-day use:\r
+               //                              * :root, :lang(), :target, :focus\r
+               //                      * all visual and state selectors:\r
+               //                              * :root, :active, :hover, :visisted, :link, :enabled, :disabled, :checked\r
+               //                      * :*-of-type pseudo selectors\r
+               //              \r
+               //              dojo.query and XML Documents:\r
+               //              -----------------------------\r
+               //              FIXME\r
+               //              \r
+               // NOTE: elementsById is not currently supported\r
+               // NOTE: ignores xpath-ish queries for now\r
+               //\r
+               //      query: String\r
+               //              The CSS3 expression to match against. For details on the syntax of\r
+               //              CSS3 selectors, see:\r
+               //                      http://www.w3.org/TR/css3-selectors/#selectors\r
+               //      root: String|DOMNode?\r
+               //              A node (or string ID of a node) to scope the search from. Optional.\r
+               //      returns:\r
+               //              An instance of dojo.NodeList. Many methods are available on\r
+               //              NodeLists for searching, iterating, manipulating, and handling\r
+               //              events on the matched nodes in the returned list.\r
+=====*/\r
+\r
+;(function(){\r
+       // define everything in a closure for compressability reasons. "d" is an\r
+       // alias to "dojo" since it's so frequently used. This seems a\r
+       // transformation that the build system could perform on a per-file basis.\r
+\r
+       ////////////////////////////////////////////////////////////////////////\r
+       // Utility code\r
+       ////////////////////////////////////////////////////////////////////////\r
+\r
+       var d = dojo;\r
+       var childNodesName = dojo.isIE ? "children" : "childNodes";\r
+\r
+       var getQueryParts = function(query){\r
+               // summary: state machine for query tokenization\r
+               if(query.charAt(query.length-1) == ">"){\r
+                       query += " *"\r
+               }\r
+               query += " "; // ensure that we terminate the state machine\r
+\r
+               var ts = function(s, e){\r
+                       return d.trim(query.slice(s, e));\r
+               }\r
+\r
+               // the overall data graph of the full query, as represented by queryPart objects\r
+               var qparts = []; \r
+               // state keeping vars\r
+               var inBrackets = -1;\r
+               var inParens = -1;\r
+               var inMatchFor = -1;\r
+               var inPseudo = -1;\r
+               var inClass = -1;\r
+               var inId = -1;\r
+               var inTag = -1;\r
+               var lc = ""; // the last character\r
+               var cc = ""; // the current character\r
+               var pStart;\r
+               // iteration vars\r
+               var x = 0; // index in the query\r
+               var ql = query.length;\r
+               var currentPart = null; // data structure representing the entire clause\r
+               var _cp = null; // the current pseudo or attr matcher\r
+\r
+               var endTag = function(){\r
+                       if(inTag >= 0){\r
+                               var tv = (inTag == x) ? null : ts(inTag, x).toLowerCase();\r
+                               currentPart[ (">~+".indexOf(tv) < 0)? "tag" : "oper" ] = tv;\r
+                               inTag = -1;\r
+                       }\r
+               }\r
+\r
+               var endId = function(){\r
+                       if(inId >= 0){\r
+                               currentPart.id = ts(inId, x).replace(/\\/g, "");\r
+                               inId = -1;\r
+                       }\r
+               }\r
+\r
+               var endClass = function(){\r
+                       if(inClass >= 0){\r
+                               currentPart.classes.push(ts(inClass+1, x).replace(/\\/g, ""));\r
+                               inClass = -1;\r
+                       }\r
+               }\r
+\r
+               var endAll = function(){\r
+                       endId(); endTag(); endClass();\r
+               }\r
+\r
+               for(; lc=cc, cc=query.charAt(x),x<ql; x++){\r
+                       if(lc == "\\"){ continue; }\r
+                       if(!currentPart){\r
+                               // NOTE: I hate all this alloc, but it's shorter than writing tons of if's\r
+                               pStart = x;\r
+                               currentPart = {\r
+                                       query: null,\r
+                                       pseudos: [],\r
+                                       attrs: [],\r
+                                       classes: [],\r
+                                       tag: null,\r
+                                       oper: null,\r
+                                       id: null\r
+                               };\r
+                               inTag = x;\r
+                       }\r
+\r
+                       if(inBrackets >= 0){\r
+                               // look for a the close first\r
+                               if(cc == "]"){\r
+                                       if(!_cp.attr){\r
+                                               _cp.attr = ts(inBrackets+1, x);\r
+                                       }else{\r
+                                               _cp.matchFor = ts((inMatchFor||inBrackets+1), x);\r
+                                       }\r
+                                       var cmf = _cp.matchFor;\r
+                                       if(cmf){\r
+                                               if(     (cmf.charAt(0) == '"') || (cmf.charAt(0)  == "'") ){\r
+                                                       _cp.matchFor = cmf.substring(1, cmf.length-1);\r
+                                               }\r
+                                       }\r
+                                       currentPart.attrs.push(_cp);\r
+                                       _cp = null; // necessaray?\r
+                                       inBrackets = inMatchFor = -1;\r
+                               }else if(cc == "="){\r
+                                       var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";\r
+                                       _cp.type = addToCc+cc;\r
+                                       _cp.attr = ts(inBrackets+1, x-addToCc.length);\r
+                                       inMatchFor = x+1;\r
+                               }\r
+                               // now look for other clause parts\r
+                       }else if(inParens >= 0){\r
+                               if(cc == ")"){\r
+                                       if(inPseudo >= 0){\r
+                                               _cp.value = ts(inParens+1, x);\r
+                                       }\r
+                                       inPseudo = inParens = -1;\r
+                               }\r
+                       }else if(cc == "#"){\r
+                               endAll();\r
+                               inId = x+1;\r
+                       }else if(cc == "."){\r
+                               endAll();\r
+                               inClass = x;\r
+                       }else if(cc == ":"){\r
+                               endAll();\r
+                               inPseudo = x;\r
+                       }else if(cc == "["){\r
+                               endAll();\r
+                               inBrackets = x;\r
+                               _cp = {\r
+                                       /*=====\r
+                                       attr: null, type: null, matchFor: null\r
+                                       =====*/\r
+                               };\r
+                       }else if(cc == "("){\r
+                               if(inPseudo >= 0){\r
+                                       _cp = { \r
+                                               name: ts(inPseudo+1, x), \r
+                                               value: null\r
+                                       }\r
+                                       currentPart.pseudos.push(_cp);\r
+                               }\r
+                               inParens = x;\r
+                       }else if(cc == " " && lc != cc){\r
+                               // note that we expect the string to be " " terminated\r
+                               endAll();\r
+                               if(inPseudo >= 0){\r
+                                       currentPart.pseudos.push({ name: ts(inPseudo+1, x) });\r
+                               }\r
+                               currentPart.hasLoops = (        \r
+                                               currentPart.pseudos.length || \r
+                                               currentPart.attrs.length || \r
+                                               currentPart.classes.length      );\r
+                               currentPart.query = ts(pStart, x);\r
+                               currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");\r
+                               qparts.push(currentPart);\r
+                               currentPart = null;\r
+                       }\r
+               }\r
+               return qparts;\r
+       };\r
+       \r
+\r
+       ////////////////////////////////////////////////////////////////////////\r
+       // XPath query code\r
+       ////////////////////////////////////////////////////////////////////////\r
+\r
+       // this array is a lookup used to generate an attribute matching function.\r
+       // There is a similar lookup/generator list for the DOM branch with similar\r
+       // calling semantics.\r
+       var xPathAttrs = {\r
+               "*=": function(attr, value){\r
+                       return "[contains(@"+attr+", '"+ value +"')]";\r
+               },\r
+               "^=": function(attr, value){\r
+                       return "[starts-with(@"+attr+", '"+ value +"')]";\r
+               },\r
+               "$=": function(attr, value){\r
+                       return "[substring(@"+attr+", string-length(@"+attr+")-"+(value.length-1)+")='"+value+"']";\r
+               },\r
+               "~=": function(attr, value){\r
+                       return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";\r
+               },\r
+               "|=": function(attr, value){\r
+                       return "[contains(concat(' ',@"+attr+",' '), ' "+ value +"-')]";\r
+               },\r
+               "=": function(attr, value){\r
+                       return "[@"+attr+"='"+ value +"']";\r
+               }\r
+       };\r
+\r
+       // takes a list of attribute searches, the overall query, a function to\r
+       // generate a default matcher, and a closure-bound method for providing a\r
+       // matching function that generates whatever type of yes/no distinguisher\r
+       // the query method needs. The method is a bit tortured and hard to read\r
+       // because it needs to be used in both the XPath and DOM branches.\r
+       var handleAttrs = function(     attrList, \r
+                                                               query, \r
+                                                               getDefault, \r
+                                                               handleMatch){\r
+               d.forEach(query.attrs, function(attr){\r
+                       var matcher;\r
+                       // type, attr, matchFor\r
+                       if(attr.type && attrList[attr.type]){\r
+                               matcher = attrList[attr.type](attr.attr, attr.matchFor);\r
+                       }else if(attr.attr.length){\r
+                               matcher = getDefault(attr.attr);\r
+                       }\r
+                       if(matcher){ handleMatch(matcher); }\r
+               });\r
+       }\r
+\r
+       var buildPath = function(query){\r
+               var xpath = ".";\r
+               var qparts = getQueryParts(d.trim(query));\r
+               while(qparts.length){\r
+                       var tqp = qparts.shift();\r
+                       var prefix;\r
+                       // FIXME: need to add support for ~ and +\r
+                       if(tqp.oper == ">"){\r
+                               prefix = "/";\r
+                               // prefix = "/child::node()";\r
+                               tqp = qparts.shift();\r
+                       }else{\r
+                               prefix = "//";\r
+                               // prefix = "/descendant::node()"\r
+                       }\r
+\r
+                       // get the tag name (if any)\r
+\r
+                       xpath += prefix + tqp.tag;\r
+                       \r
+                       // check to see if it's got an id. Needs to come first in xpath.\r
+                       if(tqp.id){\r
+                               xpath += "[@id='"+tqp.id+"'][1]";\r
+                       }\r
+\r
+                       d.forEach(tqp.classes, function(cn){\r
+                               var cnl = cn.length;\r
+                               var padding = " ";\r
+                               if(cn.charAt(cnl-1) == "*"){\r
+                                       padding = ""; cn = cn.substr(0, cnl-1);\r
+                               }\r
+                               xpath += \r
+                                       "[contains(concat(' ',@class,' '), ' "+\r
+                                       cn + padding + "')]";\r
+                       });\r
+\r
+                       handleAttrs(xPathAttrs, tqp, \r
+                               function(condition){\r
+                                               return "[@"+condition+"]";\r
+                               },\r
+                               function(matcher){\r
+                                       xpath += matcher;\r
+                               }\r
+                       );\r
+\r
+                       // FIXME: need to implement pseudo-class checks!!\r
+               };\r
+               return xpath;\r
+       };\r
+\r
+       var _xpathFuncCache = {};\r
+       var getXPathFunc = function(path){\r
+               if(_xpathFuncCache[path]){\r
+                       return _xpathFuncCache[path];\r
+               }\r
+\r
+               var doc = d.doc;\r
+               // var parent = d.body(); // FIXME\r
+               // FIXME: don't need to memoize. The closure scope handles it for us.\r
+               var xpath = buildPath(path);\r
+\r
+               var tf = function(parent){\r
+                       // XPath query strings are memoized.\r
+                       var ret = [];\r
+                       var xpathResult;\r
+                       try{\r
+                               xpathResult = doc.evaluate(xpath, parent, null, \r
+                                                                                               // XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);\r
+                                                                                               XPathResult.ANY_TYPE, null);\r
+                       }catch(e){\r
+                               console.debug("failure in exprssion:", xpath, "under:", parent);\r
+                               console.debug(e);\r
+                       }\r
+                       var result = xpathResult.iterateNext();\r
+                       while(result){\r
+                               ret.push(result);\r
+                               result = xpathResult.iterateNext();\r
+                       }\r
+                       return ret;\r
+               }\r
+               return _xpathFuncCache[path] = tf;\r
+       };\r
+\r
+       /*\r
+       d.xPathMatch = function(query){\r
+               // XPath based DOM query system. Handles a small subset of CSS\r
+               // selectors, subset is identical to the non-XPath version of this\r
+               // function. \r
+\r
+               // FIXME: need to add support for alternate roots\r
+               return getXPathFunc(query)();\r
+       }\r
+       */\r
+\r
+       ////////////////////////////////////////////////////////////////////////\r
+       // DOM query code\r
+       ////////////////////////////////////////////////////////////////////////\r
+\r
+       var _filtersCache = {};\r
+       var _simpleFiltersCache = {};\r
+\r
+       // the basic building block of the yes/no chaining system. agree(f1, f2)\r
+       // generates a new function which returns the boolean results of both of\r
+       // the passed functions to a single logical-anded result.\r
+       var agree = function(first, second){\r
+               if(!first){ return second; }\r
+               if(!second){ return first; }\r
+\r
+               return function(){\r
+                       return first.apply(window, arguments) && second.apply(window, arguments);\r
+               }\r
+       }\r
+\r
+       var _filterDown = function(element, queryParts, matchArr, idx){\r
+               var nidx = idx+1;\r
+               var isFinal = (queryParts.length == nidx);\r
+               var tqp = queryParts[idx];\r
+\r
+               // see if we can constrain our next level to direct children\r
+               if(tqp.oper == ">"){\r
+                       var ecn = element[childNodesName];\r
+                       if(!ecn || !ecn.length){\r
+                               return;\r
+                       }\r
+                       nidx++;\r
+                       isFinal = (queryParts.length == nidx);\r
+                       // kinda janky, too much array alloc\r
+                       var tf = getFilterFunc(queryParts[idx+1]);\r
+                       // for(var x=ecn.length-1, te; x>=0, te=ecn[x]; x--){\r
+                       for(var x=0, ecnl=ecn.length, te; x<ecnl, te=ecn[x]; x++){\r
+                               if(tf(te)){\r
+                                       if(isFinal){\r
+                                               matchArr.push(te);\r
+                                       }else{\r
+                                               _filterDown(te, queryParts, matchArr, nidx);\r
+                                       }\r
+                               }\r
+                               /*\r
+                               if(x==0){\r
+                                       break;\r
+                               }\r
+                               */\r
+                       }\r
+               }\r
+\r
+               // otherwise, keep going down, unless we'er at the end\r
+               var candidates = getElementsFunc(tqp)(element);\r
+               if(isFinal){\r
+                       while(candidates.length){\r
+                               matchArr.push(candidates.shift());\r
+                       }\r
+                       /*\r
+                       candidates.unshift(0, matchArr.length-1);\r
+                       matchArr.splice.apply(matchArr, candidates);\r
+                       */\r
+               }else{\r
+                       // if we're not yet at the bottom, keep going!\r
+                       while(candidates.length){\r
+                               _filterDown(candidates.shift(), queryParts, matchArr, nidx);\r
+                       }\r
+               }\r
+       }\r
+\r
+       var filterDown = function(elements, queryParts){\r
+               var ret = [];\r
+\r
+               // for every root, get the elements that match the descendant selector\r
+               // for(var x=elements.length-1, te; x>=0, te=elements[x]; x--){\r
+               var x = elements.length - 1, te;\r
+               while(te = elements[x--]){\r
+                       _filterDown(te, queryParts, ret, 0);\r
+               }\r
+               return ret;\r
+       }\r
+\r
+       var getFilterFunc = function(q){\r
+               // note: query can't have spaces!\r
+               if(_filtersCache[q.query]){\r
+                       return _filtersCache[q.query];\r
+               }\r
+               var ff = null;\r
+\r
+               // does it have a tagName component?\r
+               if(q.tag){\r
+                       if(q.tag == "*"){\r
+                               ff = agree(ff, \r
+                                       function(elem){\r
+                                               return (elem.nodeType == 1);\r
+                                       }\r
+                               );\r
+                       }else{\r
+                               // tag name match\r
+                               ff = agree(ff, \r
+                                       function(elem){\r
+                                               return (\r
+                                                       (elem.nodeType == 1) &&\r
+                                                       (q.tag == elem.tagName.toLowerCase())\r
+                                               );\r
+                                               // return isTn;\r
+                                       }\r
+                               );\r
+                       }\r
+               }\r
+\r
+               // does the node have an ID?\r
+               if(q.id){\r
+                       ff = agree(ff, \r
+                               function(elem){\r
+                                       return (\r
+                                               (elem.nodeType == 1) &&\r
+                                               (elem.id == q.id)\r
+                                       );\r
+                               }\r
+                       );\r
+               }\r
+\r
+               if(q.hasLoops){\r
+                       // if we have other query param parts, make sure we add them to the\r
+                       // filter chain\r
+                       ff = agree(ff, getSimpleFilterFunc(q));\r
+               }\r
+\r
+               return _filtersCache[q.query] = ff;\r
+       }\r
+\r
+       var getNodeIndex = function(node){\r
+               // NOTE: \r
+               //              we could have a more accurate caching mechanism by invalidating\r
+               //              caches after the query has finished, but I think that'd lead to\r
+               //              significantly more cache churn than the cache would provide\r
+               //              value for in the common case. Generally, we're more\r
+               //              conservative (and therefore, more accurate) than jQuery and\r
+               //              DomQuery WRT node node indexes, but there may be corner cases\r
+               //              in which we fall down.  How much we care about them is TBD.\r
+\r
+               var pn = node.parentNode;\r
+               var pnc = pn.childNodes;\r
+\r
+               // check to see if we can trust the cache. If not, re-key the whole\r
+               // thing and return our node match from that.\r
+\r
+               var nidx = -1;\r
+               var child = pn.firstChild;\r
+               if(!child){\r
+                       return nidx;\r
+               }\r
+\r
+               var ci = node["__cachedIndex"];\r
+               var cl = pn["__cachedLength"];\r
+\r
+               // only handle cache building if we've gone out of sync\r
+               if(((typeof cl == "number")&&(cl != pnc.length))||(typeof ci != "number")){\r
+                       // rip though the whole set, building cache indexes as we go\r
+                       pn["__cachedLength"] = pnc.length;\r
+                       var idx = 1;\r
+                       do{\r
+                               // we only assign indexes for nodes with nodeType == 1, as per:\r
+                               //              http://www.w3.org/TR/css3-selectors/#nth-child-pseudo\r
+                               // only elements are counted in the search order, and they\r
+                               // begin at 1 for the first child's index\r
+\r
+                               if(child === node){\r
+                                       nidx = idx;\r
+                               }\r
+                               if(child.nodeType == 1){\r
+                                       child["__cachedIndex"] = idx;\r
+                                       idx++;\r
+                               }\r
+                               child = child.nextSibling;\r
+                       }while(child);\r
+               }else{\r
+                       // NOTE: \r
+                       //              could be incorrect in some cases (node swaps involving the\r
+                       //              passed node, etc.), but we ignore those due to the relative\r
+                       //              unlikelihood of that occuring\r
+                       nidx = ci;\r
+               }\r
+               return nidx;\r
+       }\r
+\r
+       var firedCount = 0;\r
+\r
+       var blank = "";\r
+       var _getAttr = function(elem, attr){\r
+               if(attr == "class"){\r
+                       return elem.className || blank;\r
+               }\r
+               if(attr == "for"){\r
+                       return elem.htmlFor || blank;\r
+               }\r
+               return elem.getAttribute(attr, 2) || blank;\r
+       }\r
+\r
+       var attrs = {\r
+               "*=": function(attr, value){\r
+                       return function(elem){\r
+                               // E[foo*="bar"]\r
+                               //              an E element whose "foo" attribute value contains\r
+                               //              the substring "bar"\r
+                               return (_getAttr(elem, attr).indexOf(value)>=0);\r
+                       }\r
+               },\r
+               "^=": function(attr, value){\r
+                       // E[foo^="bar"]\r
+                       //              an E element whose "foo" attribute value begins exactly\r
+                       //              with the string "bar"\r
+                       return function(elem){\r
+                               return (_getAttr(elem, attr).indexOf(value)==0);\r
+                       }\r
+               },\r
+               "$=": function(attr, value){\r
+                       // E[foo$="bar"]        \r
+                       //              an E element whose "foo" attribute value ends exactly\r
+                       //              with the string "bar"\r
+                       var tval = " "+value;\r
+                       return function(elem){\r
+                               var ea = " "+_getAttr(elem, attr);\r
+                               return (ea.lastIndexOf(value)==(ea.length-value.length));\r
+                       }\r
+               },\r
+               "~=": function(attr, value){\r
+                       // E[foo~="bar"]        \r
+                       //              an E element whose "foo" attribute value is a list of\r
+                       //              space-separated values, one of which is exactly equal\r
+                       //              to "bar"\r
+\r
+                       // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";\r
+                       var tval = " "+value+" ";\r
+                       return function(elem){\r
+                               var ea = " "+_getAttr(elem, attr)+" ";\r
+                               return (ea.indexOf(tval)>=0);\r
+                       }\r
+               },\r
+               "|=": function(attr, value){\r
+                       // E[hreflang|="en"]\r
+                       //              an E element whose "hreflang" attribute has a\r
+                       //              hyphen-separated list of values beginning (from the\r
+                       //              left) with "en"\r
+                       var valueDash = " "+value+"-";\r
+                       return function(elem){\r
+                               var ea = " "+(elem.getAttribute(attr, 2) || "");\r
+                               return (\r
+                                       (ea == value) ||\r
+                                       (ea.indexOf(valueDash)==0)\r
+                               );\r
+                       }\r
+               },\r
+               "=": function(attr, value){\r
+                       return function(elem){\r
+                               return (_getAttr(elem, attr) == value);\r
+                       }\r
+               }\r
+       };\r
+\r
+       var pseudos = {\r
+               "first-child": function(name, condition){\r
+                       return function(elem){\r
+                               if(elem.nodeType != 1){ return false; }\r
+                               // check to see if any of the previous siblings are elements\r
+                               var fc = elem.previousSibling;\r
+                               while(fc && (fc.nodeType != 1)){\r
+                                       fc = fc.previousSibling;\r
+                               }\r
+                               return (!fc);\r
+                       }\r
+               },\r
+               "last-child": function(name, condition){\r
+                       return function(elem){\r
+                               if(elem.nodeType != 1){ return false; }\r
+                               // check to see if any of the next siblings are elements\r
+                               var nc = elem.nextSibling;\r
+                               while(nc && (nc.nodeType != 1)){\r
+                                       nc = nc.nextSibling;\r
+                               }\r
+                               return (!nc);\r
+                       }\r
+               },\r
+               "empty": function(name, condition){\r
+                       return function(elem){\r
+                               // DomQuery and jQuery get this wrong, oddly enough.\r
+                               // The CSS 3 selectors spec is pretty explicit about\r
+                               // it, too.\r
+                               var cn = elem.childNodes;\r
+                               var cnl = elem.childNodes.length;\r
+                               // if(!cnl){ return true; }\r
+                               for(var x=cnl-1; x >= 0; x--){\r
+                                       var nt = cn[x].nodeType;\r
+                                       if((nt == 1)||(nt == 3)){ return false; }\r
+                               }\r
+                               return true;\r
+                       }\r
+               },\r
+               /* non standard!\r
+               "contains": function(name, condition){\r
+                       return function(elem){\r
+                               // FIXME: I dislike this version of "contains", as\r
+                               // whimsical attribute could set it off. An inner-text\r
+                               // based version might be more accurate, but since\r
+                               // jQuery and DomQuery also potentially get this wrong,\r
+                               // I'm leaving it for now.\r
+                               return (elem.innerHTML.indexOf(condition) >= 0);\r
+                       }\r
+               },\r
+               */\r
+               "not": function(name, condition){\r
+                       var ntf = getFilterFunc(getQueryParts(condition)[0]);\r
+                       return function(elem){\r
+                               return (!ntf(elem));\r
+                       }\r
+               },\r
+               "nth-child": function(name, condition){\r
+                       var pi = parseInt;\r
+                       if(condition == "odd"){\r
+                               return function(elem){\r
+                                       return (\r
+                                               ((getNodeIndex(elem)) % 2) == 1\r
+                                       );\r
+                               }\r
+                       }else if((condition == "2n")||\r
+                               (condition == "even")){\r
+                               return function(elem){\r
+                                       return ((getNodeIndex(elem) % 2) == 0);\r
+                               }\r
+                       }else if(condition.indexOf("0n+") == 0){\r
+                               var ncount = pi(condition.substr(3));\r
+                               return function(elem){\r
+                                       return (elem.parentNode[childNodesName][ncount-1] === elem);\r
+                               }\r
+                       }else if(       (condition.indexOf("n+") > 0) &&\r
+                                               (condition.length > 3) ){\r
+                               var tparts = condition.split("n+", 2);\r
+                               var pred = pi(tparts[0]);\r
+                               var idx = pi(tparts[1]);\r
+                               return function(elem){\r
+                                       return ((getNodeIndex(elem) % pred) == idx);\r
+                               }\r
+                       }else if(condition.indexOf("n") == -1){\r
+                               var ncount = pi(condition);\r
+                               return function(elem){\r
+                                       return (getNodeIndex(elem) == ncount);\r
+                               }\r
+                       }\r
+               }\r
+       };\r
+\r
+       var defaultGetter = (d.isIE) ? function(cond){\r
+               var clc = cond.toLowerCase();\r
+               return function(elem){\r
+                       return elem[cond]||elem[clc];\r
+               }\r
+       } : function(cond){\r
+               return function(elem){\r
+                       return (elem && elem.getAttribute && elem.hasAttribute(cond));\r
+               }\r
+       };\r
+\r
+       var getSimpleFilterFunc = function(query){\r
+\r
+               var fcHit = (_simpleFiltersCache[query.query]||_filtersCache[query.query]);\r
+               if(fcHit){ return fcHit; }\r
+\r
+               var ff = null;\r
+\r
+               // the only case where we'll need the tag name is if we came from an ID query\r
+               if(query.id){ // do we have an ID component?\r
+                       if(query.tag != "*"){\r
+                               ff = agree(ff, function(elem){\r
+                                       return (elem.tagName.toLowerCase() == query.tag);\r
+                               });\r
+                       }\r
+               }\r
+\r
+               // if there's a class in our query, generate a match function for it\r
+               d.forEach(query.classes, function(cname, idx, arr){\r
+                       // get the class name\r
+                       var isWildcard = cname.charAt(cname.length-1) == "*";\r
+                       if(isWildcard){\r
+                               cname = cname.substr(0, cname.length-1);\r
+                       }\r
+                       // I dislike the regex thing, even if memozied in a cache, but it's VERY short\r
+                       var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");\r
+                       ff = agree(ff, function(elem){\r
+                               return re.test(elem.className);\r
+                       });\r
+                       ff.count = idx;\r
+               });\r
+\r
+               d.forEach(query.pseudos, function(pseudo){\r
+                       if(pseudos[pseudo.name]){\r
+                               ff = agree(ff, pseudos[pseudo.name](pseudo.name, pseudo.value));\r
+                       }\r
+               });\r
+\r
+               handleAttrs(attrs, query, defaultGetter,\r
+                       function(tmatcher){ ff = agree(ff, tmatcher); }\r
+               );\r
+               if(!ff){\r
+                       ff = function(){ return true; };\r
+               }\r
+               return _simpleFiltersCache[query.query] = ff;\r
+       }\r
+\r
+       var _getElementsFuncCache = { };\r
+\r
+       var getElementsFunc = function(query, root){\r
+               var fHit = _getElementsFuncCache[query.query];\r
+               if(fHit){ return fHit; }\r
+\r
+               // NOTE: this function is in the fast path! not memoized!!!\r
+\r
+               // the query doesn't contain any spaces, so there's only so many\r
+               // things it could be\r
+\r
+               if(query.id && !query.hasLoops && !query.tag){\r
+                       // ID-only query. Easy.\r
+                       return _getElementsFuncCache[query.query] = function(root){\r
+                               // FIXME: if root != document, check for parenting!\r
+                               return [ d.byId(query.id) ];\r
+                       }\r
+               }\r
+\r
+               var filterFunc = getSimpleFilterFunc(query);\r
+\r
+               var retFunc;\r
+               if(query.tag && query.id && !query.hasLoops){\r
+                       // we got a filtered ID search (e.g., "h4#thinger")\r
+                       retFunc = function(root){\r
+                               var te = d.byId(query.id);\r
+                               if(filterFunc(te)){\r
+                                       return [ te ];\r
+                               }\r
+                       }\r
+               }else{\r
+                       var tret;\r
+\r
+                       if(!query.hasLoops){\r
+                               // it's just a plain-ol elements-by-tag-name query from the root\r
+                               retFunc = function(root){\r
+                                       var ret = [];\r
+                                       var te, x=0, tret = root.getElementsByTagName(query.tag);\r
+                                       while(te=tret[x++]){\r
+                                               ret.push(te);\r
+                                       }\r
+                                       return ret;\r
+                               }\r
+                       }else{\r
+                               retFunc = function(root){\r
+                                       var ret = [];\r
+                                       var te, x=0, tret = root.getElementsByTagName(query.tag);\r
+                                       while(te=tret[x++]){\r
+                                               if(filterFunc(te)){\r
+                                                       ret.push(te);\r
+                                               }\r
+                                       }\r
+                                       return ret;\r
+                               }\r
+                       }\r
+               }\r
+               return _getElementsFuncCache[query.query] = retFunc;\r
+       }\r
+\r
+       var _partsCache = {};\r
+\r
+       ////////////////////////////////////////////////////////////////////////\r
+       // the query runner\r
+       ////////////////////////////////////////////////////////////////////////\r
+\r
+       // this is the second level of spliting, from full-length queries (e.g.,\r
+       // "div.foo .bar") into simple query expressions (e.g., ["div.foo",\r
+       // ".bar"])\r
+       var _queryFuncCache = {\r
+               "*": d.isIE ? \r
+                       function(root){ \r
+                                       return root.all;\r
+                       } : \r
+                       function(root){\r
+                                return root.getElementsByTagName("*");\r
+                       },\r
+               ">": function(root){\r
+                       var ret = [];\r
+                       var te, x=0, tret = root[childNodesName];\r
+                       while(te=tret[x++]){\r
+                               if(te.nodeType == 1){ ret.push(te); }\r
+                       }\r
+                       return ret;\r
+               }\r
+       };\r
+\r
+       var getStepQueryFunc = function(query){\r
+               // if it's trivial, get a fast-path dispatcher\r
+               var qparts = getQueryParts(d.trim(query));\r
+               // if(query[query.length-1] == ">"){ query += " *"; }\r
+               if(qparts.length == 1){\r
+                       var tt = getElementsFunc(qparts[0]);\r
+                       tt.nozip = true;\r
+                       return tt;\r
+               }\r
+\r
+               // otherwise, break it up and return a runner that iterates over the parts recursively\r
+               var sqf = function(root){\r
+                       var localQueryParts = qparts.slice(0); // clone the src arr\r
+                       var candidates;\r
+                       if(localQueryParts[0].oper == ">"){\r
+                               candidates = [ root ];\r
+                               // root = document;\r
+                       }else{\r
+                               candidates = getElementsFunc(localQueryParts.shift())(root);\r
+                       }\r
+                       return filterDown(candidates, localQueryParts);\r
+               }\r
+               return sqf;\r
+       }\r
+\r
+       // a specialized method that implements our primoridal "query optimizer".\r
+       // This allows us to dispatch queries to the fastest subsystem we can get.\r
+       var _getQueryFunc = (\r
+               // NOTE: \r
+               //              XPath on the Webkit nighlies is slower than it's DOM iteration\r
+               //              for most test cases\r
+               // FIXME: \r
+               //              we should try to capture some runtime speed data for each query\r
+               //              function to determine on the fly if we should stick w/ the\r
+               //              potentially optimized variant or if we should try something\r
+               //              new.\r
+               (document["evaluate"] && !d.isSafari) ? \r
+               function(query){\r
+                       // has xpath support that's faster than DOM\r
+                       var qparts = query.split(" ");\r
+                       // can we handle it?\r
+                       if(     (document["evaluate"])&&\r
+                               (query.indexOf(":") == -1)&&\r
+                               (\r
+                                       (true) // ||\r
+                                       // (query.indexOf("[") == -1) ||\r
+                                       // (query.indexOf("=") == -1)\r
+                               )\r
+                       ){\r
+                               // dojo.debug(query);\r
+                               // should we handle it?\r
+\r
+                               // kind of a lame heuristic, but it works\r
+                               if(     \r
+                                       // a "div div div" style query\r
+                                       ((qparts.length > 2)&&(query.indexOf(">") == -1))||\r
+                                       // or something else with moderate complexity. kinda janky\r
+                                       (qparts.length > 3)||\r
+                                       (query.indexOf("[")>=0)||\r
+                                       // or if it's a ".thinger" query\r
+                                       ((1 == qparts.length)&&(0 <= query.indexOf(".")))\r
+\r
+                               ){\r
+                                       // use get and cache a xpath runner for this selector\r
+                                       return getXPathFunc(query);\r
+                               }\r
+                       }\r
+\r
+                       // fallthrough\r
+                       return getStepQueryFunc(query);\r
+               } : getStepQueryFunc\r
+       );\r
+       // uncomment to disable XPath for testing and tuning the DOM path\r
+       // _getQueryFunc = getStepQueryFunc;\r
+\r
+       // FIXME: we've got problems w/ the NodeList query()/filter() functions if we go XPath for everything\r
+\r
+       // uncomment to disable DOM queries for testing and tuning XPath\r
+       // _getQueryFunc = getXPathFunc;\r
+\r
+       // this is the primary caching for full-query results. The query dispatcher\r
+       // functions are generated here and then pickled for hash lookup in the\r
+       // future\r
+       var getQueryFunc = function(query){\r
+               // return a cached version if one is available\r
+               if(_queryFuncCache[query]){ return _queryFuncCache[query]; }\r
+               if(0 > query.indexOf(",")){\r
+                       // if it's not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher\r
+                       return _queryFuncCache[query] = _getQueryFunc(query);\r
+               }else{\r
+                       // if it's a complex query, break it up into it's constituent parts\r
+                       // and return a dispatcher that will merge the parts when run\r
+\r
+                       // var parts = query.split(", ");\r
+                       var parts = query.split(/\s*,\s*/);\r
+                       var tf = function(root){\r
+                               var pindex = 0; // avoid array alloc for every invocation\r
+                               var ret = [];\r
+                               var tp;\r
+                               while(tp = parts[pindex++]){\r
+                                       ret = ret.concat(_getQueryFunc(tp, tp.indexOf(" "))(root));\r
+                               }\r
+                               return ret;\r
+                       }\r
+                       // ...cache and return\r
+                       return _queryFuncCache[query] = tf;\r
+               }\r
+       }\r
+\r
+       // FIXME: \r
+       //              Dean's new Base2 uses a system whereby queries themselves note if\r
+       //              they'll need duplicate filtering. We need to get on that plan!!\r
+\r
+       // attempt to efficiently determine if an item in a list is a dupe,\r
+       // returning a list of "uniques", hopefully in doucment order\r
+       var _zipIdx = 0;\r
+       var _zip = function(arr){\r
+               if(arr && arr.nozip){ return d.NodeList._wrap(arr); }\r
+               var ret = new d.NodeList();\r
+               if(!arr){ return ret; }\r
+               if(arr[0]){\r
+                       ret.push(arr[0]);\r
+               }\r
+               if(arr.length < 2){ return ret; }\r
+               _zipIdx++;\r
+               arr[0]["_zipIdx"] = _zipIdx;\r
+               for(var x=1, te; te = arr[x]; x++){\r
+                       if(arr[x]["_zipIdx"] != _zipIdx){ \r
+                               ret.push(te);\r
+                       }\r
+                       te["_zipIdx"] = _zipIdx;\r
+               }\r
+               // FIXME: should we consider stripping these properties?\r
+               return ret;\r
+       }\r
+\r
+       // the main executor\r
+       d.query = function(query, root){\r
+               // see dojo.query for docs\r
+\r
+               if(query.constructor == d.NodeList){\r
+                       return query;\r
+               }\r
+               if(!d.isString(query)){\r
+                       return new d.NodeList(query); // dojo.NodeList\r
+               }\r
+               if(d.isString(root)){\r
+                       root = d.byId(root);\r
+               }\r
+\r
+               // FIXME: should support more methods on the return than the stock array.\r
+               return _zip(getQueryFunc(query)(root||d.doc));\r
+       }\r
+\r
+       /*\r
+       // exposing these was a mistake\r
+       d.query.attrs = attrs;\r
+       d.query.pseudos = pseudos;\r
+       */\r
+\r
+       // one-off function for filtering a NodeList based on a simple selector\r
+       d._filterQueryResult = function(nodeList, simpleFilter){\r
+               var tnl = new d.NodeList();\r
+               var ff = (simpleFilter) ? getFilterFunc(getQueryParts(simpleFilter)[0]) : function(){ return true; };\r
+               for(var x=0, te; te = nodeList[x]; x++){\r
+                       if(ff(te)){ tnl.push(te); }\r
+               }\r
+               return tnl;\r
+       }\r
+})();\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.xhr"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.xhr"] = true;\r
+dojo.provide("dojo._base.xhr");\r
+\r
+\r
+\r
+\r
+\r
+(function(){\r
+       var _d = dojo;\r
+       function setValue(/*Object*/obj, /*String*/name, /*String*/value){\r
+               //summary:\r
+               //              For the nameed property in object, set the value. If a value\r
+               //              already exists and it is a string, convert the value to be an\r
+               //              array of values.\r
+               var val = obj[name];\r
+               if(_d.isString(val)){\r
+                       obj[name] = [val, value];\r
+               }else if(_d.isArray(val)){\r
+                       val.push(value);\r
+               }else{\r
+                       obj[name] = value;\r
+               }\r
+       }\r
+\r
+       dojo.formToObject = function(/*DOMNode||String*/ formNode){\r
+               // summary:\r
+               //              dojo.formToObject returns the values encoded in an HTML form as\r
+               //              string properties in an object which it then returns. Disabled form\r
+               //              elements, buttons, and other non-value form elements are skipped.\r
+               //              Multi-select elements are returned as an array of string values.\r
+               // description:\r
+               //              This form:\r
+               //\r
+               //                      <form id="test_form">\r
+               //                              <input type="text" name="blah" value="blah">\r
+               //                              <input type="text" name="no_value" value="blah" disabled>\r
+               //                              <input type="button" name="no_value2" value="blah">\r
+               //                              <select type="select" multiple name="multi" size="5">\r
+               //                                      <option value="blah">blah</option>\r
+               //                                      <option value="thud" selected>thud</option>\r
+               //                                      <option value="thonk" selected>thonk</option>\r
+               //                              </select>\r
+               //                      </form>\r
+               //\r
+               //              yields this object structure as the result of a call to\r
+               //              formToObject():\r
+               //\r
+               //                      { \r
+               //                              blah: "blah",\r
+               //                              multi: [\r
+               //                                      "thud",\r
+               //                                      "thonk"\r
+               //                              ]\r
+               //                      };\r
+       \r
+               var ret = {};\r
+               var iq = "input:not([type=file]):not([type=submit]):not([type=image]):not([type=reset]):not([type=button]), select, textarea";\r
+               _d.query(iq, formNode).filter(function(node){\r
+                       return !node.disabled;\r
+               }).forEach(function(item){\r
+                       var _in = item.name;\r
+                       var type = (item.type||"").toLowerCase();\r
+                       if(type == "radio" || type == "checkbox"){\r
+                               if(item.checked){ setValue(ret, _in, item.value); }\r
+                       }else if(item.multiple){\r
+                               ret[_in] = [];\r
+                               _d.query("option", item).forEach(function(opt){\r
+                                       if(opt.selected){\r
+                                               setValue(ret, _in, opt.value);\r
+                                       }\r
+                               });\r
+                       }else{ \r
+                               setValue(ret, _in, item.value);\r
+                               if(type == "image"){\r
+                                       ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0;\r
+                               }\r
+                       }\r
+               });\r
+               return ret; // Object\r
+       }\r
+\r
+       dojo.objectToQuery = function(/*Object*/ map){\r
+               //      summary:\r
+               //              takes a name/value mapping object and returns a string representing\r
+               //              a URL-encoded version of that object.\r
+               //      example:\r
+               //              this object:\r
+               //\r
+               //              |       { \r
+               //              |               blah: "blah",\r
+               //              |               multi: [\r
+               //              |                       "thud",\r
+               //              |                       "thonk"\r
+               //              |               ]\r
+               //              |       };\r
+               //\r
+               //      yields the following query string:\r
+               //      \r
+               //      |       "blah=blah&multi=thud&multi=thonk"\r
+\r
+               // FIXME: need to implement encodeAscii!!\r
+               var enc = encodeURIComponent;\r
+               var pairs = [];\r
+               var backstop = {};\r
+               for(var name in map){\r
+                       var value = map[name];\r
+                       if(value != backstop[name]){\r
+                               var assign = enc(name) + "=";\r
+                               if(_d.isArray(value)){\r
+                                       for(var i=0; i < value.length; i++){\r
+                                               pairs.push(assign + enc(value[i]));\r
+                                       }\r
+                               }else{\r
+                                       pairs.push(assign + enc(value));\r
+                               }\r
+                       }\r
+               }\r
+               return pairs.join("&"); // String\r
+       }\r
+\r
+       dojo.formToQuery = function(/*DOMNode||String*/ formNode){\r
+               // summary:\r
+               //              return URL-encoded string representing the form passed as either a\r
+               //              node or string ID identifying the form to serialize\r
+               return _d.objectToQuery(_d.formToObject(formNode)); // String\r
+       }\r
+\r
+       dojo.formToJson = function(/*DOMNode||String*/ formNode, /*Boolean?*/prettyPrint){\r
+               // summary:\r
+               //              return a serialized JSON string from a form node or string\r
+               //              ID identifying the form to serialize\r
+               return _d.toJson(_d.formToObject(formNode), prettyPrint); // String\r
+       }\r
+\r
+       dojo.queryToObject = function(/*String*/ str){\r
+               // summary:\r
+               //              returns an object representing a de-serialized query section of a\r
+               //              URL. Query keys with multiple values are returned in an array.\r
+               // description:\r
+               //              This string:\r
+               //\r
+               //                      "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&"\r
+               //              \r
+               //              returns this object structure:\r
+               //\r
+               //                      {\r
+               //                              foo: [ "bar", "baz" ],\r
+               //                              thinger: " spaces =blah",\r
+               //                              zonk: "blarg"\r
+               //                      }\r
+               //      \r
+               //              Note that spaces and other urlencoded entities are correctly\r
+               //              handled.\r
+\r
+               // FIXME: should we grab the URL string if we're not passed one?\r
+               var ret = {};\r
+               var qp = str.split("&");\r
+               var dec = decodeURIComponent;\r
+               _d.forEach(qp, function(item){\r
+                       if(item.length){\r
+                               var parts = item.split("=");\r
+                               var name = dec(parts.shift());\r
+                               var val = dec(parts.join("="));\r
+                               if(_d.isString(ret[name])){\r
+                                       ret[name] = [ret[name]];\r
+                               }\r
+                               if(_d.isArray(ret[name])){\r
+                                       ret[name].push(val);\r
+                               }else{\r
+                                       ret[name] = val;\r
+                               }\r
+                       }\r
+               });\r
+               return ret; // Object\r
+       }\r
+\r
+       /*\r
+               from refactor.txt:\r
+\r
+               all bind() replacement APIs take the following argument structure:\r
+\r
+                       {\r
+                               url: "blah.html",\r
+\r
+                               // all below are optional, but must be supported in some form by\r
+                               // every IO API\r
+                               timeout: 1000, // milliseconds\r
+                               handleAs: "text", // replaces the always-wrong "mimetype"\r
+                               content: { \r
+                                       key: "value"\r
+                               },\r
+\r
+                               // browser-specific, MAY be unsupported\r
+                               sync: true, // defaults to false\r
+                               form: dojo.byId("someForm") \r
+                       }\r
+       */\r
+\r
+       // need to block async callbacks from snatching this thread as the result\r
+       // of an async callback might call another sync XHR, this hangs khtml forever\r
+       // must checked by watchInFlight()\r
+\r
+       dojo._blockAsync = false;\r
+\r
+       dojo._contentHandlers = {\r
+               "text": function(xhr){ return xhr.responseText; },\r
+               "json": function(xhr){\r
+                       if(!djConfig.usePlainJson){\r
+                               console.warn("Consider using mimetype:text/json-comment-filtered"\r
+                                       + " to avoid potential security issues with JSON endpoints"\r
+                                       + " (use djConfig.usePlainJson=true to turn off this message)");\r
+                       }\r
+                       return _d.fromJson(xhr.responseText);\r
+               },\r
+               "json-comment-filtered": function(xhr){ \r
+                       // NOTE: we provide the json-comment-filtered option as one solution to\r
+                       // the "JavaScript Hijacking" issue noted by Fortify and others. It is\r
+                       // not appropriate for all circumstances.\r
+\r
+                       var value = xhr.responseText;\r
+                       var cStartIdx = value.indexOf("\/*");\r
+                       var cEndIdx = value.lastIndexOf("*\/");\r
+                       if(cStartIdx == -1 || cEndIdx == -1){\r
+                               throw new Error("JSON was not comment filtered");\r
+                       }\r
+                       return _d.fromJson(value.substring(cStartIdx+2, cEndIdx));\r
+               },\r
+               "javascript": function(xhr){ \r
+                       // FIXME: try Moz and IE specific eval variants?\r
+                       return _d.eval(xhr.responseText);\r
+               },\r
+               "xml": function(xhr){ \r
+                       var result = xhr.responseXML;\r
+                       if(_d.isIE && (!result || window.location.protocol == "file:")){\r
+                               _d.forEach(["MSXML2", "Microsoft", "MSXML", "MSXML3"], function(prefix){\r
+                                       try{\r
+                                               var dom = new ActiveXObject(prefix + ".XMLDOM");\r
+                                               dom.async = false;\r
+                                               dom.loadXML(xhr.responseText);\r
+                                               result = dom;\r
+                                       }catch(e){ /* Not available. Squelch and try next one. */ }\r
+                               });\r
+                       }\r
+                       return result; // DOMDocument\r
+               }\r
+       };\r
+\r
+       dojo._contentHandlers["json-comment-optional"] = function(xhr){\r
+               var handlers = _d._contentHandlers;\r
+               try{\r
+                       return handlers["json-comment-filtered"](xhr);\r
+               }catch(e){\r
+                       return handlers["json"](xhr);\r
+               }\r
+       };\r
+\r
+       /*=====\r
+       dojo.__ioArgs = function(kwArgs){\r
+               //      url: String\r
+               //              URL to server endpoint.\r
+               //      content: Object?\r
+               //              Contains properties with string values. These\r
+               //              properties will be serialized as name1=value2 and\r
+               //              passed in the request.\r
+               //      timeout: Integer?\r
+               //              Milliseconds to wait for the response. If this time\r
+               //              passes, the then error callbacks are called.\r
+               //      form: DOMNode?\r
+               //              DOM node for a form. Used to extract the form values\r
+               //              and send to the server.\r
+               //      preventCache: Boolean?\r
+               //              Default is false. If true, then a\r
+               //              "dojo.preventCache" parameter is sent in the request\r
+               //              with a value that changes with each request\r
+               //              (timestamp). Useful only with GET-type requests.\r
+               //      handleAs: String?\r
+               //              Acceptable values depend on the type of IO\r
+               //              transport (see specific IO calls for more information).\r
+               //      load: Function?\r
+               //              function(response, ioArgs){}. response is an Object, ioArgs\r
+               //              is of type dojo.__ioCallbackArgs. The load function will be\r
+               //              called on a successful response.\r
+               //      error: Function?\r
+               //              function(response, ioArgs){}. response is an Object, ioArgs\r
+               //              is of type dojo.__ioCallbackArgs. The error function will\r
+               //              be called in an error case. \r
+               //      handle: Function\r
+               //              function(response, ioArgs){}. response is an Object, ioArgs\r
+               //              is of type dojo.__ioCallbackArgs. The handle function will\r
+               //              be called in either the successful or error case.  For\r
+               //              the load, error and handle functions, the ioArgs object\r
+               //              will contain the following properties: \r
+       }\r
+       =====*/\r
+\r
+       /*=====\r
+       dojo.__ioCallbackArgs = function(kwArgs){\r
+               //      args: Object\r
+               //              the original object argument to the IO call.\r
+               //      xhr: XMLHttpRequest\r
+               //              For XMLHttpRequest calls only, the\r
+               //              XMLHttpRequest object that was used for the\r
+               //              request.\r
+               //      url: String\r
+               //              The final URL used for the call. Many times it\r
+               //              will be different than the original args.url\r
+               //              value.\r
+               //      query: String\r
+               //              For non-GET requests, the\r
+               //              name1=value1&name2=value2 parameters sent up in\r
+               //              the request.\r
+               //      handleAs: String\r
+               //              The final indicator on how the response will be\r
+               //              handled.\r
+               //      id: String\r
+               //              For dojo.io.script calls only, the internal\r
+               //              script ID used for the request.\r
+               //      canDelete: Boolean\r
+               //              For dojo.io.script calls only, indicates\r
+               //              whether the script tag that represents the\r
+               //              request can be deleted after callbacks have\r
+               //              been called. Used internally to know when\r
+               //              cleanup can happen on JSONP-type requests.\r
+               //      json: Object\r
+               //              For dojo.io.script calls only: holds the JSON\r
+               //              response for JSONP-type requests. Used\r
+               //              internally to hold on to the JSON responses.\r
+               //              You should not need to access it directly --\r
+               //              the same object should be passed to the success\r
+               //              callbacks directly.\r
+       }\r
+       =====*/\r
+\r
+\r
+\r
+       dojo._ioSetArgs = function(/*dojo.__ioArgs*/args,\r
+                       /*Function*/canceller,\r
+                       /*Function*/okHandler,\r
+                       /*Function*/errHandler){\r
+               //      summary: \r
+               //              sets up the Deferred and ioArgs property on the Deferred so it\r
+               //              can be used in an io call.\r
+               //      args:\r
+               //              The args object passed into the public io call. Recognized properties on\r
+               //              the args object are:\r
+               //      canceller:\r
+               //              The canceller function used for the Deferred object. The function\r
+               //              will receive one argument, the Deferred object that is related to the\r
+               //              canceller.\r
+               //      okHandler:\r
+               //              The first OK callback to be registered with Deferred. It has the opportunity\r
+               //              to transform the OK response. It will receive one argument -- the Deferred\r
+               //              object returned from this function.\r
+               //      errHandler:\r
+               //              The first error callback to be registered with Deferred. It has the opportunity\r
+               //              to do cleanup on an error. It will receive two arguments: error (the \r
+               //              Error object) and dfd, the Deferred object returned from this function.\r
+\r
+               var ioArgs = {args: args, url: args.url};\r
+\r
+               //Get values from form if requestd.\r
+               var formObject = null;\r
+               if(args.form){ \r
+                       var form = _d.byId(args.form);\r
+                       //IE requires going through getAttributeNode instead of just getAttribute in some form cases, \r
+                       //so use it for all.  See #2844\r
+                       var actnNode = form.getAttributeNode("action");\r
+                       ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null); \r
+                       formObject = _d.formToObject(form);\r
+               }\r
+\r
+               // set up the query params\r
+               var miArgs = [{}];\r
+       \r
+               if(formObject){\r
+                       // potentially over-ride url-provided params w/ form values\r
+                       miArgs.push(formObject);\r
+               }\r
+               if(args.content){\r
+                       // stuff in content over-rides what's set by form\r
+                       miArgs.push(args.content);\r
+               }\r
+               if(args.preventCache){\r
+                       miArgs.push({"dojo.preventCache": new Date().valueOf()});\r
+               }\r
+               ioArgs.query = _d.objectToQuery(_d.mixin.apply(null, miArgs));\r
+       \r
+               // .. and the real work of getting the deferred in order, etc.\r
+               ioArgs.handleAs = args.handleAs || "text";\r
+               var d = new _d.Deferred(canceller);\r
+               d.addCallbacks(okHandler, function(error){\r
+                       return errHandler(error, d);\r
+               });\r
+\r
+               //Support specifying load, error and handle callback functions from the args.\r
+               //For those callbacks, the "this" object will be the args object.\r
+               //The callbacks will get the deferred result value as the\r
+               //first argument and the ioArgs object as the second argument.\r
+               var ld = args.load;\r
+               if(ld && _d.isFunction(ld)){\r
+                       d.addCallback(function(value){\r
+                               return ld.call(args, value, ioArgs);\r
+                       });\r
+               }\r
+               var err = args.error;\r
+               if(err && _d.isFunction(err)){\r
+                       d.addErrback(function(value){\r
+                               return err.call(args, value, ioArgs);\r
+                       });\r
+               }\r
+               var handle = args.handle;\r
+               if(handle && _d.isFunction(handle)){\r
+                       d.addBoth(function(value){\r
+                               return handle.call(args, value, ioArgs);\r
+                       });\r
+               }\r
+               \r
+               d.ioArgs = ioArgs;\r
+       \r
+               // FIXME: need to wire up the xhr object's abort method to something\r
+               // analagous in the Deferred\r
+               return d;\r
+       }\r
+\r
+       var _deferredCancel = function(/*Deferred*/dfd){\r
+               //summary: canceller function for dojo._ioSetArgs call.\r
+               \r
+               dfd.canceled = true;\r
+               var xhr = dfd.ioArgs.xhr;\r
+               var _at = typeof xhr.abort;\r
+               if(_at == "function" || _at == "unknown"){\r
+                       xhr.abort();\r
+               }\r
+               var err = new Error("xhr cancelled");\r
+               err.dojoType = "cancel";\r
+               return err;\r
+       }\r
+       var _deferredOk = function(/*Deferred*/dfd){\r
+               //summary: okHandler function for dojo._ioSetArgs call.\r
+\r
+               return _d._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);\r
+       }\r
+       var _deferError = function(/*Error*/error, /*Deferred*/dfd){\r
+               //summary: errHandler function for dojo._ioSetArgs call.\r
+               \r
+               // console.debug("xhr error in:", dfd.ioArgs.xhr);\r
+               console.debug(error);\r
+               return error;\r
+       }\r
+\r
+       var _makeXhrDeferred = function(/*dojo.__xhrArgs*/args){\r
+               //summary: makes the Deferred object for this xhr request.\r
+               var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError);\r
+               //Pass the args to _xhrObj, to allow xhr iframe proxy interceptions.\r
+               dfd.ioArgs.xhr = _d._xhrObj(dfd.ioArgs.args);\r
+               return dfd;\r
+       }\r
+\r
+       // avoid setting a timer per request. It degrades performance on IE\r
+       // something fierece if we don't use unified loops.\r
+       var _inFlightIntvl = null;\r
+       var _inFlight = [];\r
+       var _watchInFlight = function(){\r
+               //summary: \r
+               //              internal method that checks each inflight XMLHttpRequest to see\r
+               //              if it has completed or if the timeout situation applies.\r
+               \r
+               var now = (new Date()).getTime();\r
+               // make sure sync calls stay thread safe, if this callback is called\r
+               // during a sync call and this results in another sync call before the\r
+               // first sync call ends the browser hangs\r
+               if(!_d._blockAsync){\r
+                       // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating\r
+                       // note: the second clause is an assigment on purpose, lint may complain\r
+                       for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){\r
+                               var dfd = tif.dfd;\r
+                               try{\r
+                                       if(!dfd || dfd.canceled || !tif.validCheck(dfd)){\r
+                                               _inFlight.splice(i--, 1); \r
+                                       }else if(tif.ioCheck(dfd)){\r
+                                               _inFlight.splice(i--, 1);\r
+                                               tif.resHandle(dfd);\r
+                                       }else if(dfd.startTime){\r
+                                               //did we timeout?\r
+                                               if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){\r
+                                                       _inFlight.splice(i--, 1);\r
+                                                       var err = new Error("timeout exceeded");\r
+                                                       err.dojoType = "timeout";\r
+                                                       dfd.errback(err);\r
+                                                       //Cancel the request so the io module can do appropriate cleanup.\r
+                                                       dfd.cancel();\r
+                                               }\r
+                                       }\r
+                               }catch(e){\r
+                                       // FIXME: make sure we errback! (fixed?  remove console.debug?)\r
+                                       console.debug(e);\r
+                                       dfd.errback(new Error("_watchInFlightError!"));\r
+                               }\r
+                       }\r
+               }\r
+\r
+               if(!_inFlight.length){\r
+                       clearInterval(_inFlightIntvl);\r
+                       _inFlightIntvl = null;\r
+                       return;\r
+               }\r
+\r
+       }\r
+\r
+       dojo._ioCancelAll = function(){\r
+               //summary: Cancels all pending IO requests, regardless of IO type\r
+               //(xhr, script, iframe).\r
+               try{\r
+                       _d.forEach(_inFlight, function(i){\r
+                               i.dfd.cancel();\r
+                       });\r
+               }catch(e){/*squelch*/}\r
+       }\r
+\r
+       //Automatically call cancel all io calls on unload\r
+       //in IE for trac issue #2357.\r
+       if(_d.isIE){\r
+               _d.addOnUnload(_d._ioCancelAll);\r
+       }\r
+\r
+       _d._ioWatch = function(/*Deferred*/dfd,\r
+               /*Function*/validCheck,\r
+               /*Function*/ioCheck,\r
+               /*Function*/resHandle){\r
+               //summary: watches the io request represented by dfd to see if it completes.\r
+               //dfd:\r
+               //              The Deferred object to watch.\r
+               //validCheck:\r
+               //              Function used to check if the IO request is still valid. Gets the dfd\r
+               //              object as its only argument.\r
+               //ioCheck:\r
+               //              Function used to check if basic IO call worked. Gets the dfd\r
+               //              object as its only argument.\r
+               //resHandle:\r
+               //              Function used to process response. Gets the dfd\r
+               //              object as its only argument.\r
+               if(dfd.ioArgs.args.timeout){\r
+                       dfd.startTime = (new Date()).getTime();\r
+               }\r
+               _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle});\r
+               if(!_inFlightIntvl){\r
+                       _inFlightIntvl = setInterval(_watchInFlight, 50);\r
+               }\r
+               _watchInFlight(); // handle sync requests\r
+       }\r
+\r
+       var _defaultContentType = "application/x-www-form-urlencoded";\r
+\r
+       var _validCheck = function(/*Deferred*/dfd){\r
+               return dfd.ioArgs.xhr.readyState; //boolean\r
+       }\r
+       var _ioCheck = function(/*Deferred*/dfd){\r
+               return 4 == dfd.ioArgs.xhr.readyState; //boolean\r
+       }\r
+       var _resHandle = function(/*Deferred*/dfd){\r
+               var xhr = dfd.ioArgs.xhr;\r
+               if(_d._isDocumentOk(xhr)){\r
+                       dfd.callback(dfd);\r
+               }else{\r
+                       var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status);\r
+                       err.status = xhr.status;\r
+                       err.responseText = xhr.responseText;\r
+                       dfd.errback(err);\r
+               }\r
+       }\r
+\r
+       var _doIt = function(/*String*/type, /*Deferred*/dfd){\r
+               // IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open).\r
+               // workaround for IE6's apply() "issues"\r
+               var ioArgs = dfd.ioArgs;\r
+               var args = ioArgs.args;\r
+               ioArgs.xhr.open(type, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined);\r
+               if(args.headers){\r
+                       for(var hdr in args.headers){\r
+                               if(hdr.toLowerCase() === "content-type" && !args.contentType){\r
+                                       args.contentType = args.headers[hdr];\r
+                               }else{\r
+                                       ioArgs.xhr.setRequestHeader(hdr, args.headers[hdr]);\r
+                               }\r
+                       }\r
+               }\r
+               // FIXME: is this appropriate for all content types?\r
+               ioArgs.xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType);\r
+               // FIXME: set other headers here!\r
+               try{\r
+                       ioArgs.xhr.send(ioArgs.query);\r
+               }catch(e){\r
+                       dfd.cancel();\r
+               }\r
+               _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle);\r
+               return dfd; //Deferred\r
+       }\r
+\r
+       dojo._ioAddQueryToUrl = function(/*dojo.__ioCallbackArgs*/ioArgs){\r
+               //summary: Adds query params discovered by the io deferred construction to the URL.\r
+               //Only use this for operations which are fundamentally GET-type operations.\r
+               if(ioArgs.query.length){\r
+                       ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query;\r
+                       ioArgs.query = null;\r
+               }               \r
+       }\r
+\r
+       /*=====\r
+       dojo.__xhrArgs = function(kwArgs){\r
+               //      summary:\r
+               //              In addition to the properties listed for the dojo.__ioArgs type,\r
+               //              the following properties are allowed for dojo.xhr* methods.\r
+               //      handleAs: \r
+               //              String. Acceptable values are:\r
+               //                      "text" (default)\r
+               //                      "json"\r
+               //                      "json-comment-optional"\r
+               //                      "json-comment-filtered"\r
+               //                      "javascript"\r
+               //                      "xml"\r
+               //      sync:\r
+               //              Boolean. false is default. Indicates whether the request should\r
+               //              be a synchronous (blocking) request.\r
+               //      headers:\r
+               //              Object. Additional HTTP headers to send in the request.\r
+       }\r
+       =====*/\r
+\r
+       dojo.xhrGet = function(/*dojo.__xhrArgs*/ args){\r
+               //      summary: \r
+               //              Sends an HTTP GET request to the server.\r
+               var dfd = _makeXhrDeferred(args);\r
+               _d._ioAddQueryToUrl(dfd.ioArgs);\r
+               return _doIt("GET", dfd); // dojo.Deferred\r
+       }\r
+\r
+       dojo.xhrPost = function(/*dojo.__xhrArgs*/ args){\r
+               //summary: \r
+               //              Sends an HTTP POST request to the server.\r
+               return _doIt("POST", _makeXhrDeferred(args)); // dojo.Deferred\r
+       }\r
+\r
+       dojo.rawXhrPost = function(/*dojo.__xhrArgs*/ args){\r
+               //      summary:\r
+               //              Sends an HTTP POST request to the server. In addtion to the properties\r
+               //              listed for the dojo.__xhrArgs type, the following property is allowed:\r
+               //      postData:\r
+               //              String. The raw data to send in the body of the POST request.\r
+               var dfd = _makeXhrDeferred(args);\r
+               dfd.ioArgs.query = args.postData;\r
+               return _doIt("POST", dfd); // dojo.Deferred\r
+       }\r
+\r
+       dojo.xhrPut = function(/*dojo.__xhrArgs*/ args){\r
+               //      summary:\r
+               //              Sends an HTTP PUT request to the server.\r
+               return _doIt("PUT", _makeXhrDeferred(args)); // dojo.Deferred\r
+       }\r
+\r
+       dojo.rawXhrPut = function(/*dojo.__xhrArgs*/ args){\r
+               //      summary:\r
+               //              Sends an HTTP PUT request to the server. In addtion to the properties\r
+               //              listed for the dojo.__xhrArgs type, the following property is allowed:\r
+               //      putData:\r
+               //              String. The raw data to send in the body of the PUT request.\r
+               var dfd = _makeXhrDeferred(args);\r
+               var ioArgs = dfd.ioArgs;\r
+               if(args.putData){\r
+                       ioArgs.query = args.putData;\r
+                       args.putData = null;\r
+               }\r
+               return _doIt("PUT", dfd); // dojo.Deferred\r
+       }\r
+\r
+       dojo.xhrDelete = function(/*dojo.__xhrArgs*/ args){\r
+               //      summary:\r
+               //              Sends an HTTP DELETE request to the server.\r
+               var dfd = _makeXhrDeferred(args);\r
+               _d._ioAddQueryToUrl(dfd.ioArgs);\r
+               return _doIt("DELETE", dfd); // dojo.Deferred\r
+       }\r
+\r
+       /*\r
+       dojo.wrapForm = function(formNode){\r
+               //summary:\r
+               //              A replacement for FormBind, but not implemented yet.\r
+\r
+               // FIXME: need to think harder about what extensions to this we might\r
+               // want. What should we allow folks to do w/ this? What events to\r
+               // set/send?\r
+               throw new Error("dojo.wrapForm not yet implemented");\r
+       }\r
+       */\r
+})();\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo._base.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo._base.fx"] = true;\r
+dojo.provide("dojo._base.fx");\r
+\r
+\r
+\r
+\r
+\r
+\r
+/*\r
+       Animation losely package based on Dan Pupius' work, contributed under CLA: \r
+               http://pupius.co.uk/js/Toolkit.Drawing.js\r
+*/\r
+\r
+dojo._Line = function(/*int*/ start, /*int*/ end){\r
+       //      summary:\r
+       //              dojo._Line is the object used to generate values from a start value\r
+       //              to an end value\r
+       //      start: int\r
+       //              Beginning value for range\r
+       //      end: int\r
+       //              Ending value for range\r
+       this.start = start;\r
+       this.end = end;\r
+       this.getValue = function(/*float*/ n){\r
+               //      summary: returns the point on the line\r
+               //      n: a floating point number greater than 0 and less than 1\r
+               return ((this.end - this.start) * n) + this.start; // Decimal\r
+       }\r
+}\r
+\r
+dojo.declare("dojo._Animation", null, {\r
+       //      summary\r
+       //              A generic animation object that fires callbacks into it's handlers\r
+       //              object at various states\r
+       //\r
+       constructor: function(/*Object*/ args){\r
+               dojo.mixin(this, args);\r
+               if(dojo.isArray(this.curve)){\r
+                       /* curve: Array\r
+                               pId: a */\r
+                       this.curve = new dojo._Line(this.curve[0], this.curve[1]);\r
+               }\r
+       },\r
+       \r
+       // duration: Integer\r
+       //      The time in milliseonds the animation will take to run\r
+       duration: 1000,\r
+\r
+/*=====\r
+       // curve: dojo._Line||Array\r
+       //      A two element array of start and end values, or a dojo._Line instance to be\r
+       //      used in the Animation. \r
+       curve: null,\r
+\r
+       // easing: Function\r
+       //      A Function to adjust the acceleration (or deceleration) of the progress \r
+       //      across a dojo._Line\r
+       easing: null,\r
+=====*/\r
+\r
+       // repeat: Integer\r
+       //      The number of times to loop the animation\r
+       repeat: 0,\r
+\r
+       // rate: Integer\r
+       //      the time in milliseconds to wait before advancing to next frame \r
+       //      (used as a fps timer: rate/1000 = fps)\r
+       rate: 10 /* 100 fps */,\r
+\r
+/*===== \r
+       // delay: Integer\r
+       //      The time in milliseconds to wait before starting animation after it has been .play()'ed\r
+       delay: null,\r
+\r
+       // events\r
+       //\r
+       // beforeBegin: Event\r
+       //      Synthetic event fired before a dojo._Animation begins playing (synhcronous)\r
+       beforeBegin: null,\r
+\r
+       // onBegin: Event\r
+       //      Synthetic event fired as a dojo._Animation begins playing (useful?)\r
+       onBegin: null,\r
+\r
+       // onAnimate: Event\r
+       //      Synthetic event fired at each interval of a dojo._Animation\r
+       onAnimate: null,\r
+\r
+       // onEnd: Event\r
+       //      Synthetic event fired after the final frame of a dojo._Animation\r
+       onEnd: null,\r
+\r
+       // ???\r
+       onPlay: null,\r
+\r
+       // onPause: Event\r
+       //      Synthetic event fired when a dojo._Animation is paused\r
+       onPause: null,\r
+\r
+       // onStop: Event\r
+       //      Synthetic event fires when a dojo._Animation is stopped\r
+       onStop: null,\r
+\r
+=====*/\r
+\r
+       _percent: 0,\r
+       _startRepeatCount: 0,\r
+\r
+       fire: function(/*Event*/ evt, /*Array?*/ args){\r
+               //      summary:\r
+               //              Convenience function.  Fire event "evt" and pass it the\r
+               //              arguments specified in "args".\r
+               //      evt:\r
+               //              The event to fire.\r
+               //      args:\r
+               //              The arguments to pass to the event.\r
+               if(this[evt]){\r
+                       this[evt].apply(this, args||[]);\r
+               }\r
+               return this; // dojo._Animation\r
+       },\r
+\r
+       play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){\r
+               // summary:\r
+               //              Start the animation.\r
+               // delay:\r
+               //              How many milliseconds to delay before starting.\r
+               // gotoStart:\r
+               //              If true, starts the animation from the beginning; otherwise,\r
+               //              starts it from its current position.\r
+               var _t = this;\r
+               if(gotoStart){\r
+                       _t._stopTimer();\r
+                       _t._active = _t._paused = false;\r
+                       _t._percent = 0;\r
+               }else if(_t._active && !_t._paused){\r
+                       return _t; // dojo._Animation\r
+               }\r
+\r
+               _t.fire("beforeBegin");\r
+\r
+               var d = delay||_t.delay;\r
+               var _p = dojo.hitch(_t, "_play", gotoStart);\r
+               if(d > 0){\r
+                       setTimeout(_p, d);\r
+                       return _t; // dojo._Animation\r
+               }\r
+               _p();\r
+               return _t;\r
+       },\r
+\r
+       _play: function(gotoStart){\r
+               var _t = this;\r
+               _t._startTime = new Date().valueOf();\r
+               if(_t._paused){\r
+                       _t._startTime -= _t.duration * _t._percent;\r
+               }\r
+               _t._endTime = _t._startTime + _t.duration;\r
+\r
+               _t._active = true;\r
+               _t._paused = false;\r
+\r
+               var value = _t.curve.getValue(_t._percent);\r
+               if(!_t._percent){\r
+                       if(!_t._startRepeatCount){\r
+                               _t._startRepeatCount = _t.repeat;\r
+                       }\r
+                       _t.fire("onBegin", [value]);\r
+               }\r
+\r
+               _t.fire("onPlay", [value]);\r
+\r
+               _t._cycle();\r
+               return _t; // dojo._Animation\r
+       },\r
+\r
+       pause: function(){\r
+               // summary: Pauses a running animation.\r
+               this._stopTimer();\r
+               if(!this._active){ return this; /*dojo._Animation*/}\r
+               this._paused = true;\r
+               this.fire("onPause", [this.curve.getValue(this._percent)]);\r
+               return this; // dojo._Animation\r
+       },\r
+\r
+       gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){\r
+               //      summary:\r
+               //              Sets the progress of the animation.\r
+               //      percent:\r
+               //              A percentage in decimal notation (between and including 0.0 and 1.0).\r
+               //      andPlay:\r
+               //              If true, play the animation after setting the progress.\r
+               this._stopTimer();\r
+               this._active = this._paused = true;\r
+               this._percent = percent;\r
+               if(andPlay){ this.play(); }\r
+               return this; // dojo._Animation\r
+       },\r
+\r
+       stop: function(/*boolean?*/ gotoEnd){\r
+               // summary: Stops a running animation.\r
+               // gotoEnd: If true, the animation will end.\r
+               if(!this._timer){ return; }\r
+               this._stopTimer();\r
+               if(gotoEnd){\r
+                       this._percent = 1;\r
+               }\r
+               this.fire("onStop", [this.curve.getValue(this._percent)]);\r
+               this._active = this._paused = false;\r
+               return this; // dojo._Animation\r
+       },\r
+\r
+       status: function(){\r
+               // summary: Returns a string token representation of the status of\r
+               //                      the animation, one of: "paused", "playing", "stopped"\r
+               if(this._active){\r
+                       return this._paused ? "paused" : "playing"; // String\r
+               }\r
+               return "stopped"; // String\r
+       },\r
+\r
+       _cycle: function(){\r
+               var _t = this;\r
+               if(_t._active){\r
+                       var curr = new Date().valueOf();\r
+                       var step = (curr - _t._startTime) / (_t._endTime - _t._startTime);\r
+\r
+                       if(step >= 1){\r
+                               step = 1;\r
+                       }\r
+                       _t._percent = step;\r
+\r
+                       // Perform easing\r
+                       if(_t.easing){\r
+                               step = _t.easing(step);\r
+                       }\r
+\r
+                       _t.fire("onAnimate", [_t.curve.getValue(step)]);\r
+\r
+                       if(_t._percent < 1){\r
+                               _t._startTimer();\r
+                       }else{\r
+                               _t._active = false;\r
+\r
+                               if(_t.repeat > 0){\r
+                                       _t.repeat--;\r
+                                       _t.play(null, true);\r
+                               }else if(_t.repeat == -1){\r
+                                       _t.play(null, true);\r
+                               }else{\r
+                                       if(_t._startRepeatCount){\r
+                                               _t.repeat = _t._startRepeatCount;\r
+                                               _t._startRepeatCount = 0;\r
+                                       }\r
+                               }\r
+                               _t._percent = 0;\r
+                               _t.fire("onEnd");\r
+                       }\r
+               }\r
+               return _t; // dojo._Animation\r
+       }\r
+});\r
+\r
+(function(){\r
+       var d = dojo;\r
+       var ctr = 0;\r
+       var _globalTimerList = [];\r
+       var runner = {\r
+               run: function(){}\r
+       };\r
+       var timer = null;\r
+       dojo._Animation.prototype._startTimer = function(){\r
+               // this._timer = setTimeout(dojo.hitch(this, "_cycle"), this.rate);\r
+               if(!this._timer){\r
+                       this._timer = dojo.connect(runner, "run", this, "_cycle");\r
+                       ctr++;\r
+               }\r
+               if(!timer){\r
+                       timer = setInterval(dojo.hitch(runner, "run"), this.rate);\r
+               }\r
+       };\r
+\r
+       dojo._Animation.prototype._stopTimer = function(){\r
+               dojo.disconnect(this._timer);\r
+               this._timer = null;\r
+               ctr--;\r
+               if(!ctr){\r
+                       clearInterval(timer);\r
+                       timer = null;\r
+               }\r
+       };\r
+\r
+       var _makeFadeable = (d.isIE) ? function(node){\r
+               // only set the zoom if the "tickle" value would be the same as the\r
+               // default\r
+               var ns = node.style;\r
+               if(!ns.zoom.length && d.style(node, "zoom") == "normal"){\r
+                       // make sure the node "hasLayout"\r
+                       // NOTE: this has been tested with larger and smaller user-set text\r
+                       // sizes and works fine\r
+                       ns.zoom = "1";\r
+                       // node.style.zoom = "normal";\r
+               }\r
+               // don't set the width to auto if it didn't already cascade that way.\r
+               // We don't want to f anyones designs\r
+               if(!ns.width.length && d.style(node, "width") == "auto"){\r
+                       ns.width = "auto";\r
+               }\r
+       } : function(){};\r
+\r
+       dojo._fade = function(/*Object*/ args){\r
+               //      summary: \r
+               //              Returns an animation that will fade the node defined by\r
+               //              args.node from the start to end values passed (args.start\r
+               //              args.end) (end is mandatory, start is optional)\r
+\r
+               args.node = d.byId(args.node);\r
+               var fArgs = d.mixin({ properties: {} }, args);\r
+               var props = (fArgs.properties.opacity = {});\r
+               props.start = !("start" in fArgs) ?\r
+                       function(){ return Number(d.style(fArgs.node, "opacity")); } : fArgs.start;\r
+               props.end = fArgs.end;\r
+\r
+               var anim = d.animateProperty(fArgs);\r
+               d.connect(anim, "beforeBegin", d.partial(_makeFadeable, fArgs.node));\r
+\r
+               return anim; // dojo._Animation\r
+       }\r
+\r
+       /*=====\r
+       dojo.__fadeArgs = function(kwArgs){\r
+               //      duration: Integer?\r
+               //              Duration of the animation in milliseconds.\r
+               // easing: Function?\r
+               //              An easing function.\r
+       }\r
+       =====*/\r
+\r
+       dojo.fadeIn = function(/*dojo.__fadeArgs*/ args){\r
+               // summary: \r
+               //              Returns an animation that will fade node defined in 'args' from\r
+               //              its current opacity to fully opaque.\r
+               return d._fade(d.mixin({ end: 1 }, args)); // dojo._Animation\r
+       }\r
+\r
+       dojo.fadeOut = function(/*dojo.__fadeArgs*/  args){\r
+               // summary: \r
+               //              Returns an animation that will fade node defined in 'args'\r
+               //              from its current opacity to fully transparent.\r
+               return d._fade(d.mixin({ end: 0 }, args)); // dojo._Animation\r
+       }\r
+\r
+       dojo._defaultEasing = function(/*Decimal?*/ n){\r
+               // summary: The default easing function for dojo._Animation(s)\r
+               return 0.5 + ((Math.sin((n + 1.5) * Math.PI))/2);\r
+       }\r
+\r
+       var PropLine = function(properties){\r
+               this._properties = properties;\r
+               for(var p in properties){\r
+                       var prop = properties[p];\r
+                       if(prop.start instanceof d.Color){\r
+                               // create a reusable temp color object to keep intermediate results\r
+                               prop.tempColor = new d.Color();\r
+                       }\r
+               }\r
+               this.getValue = function(r){\r
+                       var ret = {};\r
+                       for(var p in this._properties){\r
+                               var prop = this._properties[p];\r
+                               var start = prop.start;\r
+                               if(start instanceof d.Color){\r
+                                       ret[p] = d.blendColors(start, prop.end, r, prop.tempColor).toCss();\r
+                               }else if(!d.isArray(start)){\r
+                                       ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units||"px" : "");\r
+                               }\r
+                       }\r
+                       return ret;\r
+               }\r
+       }\r
+\r
+       dojo.animateProperty = function(/*Object*/ args){\r
+               //      summary: \r
+               //              Returns an animation that will transition the properties of\r
+               //              node defined in 'args' depending how they are defined in\r
+               //              'args.properties'\r
+               //\r
+               // description:\r
+               //              The foundation of most dojo.fx animations, dojo.AnimateProperty\r
+               //              will take an object of "properties" corresponding to style\r
+               //              properties, and animate them in parallel over a set duration.\r
+               //      \r
+               //              args.node can be a String or a DomNode reference\r
+               //      \r
+               //      example:\r
+               //      |       dojo.animateProperty({ node: node, duration:2000,\r
+               //      |               properties: {\r
+               //      |                       width: { start: '200', end: '400', unit:"px" },\r
+               //      |                       height: { start:'200', end: '400', unit:"px" },\r
+               //      |                       paddingTop: { start:'5', end:'50', unit:"px" } \r
+               //      |               }\r
+               //      |       }).play();\r
+               //\r
+\r
+               args.node = d.byId(args.node);\r
+               if(!args.easing){ args.easing = d._defaultEasing; }\r
+\r
+               var anim = new d._Animation(args);\r
+               d.connect(anim, "beforeBegin", anim, function(){\r
+                       var pm = {};\r
+                       for(var p in this.properties){\r
+                               // Make shallow copy of properties into pm because we overwrite some values below.\r
+                               // In particular if start/end are functions we don't want to overwrite them or\r
+                               // the functions won't be called if the animation is reused.\r
+                               var prop = (pm[p] = d.mixin({}, this.properties[p]));\r
+\r
+                               if(d.isFunction(prop.start)){\r
+                                       prop.start = prop.start();\r
+                               }\r
+                               if(d.isFunction(prop.end)){\r
+                                       prop.end = prop.end();\r
+                               }\r
+\r
+                               var isColor = (p.toLowerCase().indexOf("color") >= 0);\r
+                               function getStyle(node, p){\r
+                                       // dojo.style(node, "height") can return "auto" or "" on IE; this is more reliable:\r
+                                       var v = ({height: node.offsetHeight, width: node.offsetWidth})[p];\r
+                                       if(v !== undefined){ return v; }\r
+                                       v = d.style(node, p);\r
+                                       return (p=="opacity") ? Number(v) : parseFloat(v);\r
+                               }\r
+                               if(!("end" in prop)){\r
+                                       prop.end = getStyle(this.node, p);\r
+                               }else if(!("start" in prop)){\r
+                                       prop.start = getStyle(this.node, p);\r
+                               }\r
+\r
+                               if(isColor){\r
+                                       // console.debug("it's a color!");\r
+                                       prop.start = new d.Color(prop.start);\r
+                                       prop.end = new d.Color(prop.end);\r
+                               }else{\r
+                                       prop.start = (p == "opacity") ? Number(prop.start) : parseFloat(prop.start);\r
+                               }\r
+                               // console.debug("start:", prop.start);\r
+                               // console.debug("end:", prop.end);\r
+                       }\r
+                       this.curve = new PropLine(pm);\r
+               });\r
+               d.connect(anim, "onAnimate", anim, function(propValues){\r
+                       // try{\r
+                       for(var s in propValues){\r
+                               // console.debug(s, propValues[s], this.node.style[s]);\r
+                               d.style(this.node, s, propValues[s]);\r
+                               // this.node.style[s] = propValues[s];\r
+                       }\r
+                       // }catch(e){ console.debug(dojo.toJson(e)); }\r
+               });\r
+               return anim; // dojo._Animation\r
+       }\r
+})();\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo.i18n"] = true;\r
+dojo.provide("dojo.i18n");\r
+\r
+/*=====\r
+dojo.i18n = {\r
+       // summary: Utility classes to enable loading of resources for internationalization (i18n)\r
+};\r
+=====*/\r
+\r
+dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){\r
+       //      summary:\r
+       //              Returns an Object containing the localization for a given resource\r
+       //              bundle in a package, matching the specified locale.\r
+       //      description:\r
+       //              Returns a hash containing name/value pairs in its prototypesuch\r
+       //              that values can be easily overridden.  Throws an exception if the\r
+       //              bundle is not found.  Bundle must have already been loaded by\r
+       //              dojo.requireLocalization() or by a build optimization step.  NOTE:\r
+       //              try not to call this method as part of an object property\r
+       //              definition (var foo = { bar: dojo.i18n.getLocalization() }).  In\r
+       //              some loading situations, the bundle may not be available in time\r
+       //              for the object definition.  Instead, call this method inside a\r
+       //              function that is run after all modules load or the page loads (like\r
+       //              in dojo.adOnLoad()), or in a widget lifecycle method.\r
+       //      packageName:\r
+       //              package which is associated with this resource\r
+       //      bundleName:\r
+       //              the base filename of the resource bundle (without the ".js" suffix)\r
+       //      locale:\r
+       //              the variant to load (optional).  By default, the locale defined by\r
+       //              the host environment: dojo.locale\r
+\r
+       locale = dojo.i18n.normalizeLocale(locale);\r
+\r
+       // look for nearest locale match\r
+       var elements = locale.split('-');\r
+       var module = [packageName,"nls",bundleName].join('.');\r
+       var bundle = dojo._loadedModules[module];\r
+       if(bundle){\r
+               var localization;\r
+               for(var i = elements.length; i > 0; i--){\r
+                       var loc = elements.slice(0, i).join('_');\r
+                       if(bundle[loc]){\r
+                               localization = bundle[loc];\r
+                               break;\r
+                       }\r
+               }\r
+               if(!localization){\r
+                       localization = bundle.ROOT;\r
+               }\r
+\r
+               // make a singleton prototype so that the caller won't accidentally change the values globally\r
+               if(localization){\r
+                       var clazz = function(){};\r
+                       clazz.prototype = localization;\r
+                       return new clazz(); // Object\r
+               }\r
+       }\r
+\r
+       throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);\r
+};\r
+\r
+dojo.i18n.normalizeLocale = function(/*String?*/locale){\r
+       //      summary:\r
+       //              Returns canonical form of locale, as used by Dojo.\r
+       //\r
+       //  description:\r
+       //              All variants are case-insensitive and are separated by '-' as specified in RFC 3066.\r
+       //              If no locale is specified, the dojo.locale is returned.  dojo.locale is defined by\r
+       //              the user agent's locale unless overridden by djConfig.\r
+\r
+       var result = locale ? locale.toLowerCase() : dojo.locale;\r
+       if(result == "root"){\r
+               result = "ROOT";\r
+       }\r
+       return result; // String\r
+};\r
+\r
+dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){\r
+       //      summary:\r
+       //              See dojo.requireLocalization()\r
+       //      description:\r
+       //              Called by the bootstrap, but factored out so that it is only\r
+       //              included in the build when needed.\r
+\r
+       var targetLocale = dojo.i18n.normalizeLocale(locale);\r
+       var bundlePackage = [moduleName, "nls", bundleName].join(".");\r
+       // NOTE: \r
+       //              When loading these resources, the packaging does not match what is\r
+       //              on disk.  This is an implementation detail, as this is just a\r
+       //              private data structure to hold the loaded resources.  e.g.\r
+       //              tests/hello/nls/en-us/salutations.js is loaded as the object\r
+       //              tests.hello.nls.salutations.en_us={...} The structure on disk is\r
+       //              intended to be most convenient for developers and translators, but\r
+       //              in memory it is more logical and efficient to store in a different\r
+       //              order.  Locales cannot use dashes, since the resulting path will\r
+       //              not evaluate as valid JS, so we translate them to underscores.\r
+       \r
+       //Find the best-match locale to load if we have available flat locales.\r
+       var bestLocale = "";\r
+       if(availableFlatLocales){\r
+               var flatLocales = availableFlatLocales.split(",");\r
+               for(var i = 0; i < flatLocales.length; i++){\r
+                       //Locale must match from start of string.\r
+                       if(targetLocale.indexOf(flatLocales[i]) == 0){\r
+                               if(flatLocales[i].length > bestLocale.length){\r
+                                       bestLocale = flatLocales[i];\r
+                               }\r
+                       }\r
+               }\r
+               if(!bestLocale){\r
+                       bestLocale = "ROOT";\r
+               }               \r
+       }\r
+\r
+       //See if the desired locale is already loaded.\r
+       var tempLocale = availableFlatLocales ? bestLocale : targetLocale;\r
+       var bundle = dojo._loadedModules[bundlePackage];\r
+       var localizedBundle = null;\r
+       if(bundle){\r
+               if(djConfig.localizationComplete && bundle._built){return;}\r
+               var jsLoc = tempLocale.replace(/-/g, '_');\r
+               var translationPackage = bundlePackage+"."+jsLoc;\r
+               localizedBundle = dojo._loadedModules[translationPackage];\r
+       }\r
+\r
+       if(!localizedBundle){\r
+               bundle = dojo["provide"](bundlePackage);\r
+               var syms = dojo._getModuleSymbols(moduleName);\r
+               var modpath = syms.concat("nls").join("/");\r
+               var parent;\r
+\r
+               dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){\r
+                       var jsLoc = loc.replace(/-/g, '_');\r
+                       var translationPackage = bundlePackage + "." + jsLoc;\r
+                       var loaded = false;\r
+                       if(!dojo._loadedModules[translationPackage]){\r
+                               // Mark loaded whether it's found or not, so that further load attempts will not be made\r
+                               dojo["provide"](translationPackage);\r
+                               var module = [modpath];\r
+                               if(loc != "ROOT"){module.push(loc);}\r
+                               module.push(bundleName);\r
+                               var filespec = module.join("/") + '.js';\r
+                               loaded = dojo._loadPath(filespec, null, function(hash){\r
+                                       // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath\r
+                                       var clazz = function(){};\r
+                                       clazz.prototype = parent;\r
+                                       bundle[jsLoc] = new clazz();\r
+                                       for(var j in hash){ bundle[jsLoc][j] = hash[j]; }\r
+                               });\r
+                       }else{\r
+                               loaded = true;\r
+                       }\r
+                       if(loaded && bundle[jsLoc]){\r
+                               parent = bundle[jsLoc];\r
+                       }else{\r
+                               bundle[jsLoc] = parent;\r
+                       }\r
+                       \r
+                       if(availableFlatLocales){\r
+                               //Stop the locale path searching if we know the availableFlatLocales, since\r
+                               //the first call to this function will load the only bundle that is needed.\r
+                               return true;\r
+                       }\r
+               });\r
+       }\r
+\r
+       //Save the best locale bundle as the target locale bundle when we know the\r
+       //the available bundles.\r
+       if(availableFlatLocales && targetLocale != bestLocale){\r
+               bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];\r
+       }\r
+};\r
+\r
+(function(){\r
+       // If other locales are used, dojo.requireLocalization should load them as\r
+       // well, by default. \r
+       // \r
+       // Override dojo.requireLocalization to do load the default bundle, then\r
+       // iterate through the extraLocale list and load those translations as\r
+       // well, unless a particular locale was requested.\r
+\r
+       var extra = djConfig.extraLocale;\r
+       if(extra){\r
+               if(!extra instanceof Array){\r
+                       extra = [extra];\r
+               }\r
+\r
+               var req = dojo.i18n._requireLocalization;\r
+               dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){\r
+                       req(m,b,locale, availableFlatLocales);\r
+                       if(locale){return;}\r
+                       for(var i=0; i<extra.length; i++){\r
+                               req(m,b,extra[i], availableFlatLocales);\r
+                       }\r
+               };\r
+       }\r
+})();\r
+\r
+dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){\r
+       //      summary:\r
+       //              A helper method to assist in searching for locale-based resources.\r
+       //              Will iterate through the variants of a particular locale, either up\r
+       //              or down, executing a callback function.  For example, "en-us" and\r
+       //              true will try "en-us" followed by "en" and finally "ROOT".\r
+\r
+       locale = dojo.i18n.normalizeLocale(locale);\r
+\r
+       var elements = locale.split('-');\r
+       var searchlist = [];\r
+       for(var i = elements.length; i > 0; i--){\r
+               searchlist.push(elements.slice(0, i).join('-'));\r
+       }\r
+       searchlist.push(false);\r
+       if(down){searchlist.reverse();}\r
+\r
+       for(var j = searchlist.length - 1; j >= 0; j--){\r
+               var loc = searchlist[j] || "ROOT";\r
+               var stop = searchFunc(loc);\r
+               if(stop){ break; }\r
+       }\r
+};\r
+\r
+dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){\r
+       //      summary:\r
+       //              Load built, flattened resource bundles, if available for all\r
+       //              locales used in the page. Only called by built layer files.\r
+\r
+       function preload(locale){\r
+               locale = dojo.i18n.normalizeLocale(locale);\r
+               dojo.i18n._searchLocalePath(locale, true, function(loc){\r
+                       for(var i=0; i<localesGenerated.length;i++){\r
+                               if(localesGenerated[i] == loc){\r
+                                       dojo["require"](bundlePrefix+"_"+loc);\r
+                                       return true; // Boolean\r
+                               }\r
+                       }\r
+                       return false; // Boolean\r
+               });\r
+       }\r
+       preload();\r
+       var extra = djConfig.extraLocale||[];\r
+       for(var i=0; i<extra.length; i++){\r
+               preload(extra[i]);\r
+       }\r
+};\r
+\r
+}\r
+\r
diff --git a/webapps/cometd/examples/dojo/dojox/cometd.js b/webapps/cometd/examples/dojo/dojox/cometd.js
new file mode 100644 (file)
index 0000000..51a770d
--- /dev/null
@@ -0,0 +1,20 @@
+/*\r
+       Copyright (c) 2004-2007, The Dojo Foundation\r
+       All Rights Reserved.\r
+\r
+       Licensed under the Academic Free License version 2.1 or above OR the\r
+       modified BSD license. For more information on Dojo licensing, see:\r
+\r
+               http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing\r
+*/\r
+\r
+/*\r
+       This is a compiled version of Dojo, built for deployment and not for\r
+       development. To get an editable version, please visit:\r
+\r
+               http://dojotoolkit.org\r
+\r
+       for documentation and information on getting the source.\r
+*/\r
+\r
+if(!dojo._hasResource["dojo.AdapterRegistry"]){dojo._hasResource["dojo.AdapterRegistry"]=true;dojo.provide("dojo.AdapterRegistry");dojo.AdapterRegistry=function(_1){this.pairs=[];this.returnWrappers=_1||false;};dojo.extend(dojo.AdapterRegistry,{register:function(_2,_3,_4,_5,_6){this.pairs[((_6)?"unshift":"push")]([_2,_3,_4,_5]);},match:function(){for(var i=0;i<this.pairs.length;i++){var _8=this.pairs[i];if(_8[1].apply(this,arguments)){if((_8[3])||(this.returnWrappers)){return _8[2];}else{return _8[2].apply(this,arguments);}}}throw new Error("No match found");},unregister:function(_9){for(var i=0;i<this.pairs.length;i++){var _b=this.pairs[i];if(_b[0]==_9){this.pairs.splice(i,1);return true;}}return false;}});}if(!dojo._hasResource["dojo.io.script"]){dojo._hasResource["dojo.io.script"]=true;dojo.provide("dojo.io.script");dojo.io.script={get:function(_c){var _d=this._makeScriptDeferred(_c);var _e=_d.ioArgs;dojo._ioAddQueryToUrl(_e);this.attach(_e.id,_e.url);dojo._ioWatch(_d,this._validCheck,this._ioCheck,this._resHandle);return _d;},attach:function(id,url){var _11=dojo.doc.createElement("script");_11.type="text/javascript";_11.src=url;_11.id=id;dojo.doc.getElementsByTagName("head")[0].appendChild(_11);},remove:function(id){dojo._destroyElement(dojo.byId(id));if(this["jsonp_"+id]){delete this["jsonp_"+id];}},_makeScriptDeferred:function(_13){var dfd=dojo._ioSetArgs(_13,this._deferredCancel,this._deferredOk,this._deferredError);var _15=dfd.ioArgs;_15.id="dojoIoScript"+(this._counter++);_15.canDelete=false;if(_13.callbackParamName){_15.query=_15.query||"";if(_15.query.length>0){_15.query+="&";}_15.query+=_13.callbackParamName+"=dojo.io.script.jsonp_"+_15.id+"._jsonpCallback";_15.canDelete=true;dfd._jsonpCallback=this._jsonpCallback;this["jsonp_"+_15.id]=dfd;}return dfd;},_deferredCancel:function(dfd){dfd.canceled=true;if(dfd.ioArgs.canDelete){dojo.io.script._deadScripts.push(dfd.ioArgs.id);}},_deferredOk:function(dfd){if(dfd.ioArgs.canDelete){dojo.io.script._deadScripts.push(dfd.ioArgs.id);}if(dfd.ioArgs.json){return dfd.ioArgs.json;}else{return dfd.ioArgs;}},_deferredError:function(_18,dfd){if(dfd.ioArgs.canDelete){if(_18.dojoType=="timeout"){dojo.io.script.remove(dfd.ioArgs.id);}else{dojo.io.script._deadScripts.push(dfd.ioArgs.id);}}console.debug("dojo.io.script error",_18);return _18;},_deadScripts:[],_counter:1,_validCheck:function(dfd){var _1b=dojo.io.script;var _1c=_1b._deadScripts;if(_1c&&_1c.length>0){for(var i=0;i<_1c.length;i++){_1b.remove(_1c[i]);}dojo.io.script._deadScripts=[];}return true;},_ioCheck:function(dfd){if(dfd.ioArgs.json){return true;}var _1f=dfd.ioArgs.args.checkString;if(_1f&&eval("typeof("+_1f+") != 'undefined'")){return true;}return false;},_resHandle:function(dfd){if(dojo.io.script._ioCheck(dfd)){dfd.callback(dfd);}else{dfd.errback(new Error("inconceivable dojo.io.script._resHandle error"));}},_jsonpCallback:function(_21){this.ioArgs.json=_21;}};}if(!dojo._hasResource["dojox.cometd._base"]){dojo._hasResource["dojox.cometd._base"]=true;dojo.provide("dojox.cometd._base");dojox.cometd=new function(){this.DISCONNECTED="DISCONNECTED";this.CONNECTING="CONNECTING";this.CONNECTED="CONNECTED";this.DISCONNECTING="DISCONNECING";this._initialized=false;this._connected=false;this._polling=false;this.connectionTypes=new dojo.AdapterRegistry(true);this.version="1.0";this.minimumVersion="0.9";this.clientId=null;this.messageId=0;this.batch=0;this._isXD=false;this.handshakeReturn=null;this.currentTransport=null;this.url=null;this.lastMessage=null;this._messageQ=[];this.handleAs="json-comment-optional";this._advice={};this._maxInterval=30000;this._backoffInterval=1000;this._deferredSubscribes={};this._deferredUnsubscribes={};this._subscriptions=[];this._extendInList=[];this._extendOutList=[];this.state=function(){return this._initialized?(this._connected?this.CONNECTED:this.CONNECTING):(this._connected?this.DISCONNECTING:this.DISCONNECTED);};this.init=function(_22,_23,_24){_23=_23||{};_23.version=this.version;_23.minimumVersion=this.minimumVersion;_23.channel="/meta/handshake";_23.id=""+this.messageId++;this.url=_22||djConfig["cometdRoot"];if(!this.url){console.debug("no cometd root specified in djConfig and no root passed");return null;}var _25="^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";var _26=(""+window.location).match(new RegExp(_25));if(_26[4]){var tmp=_26[4].split(":");var _28=tmp[0];var _29=tmp[1]||"80";_26=this.url.match(new RegExp(_25));if(_26[4]){tmp=_26[4].split(":");var _2a=tmp[0];var _2b=tmp[1]||"80";this._isXD=((_2a!=_28)||(_2b!=_29));}}if(!this._isXD){if(_23.ext){if(_23.ext["json-comment-filtered"]!==true&&_23.ext["json-comment-filtered"]!==false){_23.ext["json-comment-filtered"]=true;}}else{_23.ext={"json-comment-filtered":true};}}_23=this._extendOut(_23);var _2c={url:this.url,handleAs:this.handleAs,content:{"message":dojo.toJson([_23])},load:dojo.hitch(this,function(msg){this._finishInit(msg);}),error:dojo.hitch(this,function(e){console.debug("handshake error!:",e);this._finishInit([{}]);})};if(_24){dojo.mixin(_2c,_24);}this._props=_23;for(var _2f in this._subscriptions){for(var sub in this._subscriptions[_2f]){if(this._subscriptions[_2f][sub].topic){dojo.unsubscribe(this._subscriptions[_2f][sub].topic);}}}this._messageQ=[];this._subscriptions=[];this._initialized=true;this.batch=0;this.startBatch();var r;if(this._isXD){_2c.callbackParamName="jsonp";r=dojo.io.script.get(_2c);}else{r=dojo.xhrPost(_2c);}dojo.publish("/cometd/meta",[{cometd:this,action:"handshake",successful:true,state:this.state()}]);return r;};this.publish=function(_32,_33,_34){var _35={data:_33,channel:_32};if(_34){dojo.mixin(_35,_34);}this._sendMessage(_35);};this.subscribe=function(_36,_37,_38){if(_37){var _39="/cometd"+_36;var _3a=this._subscriptions[_39];if(!_3a||_3a.length==0){_3a=[];this._sendMessage({channel:"/meta/subscribe",subscription:_36});var _ds=this._deferredSubscribes;_ds[_36]=new dojo.Deferred();if(_ds[_36]){_ds[_36].cancel();delete _ds[_36];}}for(var i in _3a){if(_3a[i].objOrFunc===_37&&(!_3a[i].funcName&&!_38||_3a[i].funcName==_38)){return null;}}var _3d=dojo.subscribe(_39,_37,_38);_3a.push({topic:_3d,objOrFunc:_37,funcName:_38});this._subscriptions[_39]=_3a;}return this._deferredSubscribes[_36];};this.unsubscribe=function(_3e,_3f,_40){var _41="/cometd"+_3e;var _42=this._subscriptions[_41];if(!_42||_42.length==0){return null;}var s=0;for(var i in _42){var sb=_42[i];if((!_3f)||(sb.objOrFunc===_3f&&(!sb.funcName&&!_40||sb.funcName==_40))){dojo.unsubscribe(_42[i].topic);delete _42[i];}else{s++;}}if(s==0){delete this._subscriptions[_41];this._sendMessage({channel:"/meta/unsubscribe",subscription:_3e});this._deferredUnsubscribes[_3e]=new dojo.Deferred();if(this._deferredSubscribes[_3e]){this._deferredSubscribes[_3e].cancel();delete this._deferredSubscribes[_3e];}}return this._deferredUnsubscribes[_3e];};this.disconnect=function(){for(var _46 in this._subscriptions){for(var sub in this._subscriptions[_46]){if(this._subscriptions[_46][sub].topic){dojo.unsubscribe(this._subscriptions[_46][sub].topic);}}}this._subscriptions=[];this._messageQ=[];if(this._initialized&&this.currentTransport){this._initialized=false;this.currentTransport.disconnect();}if(!this._polling){this._connected=false;dojo.publish("/cometd/meta",[{cometd:this,action:"connect",successful:false,state:this.state()}]);}this._initialized=false;dojo.publish("/cometd/meta",[{cometd:this,action:"disconnect",successful:true,state:this.state()}]);};this.subscribed=function(_48,_49){};this.unsubscribed=function(_4a,_4b){};this.tunnelInit=function(_4c,_4d){};this.tunnelCollapse=function(){};this._backoff=function(){if(!this._advice||!this._advice.interval){this._advice={reconnect:"retry",interval:0};}if(this._advice.interval<this._maxInterval){this._advice.interval+=this._backoffInterval;}};this._finishInit=function(_4e){_4e=_4e[0];this.handshakeReturn=_4e;if(_4e["advice"]){this._advice=_4e.advice;}var _4f=_4e.successful?_4e.successful:false;if(_4e.version<this.minimumVersion){console.debug("cometd protocol version mismatch. We wanted",this.minimumVersion,"but got",_4e.version);_4f=false;this._advice.reconnect="none";}if(_4f){this.currentTransport=this.connectionTypes.match(_4e.supportedConnectionTypes,_4e.version,this._isXD);this.currentTransport._cometd=this;this.currentTransport.version=_4e.version;this.clientId=_4e.clientId;this.tunnelInit=dojo.hitch(this.currentTransport,"tunnelInit");this.tunnelCollapse=dojo.hitch(this.currentTransport,"tunnelCollapse");this.currentTransport.startup(_4e);}dojo.publish("/cometd/meta",[{cometd:this,action:"handshook",successful:_4f,state:this.state()}]);if(!_4f){console.debug("cometd init failed");this._backoff();if(this._advice&&this._advice["reconnect"]=="none"){console.debug("cometd reconnect: none");}else{if(this._advice&&this._advice["interval"]&&this._advice.interval>0){setTimeout(dojo.hitch(this,function(){this.init(cometd.url,this._props);}),this._advice.interval);}else{this.init(this.url,this._props);}}}};this._extendIn=function(_50){var m=_50;dojo.forEach(dojox.cometd._extendInList,function(f){var n=f(m);if(n){m=n;}});return m;};this._extendOut=function(_54){var m=_54;dojo.forEach(dojox.cometd._extendOutList,function(f){var n=f(m);if(n){m=n;}});return m;};this.deliver=function(_58){dojo.forEach(_58,this._deliver,this);return _58;};this._deliver=function(_59){_59=this._extendIn(_59);if(!_59["channel"]){if(_59["success"]!==true){console.debug("cometd error: no channel for message!",_59);return;}}this.lastMessage=_59;if(_59.advice){this._advice=_59.advice;}var _5a=null;if((_59["channel"])&&(_59.channel.length>5)&&(_59.channel.substr(0,5)=="/meta")){switch(_59.channel){case "/meta/connect":if(_59.successful&&!this._connected){this._connected=this._initialized;this.endBatch();}else{if(!this._initialized){this._connected=false;}}dojo.publish("/cometd/meta",[{cometd:this,action:"connect",successful:_59.successful,state:this.state()}]);break;case "/meta/subscribe":_5a=this._deferredSubscribes[_59.subscription];if(!_59.successful){if(_5a){_5a.errback(new Error(_59.error));}return;}dojox.cometd.subscribed(_59.subscription,_59);if(_5a){_5a.callback(true);}break;case "/meta/unsubscribe":_5a=this._deferredUnsubscribes[_59.subscription];if(!_59.successful){if(_5a){_5a.errback(new Error(_59.error));}return;}this.unsubscribed(_59.subscription,_59);if(_5a){_5a.callback(true);}break;}}this.currentTransport.deliver(_59);if(_59.data){try{var _5b="/cometd"+_59.channel;dojo.publish(_5b,[_59]);}catch(e){console.debug(e);}}};this._sendMessage=function(_5c){if(this.currentTransport&&this._connected&&this.batch==0){return this.currentTransport.sendMessages([_5c]);}else{this._messageQ.push(_5c);return null;}};this.startBatch=function(){this.batch++;};this.endBatch=function(){if(--this.batch<=0&&this.currentTransport&&this._connected){this.batch=0;var _5d=this._messageQ;this._messageQ=[];if(_5d.length>0){this.currentTransport.sendMessages(_5d);}}};this._onUnload=function(){dojo.addOnUnload(dojox.cometd,"disconnect");};};dojox.cometd.longPollTransport=new function(){this._connectionType="long-polling";this._cometd=null;this.check=function(_5e,_5f,_60){return ((!_60)&&(dojo.indexOf(_5e,"long-polling")>=0));};this.tunnelInit=function(){var _61={channel:"/meta/connect",clientId:this._cometd.clientId,connectionType:this._connectionType,id:""+this._cometd.messageId++};_61=this._cometd._extendOut(_61);this.openTunnelWith({message:dojo.toJson([_61])});};this.tunnelCollapse=function(){if(!this._cometd._initialized){return;}if(this._cometd._advice){if(this._cometd._advice["reconnect"]=="none"){return;}if((this._cometd._advice["interval"])&&(this._cometd._advice.interval>0)){setTimeout(dojo.hitch(this,function(){this._connect();}),this._cometd._advice.interval);}else{this._connect();}}else{this._connect();}};this._connect=function(){if(!this._cometd._initialized){return;}if(this._cometd._polling){console.debug("wait for poll to complete or fail");return;}if((this._cometd._advice)&&(this._cometd._advice["reconnect"]=="handshake")){this._cometd._connected=false;this._initialized=false;this._cometd.init(this._cometd.url,this._cometd._props);}else{if(this._cometd._connected){var _62={channel:"/meta/connect",connectionType:this._connectionType,clientId:this._cometd.clientId,id:""+this._cometd.messageId++};_62=this._cometd._extendOut(_62);this.openTunnelWith({message:dojo.toJson([_62])});}}};this.deliver=function(_63){};this.openTunnelWith=function(_64,url){this._cometd._polling=true;var d=dojo.xhrPost({url:(url||this._cometd.url),content:_64,handleAs:this._cometd.handleAs,load:dojo.hitch(this,function(_67){this._cometd._polling=false;this._cometd.deliver(_67);this.tunnelCollapse();}),error:dojo.hitch(this,function(err){this._cometd._polling=false;console.debug("tunnel opening failed:",err);dojo.publish("/cometd/meta",[{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);this._cometd._backoff();this.tunnelCollapse();})});};this.sendMessages=function(_69){for(var i=0;i<_69.length;i++){_69[i].clientId=this._cometd.clientId;_69[i].id=""+this._cometd.messageId++;_69[i]=this._cometd._extendOut(_69[i]);}return dojo.xhrPost({url:this._cometd.url||djConfig["cometdRoot"],handleAs:this._cometd.handleAs,load:dojo.hitch(this._cometd,"deliver"),content:{message:dojo.toJson(_69)}});};this.startup=function(_6b){if(this._cometd._connected){return;}this.tunnelInit();};this.disconnect=function(){var _6c={channel:"/meta/disconnect",clientId:this._cometd.clientId,id:""+this._cometd.messageId++};_6c=this._cometd._extendOut(_6c);dojo.xhrPost({url:this._cometd.url||djConfig["cometdRoot"],handleAs:this._cometd.handleAs,content:{message:dojo.toJson([_6c])}});};};dojox.cometd.callbackPollTransport=new function(){this._connectionType="callback-polling";this._cometd=null;this.check=function(_6d,_6e,_6f){return (dojo.indexOf(_6d,"callback-polling")>=0);};this.tunnelInit=function(){var _70={channel:"/meta/connect",clientId:this._cometd.clientId,connectionType:this._connectionType,id:""+this._cometd.messageId++};_70=this._cometd._extendOut(_70);this.openTunnelWith({message:dojo.toJson([_70])});};this.tunnelCollapse=dojox.cometd.longPollTransport.tunnelCollapse;this._connect=dojox.cometd.longPollTransport._connect;this.deliver=dojox.cometd.longPollTransport.deliver;this.openTunnelWith=function(_71,url){this._cometd._polling=true;dojo.io.script.get({load:dojo.hitch(this,function(_73){this._cometd._polling=false;this._cometd.deliver(_73);this.tunnelCollapse();}),error:dojo.hitch(this,function(err){this._cometd._polling=false;console.debug("tunnel opening failed:",err);dojo.publish("/cometd/meta",[{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);this._cometd._backoff();this.tunnelCollapse();}),url:(url||this._cometd.url),content:_71,callbackParamName:"jsonp"});};this.sendMessages=function(_75){for(var i=0;i<_75.length;i++){_75[i].clientId=this._cometd.clientId;_75[i].id=""+this._cometd.messageId++;_75[i]=this._cometd._extendOut(_75[i]);}var _77={url:this._cometd.url||djConfig["cometdRoot"],load:dojo.hitch(this._cometd,"deliver"),callbackParamName:"jsonp",content:{message:dojo.toJson(_75)}};return dojo.io.script.get(_77);};this.startup=function(_78){if(this._cometd._connected){return;}this.tunnelInit();};this.disconnect=dojox.cometd.longPollTransport.disconnect;this.disconnect=function(){var _79={channel:"/meta/disconnect",clientId:this._cometd.clientId,id:""+this._cometd.messageId++};_79=this._cometd._extendOut(_79);dojo.io.script.get({url:this._cometd.url||djConfig["cometdRoot"],callbackParamName:"jsonp",content:{message:dojo.toJson([_79])}});};};dojox.cometd.connectionTypes.register("long-polling",dojox.cometd.longPollTransport.check,dojox.cometd.longPollTransport);dojox.cometd.connectionTypes.register("callback-polling",dojox.cometd.callbackPollTransport.check,dojox.cometd.callbackPollTransport);dojo.addOnUnload(dojox.cometd,"_onUnload");}if(!dojo._hasResource["dojox.cometd"]){dojo._hasResource["dojox.cometd"]=true;dojo.provide("dojox.cometd");}\r
diff --git a/webapps/cometd/examples/dojo/dojox/cometd.js.uncompressed.js b/webapps/cometd/examples/dojo/dojox/cometd.js.uncompressed.js
new file mode 100644 (file)
index 0000000..2bd7329
--- /dev/null
@@ -0,0 +1,1142 @@
+/*\r
+       Copyright (c) 2004-2007, The Dojo Foundation\r
+       All Rights Reserved.\r
+\r
+       Licensed under the Academic Free License version 2.1 or above OR the\r
+       modified BSD license. For more information on Dojo licensing, see:\r
+\r
+               http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing\r
+*/\r
+\r
+/*\r
+       This is a compiled version of Dojo, built for deployment and not for\r
+       development. To get an editable version, please visit:\r
+\r
+               http://dojotoolkit.org\r
+\r
+       for documentation and information on getting the source.\r
+*/\r
+\r
+if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo.AdapterRegistry"] = true;\r
+dojo.provide("dojo.AdapterRegistry");\r
+\r
+dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){\r
+       //      summary:\r
+       //              A registry to make contextual calling/searching easier.\r
+       //      description:\r
+       //              Objects of this class keep list of arrays in the form [name, check,\r
+       //              wrap, directReturn] that are used to determine what the contextual\r
+       //              result of a set of checked arguments is. All check/wrap functions\r
+       //              in this registry should be of the same arity.\r
+       //      example:\r
+       //      |       // create a new registry\r
+       //      |       var reg = new dojo.AdapterRegistry();\r
+       //      |       reg.register("handleString",\r
+       //      |               dojo.isString,\r
+       //      |               function(str){\r
+       //      |                       // do something with the string here\r
+       //      |               }\r
+       //      |       );\r
+       //      |       reg.register("handleArr",\r
+       //      |               dojo.isArray,\r
+       //      |               function(arr){\r
+       //      |                       // do something with the array here\r
+       //      |               }\r
+       //      |       );\r
+       //      |\r
+       //      |       // now we can pass reg.match() *either* an array or a string and\r
+       //      |       // the value we pass will get handled by the right function\r
+       //      |       reg.match("someValue"); // will call the first function\r
+       //      |       reg.match(["someValue"]); // will call the second\r
+\r
+       this.pairs = [];\r
+       this.returnWrappers = returnWrappers || false; // Boolean\r
+}\r
+\r
+dojo.extend(dojo.AdapterRegistry, {\r
+       register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){\r
+               //      summary: \r
+               //              register a check function to determine if the wrap function or\r
+               //              object gets selected\r
+               //      name:\r
+               //              a way to identify this matcher.\r
+               //      check:\r
+               //              a function that arguments are passed to from the adapter's\r
+               //              match() function.  The check function should return true if the\r
+               //              given arguments are appropriate for the wrap function.\r
+               //      directReturn:\r
+               //              If directReturn is true, the value passed in for wrap will be\r
+               //              returned instead of being called. Alternately, the\r
+               //              AdapterRegistry can be set globally to "return not call" using\r
+               //              the returnWrappers property. Either way, this behavior allows\r
+               //              the registry to act as a "search" function instead of a\r
+               //              function interception library.\r
+               //      override:\r
+               //              If override is given and true, the check function will be given\r
+               //              highest priority. Otherwise, it will be the lowest priority\r
+               //              adapter.\r
+               this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]);\r
+       },\r
+\r
+       match: function(/* ... */){\r
+               // summary:\r
+               //              Find an adapter for the given arguments. If no suitable adapter\r
+               //              is found, throws an exception. match() accepts any number of\r
+               //              arguments, all of which are passed to all matching functions\r
+               //              from the registered pairs.\r
+               for(var i = 0; i < this.pairs.length; i++){\r
+                       var pair = this.pairs[i];\r
+                       if(pair[1].apply(this, arguments)){\r
+                               if((pair[3])||(this.returnWrappers)){\r
+                                       return pair[2];\r
+                               }else{\r
+                                       return pair[2].apply(this, arguments);\r
+                               }\r
+                       }\r
+               }\r
+               throw new Error("No match found");\r
+       },\r
+\r
+       unregister: function(name){\r
+               // summary: Remove a named adapter from the registry\r
+\r
+               // FIXME: this is kind of a dumb way to handle this. On a large\r
+               // registry this will be slow-ish and we can use the name as a lookup\r
+               // should we choose to trade memory for speed.\r
+               for(var i = 0; i < this.pairs.length; i++){\r
+                       var pair = this.pairs[i];\r
+                       if(pair[0] == name){\r
+                               this.pairs.splice(i, 1);\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+});\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo.io.script"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo.io.script"] = true;\r
+dojo.provide("dojo.io.script");\r
+\r
+/*=====\r
+dojo.io.script.__ioArgs = function(kwArgs){\r
+       //      summary:\r
+       //              All the properties described in the dojo.__ioArgs type, apply to this\r
+       //              type as well, EXCEPT "handleAs". It is not applicable to\r
+       //              dojo.io.script.get() calls, since it is implied by the usage of\r
+       //              "callbackParamName" (response will be a JSONP call returning JSON)\r
+       //              or "checkString" (response is pure JavaScript defined in\r
+       //              the body of the script that was attached). The following additional\r
+       //              properties are allowed for dojo.io.script.get():\r
+       //      callbackParamName: String\r
+       //              The URL parameter name that indicates the JSONP callback string.\r
+       //              For instance, when using Yahoo JSONP calls it is normally, \r
+       //              callbackParamName: "callback". For AOL JSONP calls it is normally \r
+       //              callbackParamName: "c".\r
+       //      checkString: String\r
+       //              A string of JavaScript that when evaluated like so: \r
+       //              "typeof(" + checkString + ") != 'undefined'"\r
+       //              being true means that the script fetched has been loaded. \r
+       //              Do not use this if doing a JSONP type of call (use callbackParamName instead).\r
+}\r
+=====*/\r
+\r
+dojo.io.script = {\r
+       get: function(/*dojo.io.script.__ioArgs*/args){\r
+               //      summary:\r
+               //              sends a get request using a dynamically created script tag.\r
+               var dfd = this._makeScriptDeferred(args);\r
+               var ioArgs = dfd.ioArgs;\r
+               dojo._ioAddQueryToUrl(ioArgs);\r
+\r
+               this.attach(ioArgs.id, ioArgs.url);\r
+               dojo._ioWatch(dfd, this._validCheck, this._ioCheck, this._resHandle);\r
+               return dfd;\r
+       },\r
+\r
+       attach: function(/*String*/id, /*String*/url){\r
+               //      summary:\r
+               //              creates a new <script> tag pointing to the specified URL and\r
+               //              adds it to the document.\r
+               //      description:\r
+               //              Attaches the script element to the DOM.  Use this method if you\r
+               //              just want to attach a script to the DOM and do not care when or\r
+               //              if it loads.\r
+               var element = dojo.doc.createElement("script");\r
+               element.type = "text/javascript";\r
+               element.src = url;\r
+               element.id = id;\r
+               dojo.doc.getElementsByTagName("head")[0].appendChild(element);\r
+       },\r
+\r
+       remove: function(/*String*/id){\r
+               //summary: removes the script element with the given id.\r
+               dojo._destroyElement(dojo.byId(id));\r
+               \r
+               //Remove the jsonp callback on dojo.io.script, if it exists.\r
+               if(this["jsonp_" + id]){\r
+                       delete this["jsonp_" + id];\r
+               }\r
+       },\r
+\r
+       _makeScriptDeferred: function(/*Object*/args){\r
+               //summary: \r
+               //              sets up a Deferred object for an IO request.\r
+               var dfd = dojo._ioSetArgs(args, this._deferredCancel, this._deferredOk, this._deferredError);\r
+\r
+               var ioArgs = dfd.ioArgs;\r
+               ioArgs.id = "dojoIoScript" + (this._counter++);\r
+               ioArgs.canDelete = false;\r
+\r
+               //Special setup for jsonp case\r
+               if(args.callbackParamName){\r
+                       //Add the jsonp parameter.\r
+                       ioArgs.query = ioArgs.query || "";\r
+                       if(ioArgs.query.length > 0){\r
+                               ioArgs.query += "&";\r
+                       }\r
+                       ioArgs.query += args.callbackParamName + "=dojo.io.script.jsonp_" + ioArgs.id + "._jsonpCallback";\r
+\r
+                       //Setup the Deferred to have the jsonp callback.\r
+                       ioArgs.canDelete = true;\r
+                       dfd._jsonpCallback = this._jsonpCallback;\r
+                       this["jsonp_" + ioArgs.id] = dfd;\r
+               }\r
+               return dfd; // dojo.Deferred\r
+       },\r
+       \r
+       _deferredCancel: function(/*Deferred*/dfd){\r
+               //summary: canceller function for dojo._ioSetArgs call.\r
+\r
+               //DO NOT use "this" and expect it to be dojo.io.script.\r
+               dfd.canceled = true;\r
+               if(dfd.ioArgs.canDelete){\r
+                       dojo.io.script._deadScripts.push(dfd.ioArgs.id);\r
+               }\r
+       },\r
+\r
+       _deferredOk: function(/*Deferred*/dfd){\r
+               //summary: okHandler function for dojo._ioSetArgs call.\r
+\r
+               //DO NOT use "this" and expect it to be dojo.io.script.\r
+\r
+               //Add script to list of things that can be removed.             \r
+               if(dfd.ioArgs.canDelete){\r
+                       dojo.io.script._deadScripts.push(dfd.ioArgs.id);\r
+               }\r
+\r
+               if(dfd.ioArgs.json){\r
+                       //Make sure to *not* remove the json property from the\r
+                       //Deferred, so that the Deferred can still function correctly\r
+                       //after the response is received.\r
+                       return dfd.ioArgs.json;\r
+               }else{\r
+                       //FIXME: cannot return the dfd here, otherwise that stops\r
+                       //the callback chain in Deferred. So return the ioArgs instead.\r
+                       //This doesn't feel right.\r
+                       return dfd.ioArgs;\r
+               }\r
+       },\r
+       \r
+       _deferredError: function(/*Error*/error, /*Deferred*/dfd){\r
+               //summary: errHandler function for dojo._ioSetArgs call.\r
+\r
+               if(dfd.ioArgs.canDelete){\r
+                       //DO NOT use "this" and expect it to be dojo.io.script.\r
+                       if(error.dojoType == "timeout"){\r
+                               //For timeouts, remove the script element immediately to\r
+                               //avoid a response from it coming back later and causing trouble.\r
+                               dojo.io.script.remove(dfd.ioArgs.id);\r
+                       }else{\r
+                               dojo.io.script._deadScripts.push(dfd.ioArgs.id);\r
+                       }\r
+               }\r
+               console.debug("dojo.io.script error", error);\r
+               return error;\r
+       },\r
+\r
+       _deadScripts: [],\r
+       _counter: 1,\r
+\r
+       _validCheck: function(/*Deferred*/dfd){\r
+               //summary: inflight check function to see if dfd is still valid.\r
+\r
+               //Do script cleanup here. We wait for one inflight pass\r
+               //to make sure we don't get any weird things by trying to remove a script\r
+               //tag that is part of the call chain (IE 6 has been known to\r
+               //crash in that case).\r
+               var _self = dojo.io.script;\r
+               var deadScripts = _self._deadScripts;\r
+               if(deadScripts && deadScripts.length > 0){\r
+                       for(var i = 0; i < deadScripts.length; i++){\r
+                               //Remove the script tag\r
+                               _self.remove(deadScripts[i]);\r
+                       }\r
+                       dojo.io.script._deadScripts = [];\r
+               }\r
+\r
+               return true;\r
+       },\r
+\r
+       _ioCheck: function(/*Deferred*/dfd){\r
+               //summary: inflight check function to see if IO finished.\r
+\r
+               //Check for finished jsonp\r
+               if(dfd.ioArgs.json){\r
+                       return true;\r
+               }\r
+\r
+               //Check for finished "checkString" case.\r
+               var checkString = dfd.ioArgs.args.checkString;\r
+               if(checkString && eval("typeof(" + checkString + ") != 'undefined'")){\r
+                       return true;\r
+               }\r
+\r
+               return false;\r
+       },\r
+\r
+       _resHandle: function(/*Deferred*/dfd){\r
+               //summary: inflight function to handle a completed response.\r
+               if(dojo.io.script._ioCheck(dfd)){\r
+                       dfd.callback(dfd);\r
+               }else{\r
+                       //This path should never happen since the only way we can get\r
+                       //to _resHandle is if _ioCheck is true.\r
+                       dfd.errback(new Error("inconceivable dojo.io.script._resHandle error"));\r
+               }\r
+       },\r
+\r
+       _jsonpCallback: function(/*JSON Object*/json){\r
+               //summary: \r
+               //              generic handler for jsonp callback. A pointer to this function\r
+               //              is used for all jsonp callbacks.  NOTE: the "this" in this\r
+               //              function will be the Deferred object that represents the script\r
+               //              request.\r
+               this.ioArgs.json = json;\r
+       }\r
+}\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojox.cometd._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojox.cometd._base"] = true;\r
+dojo.provide("dojox.cometd._base");\r
+\r
+\r
+\r
+\r
+/*\r
+ * this file defines Comet protocol client. Actual message transport is\r
+ * deferred to one of several connection type implementations. The default is a\r
+ * long-polling implementation. A single global object named "dojox.cometd" is\r
+ * used to mediate for these connection types in order to provide a stable\r
+ * interface.\r
+ *\r
+ * extensions modules may be loaded (eg "dojox.cometd.timestamp", that use\r
+ * the cometd._extendInList and cometd._extendOutList fields to provide functions\r
+ * that extend and handling incoming and outgoing messages.\r
+ */\r
+\r
+dojox.cometd = new function(){\r
+       \r
+       // cometd states:\r
+       this.DISCONNECTED="DISCONNECTED";       // _initialized==false  && _connected==false\r
+       this.CONNECTING="CONNECTING";           // _initialized==true   && _connected==false (handshake sent)\r
+       this.CONNECTED="CONNECTED";             // _initialized==true   && _connected==true (first successful connect)\r
+       this.DISCONNECTING="DISCONNECING";      // _initialized==false  && _connected==true (disconnect sent)\r
+       \r
+       this._initialized = false;\r
+       this._connected = false;\r
+       this._polling = false;\r
+\r
+       this.connectionTypes = new dojo.AdapterRegistry(true);\r
+\r
+       this.version="1.0";\r
+       this.minimumVersion="0.9";\r
+       this.clientId=null;\r
+       this.messageId=0;\r
+       this.batch=0;\r
+\r
+       this._isXD = false;\r
+       this.handshakeReturn=null;\r
+       this.currentTransport=null;\r
+       this.url = null;\r
+       this.lastMessage=null;\r
+       this._messageQ=[];\r
+       this.handleAs="json-comment-optional";\r
+       this._advice={};\r
+       this._maxInterval=30000;\r
+       this._backoffInterval=1000;\r
+       this._deferredSubscribes={};\r
+       this._deferredUnsubscribes={};\r
+       this._subscriptions=[];\r
+       this._extendInList=[];  // List of functions invoked before delivering messages\r
+       this._extendOutList=[]; // List of functions invoked before sending messages\r
+\r
+       this.state = function() {\r
+               return this._initialized?(this._connected?this.CONNECTED:this.CONNECTING):(this._connected?this.DISCONNECTING:this.DISCONNECTED);\r
+       }\r
+\r
+       this.init = function(   /*String*/      root,\r
+                               /*Object|null */ props,\r
+                               /*Object|null */ bargs){        // return: dojo.Deferred\r
+               //      summary:\r
+               //              Initialize the cometd implementation of the Bayeux protocol\r
+               //      description:\r
+               //              Initialize the cometd implementation of the Bayeux protocol by\r
+               //              sending a handshake message. The cometd state will be changed to CONNECTING\r
+               //              until a handshake response is received and the first successful connect message\r
+               //              has returned.\r
+               //              The protocol state changes may be monitored\r
+               //              by subscribing to the dojo topic "/cometd/meta" where events are\r
+               //              published in the form {cometd:this,action:"handshake",successful:true,state:this.state()}\r
+               //      root:\r
+               //              The URL of the cometd server. If the root is absolute, the host\r
+               //              is examined to determine if xd transport is needed. Otherwise the\r
+               //              same domain is assumed.\r
+               //      props:\r
+               //              An optional object that is used as the basis of the handshake message\r
+               //      bargs:\r
+               //              An optional object of bind args mixed in with the send of the handshake\r
+               //      example:\r
+               //      |       dojox.cometd.init("/cometd");\r
+               //      |       dojox.cometd.init("http://xdHost/cometd",{ext:{user:"fred",pwd:"secret"}});\r
+\r
+\r
+               // FIXME: if the root isn't from the same host, we should automatically\r
+               // try to select an XD-capable transport\r
+               props = props||{};\r
+               // go ask the short bus server what we can support\r
+               props.version = this.version;\r
+               props.minimumVersion = this.minimumVersion;\r
+               props.channel = "/meta/handshake";\r
+               props.id = ""+this.messageId++;\r
+\r
+               this.url = root||djConfig["cometdRoot"];\r
+               if(!this.url){\r
+                       console.debug("no cometd root specified in djConfig and no root passed");\r
+                       return null;\r
+               }\r
+\r
+               // Are we x-domain? borrowed from dojo.uri.Uri in lieu of fixed host and port properties\r
+               var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";\r
+               var parts = (""+window.location).match(new RegExp(regexp));\r
+               if(parts[4]){\r
+                       var tmp = parts[4].split(":");\r
+                       var thisHost = tmp[0];\r
+                       var thisPort = tmp[1]||"80"; // FIXME: match 443\r
+\r
+                       parts = this.url.match(new RegExp(regexp));\r
+                       if(parts[4]){\r
+                               tmp = parts[4].split(":");\r
+                               var urlHost = tmp[0];\r
+                               var urlPort = tmp[1]||"80";\r
+                               this._isXD = ((urlHost != thisHost)||(urlPort != thisPort));\r
+                       }\r
+               }\r
+\r
+               if(!this._isXD){\r
+                       if(props.ext){\r
+                               if(props.ext["json-comment-filtered"]!==true && props.ext["json-comment-filtered"]!==false){\r
+                                       props.ext["json-comment-filtered"] = true;\r
+                               }\r
+                       }else{\r
+                               props.ext = { "json-comment-filtered": true };\r
+                       }\r
+               }\r
+\r
+               props=this._extendOut(props);\r
+\r
+               var bindArgs = {\r
+                       url: this.url,\r
+                       handleAs: this.handleAs,\r
+                       content: { "message": dojo.toJson([props]) },\r
+                       load: dojo.hitch(this,function(msg){\r
+                               this._finishInit(msg);\r
+                       }),\r
+                       error: dojo.hitch(this,function(e){\r
+                               console.debug("handshake error!:",e);\r
+                               this._finishInit([{}]);\r
+                       })\r
+               };\r
+\r
+               if(bargs){\r
+                       dojo.mixin(bindArgs, bargs);\r
+               }\r
+               this._props=props;\r
+               for(var tname in this._subscriptions){\r
+                       for(var sub in this._subscriptions[tname]){\r
+                               if(this._subscriptions[tname][sub].topic){\r
+                                       dojo.unsubscribe(this._subscriptions[tname][sub].topic);\r
+                               }\r
+                       }\r
+               }\r
+               this._messageQ = [];\r
+               this._subscriptions = [];\r
+               this._initialized=true;\r
+               this.batch=0;\r
+               this.startBatch();\r
+               \r
+               var r;\r
+               // if xdomain, then we assume jsonp for handshake\r
+               if(this._isXD){\r
+                       bindArgs.callbackParamName="jsonp";\r
+                       r= dojo.io.script.get(bindArgs);\r
+               } else\r
+                       r = dojo.xhrPost(bindArgs);\r
+               dojo.publish("/cometd/meta", [{cometd:this,action:"handshake",successful:true,state:this.state()}]);\r
+               return r;\r
+       }\r
+       \r
+       \r
+       this.publish = function(/*String*/channel, /*Object */data, /*Object|null */properties){\r
+               // summary:\r
+               //              publishes the passed message to the cometd server for delivery\r
+               //              on the specified topic\r
+               // channel:\r
+               //              the destination channel for the message\r
+               // data:\r
+               //              a JSON object containing the message "payload"\r
+               // properties:\r
+               //              Optional. Other meta-data to be mixed into the top-level of the\r
+               //              message\r
+               var message = {\r
+                       data: data,\r
+                       channel: channel\r
+               };\r
+               if(properties){\r
+                       dojo.mixin(message, properties);\r
+               }\r
+               this._sendMessage(message);\r
+       }\r
+\r
+       \r
+       this.subscribe = function(      /*String */     channel,\r
+                                       /*Object */     objOrFunc,\r
+                                       /*String */     funcName){ // return: dojo.Deferred\r
+               // summary:\r
+               //              inform the server of this client's interest in channel\r
+               // channel:\r
+               //              name of the cometd channel to subscribe to\r
+               // objOrFunc:\r
+               //              an object scope for funcName or the name or reference to a\r
+               //              function to be called when messages are delivered to the\r
+               //              channel\r
+               // funcName:\r
+               //              the second half of the objOrFunc/funcName pair for identifying\r
+               //              a callback function to notifiy upon channel message delivery\r
+\r
+               if(objOrFunc){\r
+                       var tname = "/cometd"+channel;\r
+                       var subs=this._subscriptions[tname];\r
+                       if(!subs || subs.length==0){\r
+                               subs=[];\r
+                               this._sendMessage({\r
+                                       channel: "/meta/subscribe",\r
+                                       subscription: channel\r
+                               });\r
+                               \r
+                               var _ds = this._deferredSubscribes;\r
+                               _ds[channel] = new dojo.Deferred();\r
+                               if(_ds[channel]){\r
+                                       _ds[channel].cancel();\r
+                                       delete _ds[channel];\r
+                               }\r
+                       }\r
+                       \r
+                       for (var i in subs){\r
+                               if (subs[i].objOrFunc===objOrFunc&&(!subs[i].funcName&&!funcName||subs[i].funcName==funcName))\r
+                                       return null;\r
+                       }\r
+                       \r
+                       var topic = dojo.subscribe(tname, objOrFunc, funcName);\r
+                       subs.push({ \r
+                               topic: topic, \r
+                               objOrFunc: objOrFunc, \r
+                               funcName: funcName\r
+                       });\r
+                       this._subscriptions[tname] =subs;\r
+               }\r
+               return this._deferredSubscribes[channel];\r
+       }\r
+\r
+\r
+\r
+       this.unsubscribe = function(    /*string*/      channel,\r
+                                       /*object|null*/ objOrFunc,\r
+                                       /*string|null*/ funcName){\r
+               // summary:\r
+               //              inform the server of this client's disinterest in channel\r
+               // channel:\r
+               //              name of the cometd channel to unsubscribe from\r
+               // objOrFunc:\r
+               //              an object scope for funcName or the name or reference to a\r
+               //              function to be called when messages are delivered to the\r
+               //              channel. If null then all subscribers to the channel are unsubscribed.\r
+               // funcName:\r
+               //              the second half of the objOrFunc/funcName pair for identifying\r
+               //              a callback function to notifiy upon channel message delivery\r
+               \r
+               var tname = "/cometd"+channel;\r
+               var subs=this._subscriptions[tname];\r
+               if(!subs || subs.length==0){\r
+                       return null;\r
+               }\r
+\r
+               var s=0;\r
+               for(var i in subs){\r
+                       var sb=subs[i];\r
+                       if( (!objOrFunc) ||\r
+                               (\r
+                                       sb.objOrFunc===objOrFunc &&\r
+                                       (!sb.funcName && !funcName || sb.funcName==funcName)\r
+                               )\r
+                       ){\r
+                               dojo.unsubscribe(subs[i].topic);\r
+                               delete subs[i];\r
+                       }else{\r
+                               s++;\r
+                       }\r
+               }\r
+               \r
+               if(s==0){\r
+                       delete this._subscriptions[tname];\r
+                       this._sendMessage({\r
+                               channel: "/meta/unsubscribe",\r
+                               subscription: channel\r
+                       });\r
+                       this._deferredUnsubscribes[channel] = new dojo.Deferred();\r
+                       if (this._deferredSubscribes[channel]){\r
+                               this._deferredSubscribes[channel].cancel();\r
+                               delete this._deferredSubscribes[channel];\r
+                       }\r
+               }\r
+               return this._deferredUnsubscribes[channel];\r
+       }\r
+       \r
+       \r
+       this.disconnect = function(){\r
+               //      summary:\r
+               //              Disconnect from the server.\r
+               //      description:\r
+               //              Disconnect from the server by sending a disconnect message\r
+               //      example:\r
+               //      |       dojox.cometd.disconnect();\r
+\r
+               for(var tname in this._subscriptions){\r
+                       for(var sub in this._subscriptions[tname]){\r
+                               if(this._subscriptions[tname][sub].topic){\r
+                                       dojo.unsubscribe(this._subscriptions[tname][sub].topic);\r
+                               }\r
+                       }\r
+               }\r
+               this._subscriptions = [];\r
+               this._messageQ = [];\r
+               if(this._initialized && this.currentTransport){\r
+                       this._initialized=false;\r
+                       this.currentTransport.disconnect();\r
+               }\r
+               if(!this._polling) {\r
+                       this._connected=false;\r
+                       dojo.publish("/cometd/meta", [{cometd:this,action:"connect",successful:false,state:this.state()}]);\r
+               }\r
+               this._initialized=false;\r
+               dojo.publish("/cometd/meta", [{cometd:this,action:"disconnect",successful:true,state:this.state()}]);\r
+       }\r
+\r
+       \r
+       // public extension points\r
+       \r
+       this.subscribed = function(     /*String*/channel, /*Object*/message){ }\r
+\r
+       this.unsubscribed = function(/*String*/channel, /*Object*/message){ }\r
+\r
+\r
+       // private methods (TODO name all with leading _)\r
+\r
+       this.tunnelInit = function(childLocation, childDomain){\r
+               // placeholder - replaced by _finishInit\r
+       }\r
+       \r
+       this.tunnelCollapse = function(){\r
+               // placeholder - replaced by _finishInit\r
+       }\r
+       \r
+       this._backoff = function(){\r
+               if(!this._advice || !this._advice.interval){\r
+                       this._advice={reconnect:"retry",interval:0}; // TODO Is this good advice?\r
+               }\r
+               if(this._advice.interval<this._maxInterval){\r
+                       this._advice.interval+=this._backoffInterval;\r
+               }\r
+       }\r
+\r
+       this._finishInit = function(data){\r
+               //      summary:\r
+               //              Handle the handshake return from the server and initialize\r
+               //              connection if all is OK\r
+               data = data[0];\r
+               this.handshakeReturn = data;\r
+               \r
+               // remember any advice\r
+               if(data["advice"]){\r
+                       this._advice = data.advice;\r
+               }\r
+\r
+               var successful=data.successful?data.successful:false;\r
+               \r
+               // check version\r
+               if(data.version < this.minimumVersion){\r
+                       console.debug("cometd protocol version mismatch. We wanted", this.minimumVersion, "but got", data.version);\r
+                       successful=false;\r
+                       this._advice.reconnect="none";\r
+               }\r
+               \r
+               // If all OK\r
+               if(successful){\r
+                       // pick a transport\r
+                       this.currentTransport = this.connectionTypes.match(\r
+                               data.supportedConnectionTypes,\r
+                               data.version,\r
+                               this._isXD\r
+                       );\r
+                       // initialize the transport\r
+                       this.currentTransport._cometd = this;\r
+                       this.currentTransport.version = data.version;\r
+                       this.clientId = data.clientId;\r
+                       this.tunnelInit = dojo.hitch(this.currentTransport, "tunnelInit");\r
+                       this.tunnelCollapse = dojo.hitch(this.currentTransport, "tunnelCollapse");\r
+                       this.currentTransport.startup(data);\r
+               }\r
+\r
+               dojo.publish("/cometd/meta", [{cometd:this,action:"handshook",successful:successful,state:this.state()}]);\r
+\r
+               // If there is a problem\r
+               if(!successful){\r
+                       console.debug("cometd init failed");\r
+                       this._backoff();\r
+                       // follow advice\r
+                       if(this._advice && this._advice["reconnect"]=="none"){\r
+                               console.debug("cometd reconnect: none");\r
+                       }else if(this._advice && this._advice["interval"] && this._advice.interval>0 ){\r
+                               setTimeout(\r
+                                       dojo.hitch(this, function(){ this.init(cometd.url,this._props); }),\r
+                                       this._advice.interval\r
+                               );\r
+                       }else{\r
+                               this.init(this.url,this._props);\r
+                       }\r
+               }\r
+       }\r
+\r
+       this._extendIn = function(message){\r
+               // Handle extensions for inbound messages\r
+               var m=message;\r
+               dojo.forEach(dojox.cometd._extendInList, function(f){\r
+                       var n=f(m);\r
+                       if(n)m=n;\r
+               });\r
+               return m;\r
+       }\r
+\r
+       this._extendOut= function(message){\r
+               // Handle extensions for inbound messages\r
+               var m=message;\r
+               dojo.forEach(dojox.cometd._extendOutList, function(f){\r
+                       var n=f(m);\r
+                       if(n)m=n;\r
+               });\r
+               return m;\r
+       }\r
+\r
+\r
+       this.deliver = function(messages){\r
+               dojo.forEach(messages, this._deliver, this);\r
+               return messages;\r
+       }\r
+\r
+       this._deliver = function(message){\r
+               // dipatch events along the specified path\r
+               \r
+               message=this._extendIn(message);\r
+\r
+               if(!message["channel"]){\r
+                       if(message["success"] !== true){\r
+                               console.debug("cometd error: no channel for message!", message);\r
+                               return;\r
+                       }\r
+               }\r
+               this.lastMessage = message;\r
+\r
+               if(message.advice){\r
+                       this._advice = message.advice; // TODO maybe merge?\r
+               }\r
+\r
+               // check to see if we got a /meta channel message that we care about\r
+               var deferred=null;\r
+               if(     (message["channel"]) &&\r
+                       (message.channel.length > 5)&&\r
+                       (message.channel.substr(0, 5) == "/meta")){\r
+                       // check for various meta topic actions that we need to respond to\r
+                       switch(message.channel){\r
+                               case "/meta/connect":\r
+                                       if(message.successful && !this._connected){\r
+                                               this._connected = this._initialized;\r
+                                               this.endBatch();\r
+                                       } else if(!this._initialized){\r
+                                               this._connected = false; // finish disconnect\r
+                                       }\r
+                                       dojo.publish("/cometd/meta",[{cometd:this,action:"connect",successful:message.successful,state:this.state()}]);\r
+                                       break;\r
+                               case "/meta/subscribe":\r
+                                       deferred = this._deferredSubscribes[message.subscription];\r
+                                       if(!message.successful){\r
+                                               if(deferred){\r
+                                                       deferred.errback(new Error(message.error));\r
+                                               }\r
+                                               return;\r
+                                       }\r
+                                       dojox.cometd.subscribed(message.subscription, message);\r
+                                       if(deferred){\r
+                                               deferred.callback(true);\r
+                                       }\r
+                                       break;\r
+                               case "/meta/unsubscribe":\r
+                                       deferred = this._deferredUnsubscribes[message.subscription];\r
+                                       if(!message.successful){\r
+                                               if(deferred){\r
+                                                       deferred.errback(new Error(message.error));\r
+                                               }\r
+                                               return;\r
+                                       }\r
+                                       this.unsubscribed(message.subscription, message);\r
+                                       if(deferred){\r
+                                               deferred.callback(true);\r
+                                       }\r
+                                       break;\r
+                       }\r
+               }\r
+               \r
+               // send the message down for processing by the transport\r
+               this.currentTransport.deliver(message);\r
+\r
+               if(message.data){\r
+                       // dispatch the message to any locally subscribed listeners\r
+                       try {\r
+                               var tname = "/cometd"+message.channel;\r
+                               dojo.publish(tname, [ message ]);\r
+                       }catch(e){\r
+                               console.debug(e);\r
+                       }\r
+               }\r
+       }\r
+\r
+       this._sendMessage = function(/* object */ message){\r
+               if(this.currentTransport && this._connected && this.batch==0){\r
+                       return this.currentTransport.sendMessages([message]);\r
+               }\r
+               else{\r
+                       this._messageQ.push(message);\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       this.startBatch = function(){\r
+               this.batch++;\r
+       }\r
+\r
+       this.endBatch = function(){\r
+               if(--this.batch <= 0 && this.currentTransport && this._connected){\r
+                       this.batch=0;\r
+\r
+                       var messages=this._messageQ;\r
+                       this._messageQ=[];\r
+                       if(messages.length>0){\r
+                               this.currentTransport.sendMessages(messages);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       this._onUnload = function(){\r
+               // make this the last of the onUnload method\r
+               dojo.addOnUnload(dojox.cometd,"disconnect");\r
+       }\r
+}\r
+\r
+/*\r
+transport objects MUST expose the following methods:\r
+       - check\r
+       - startup\r
+       - sendMessages\r
+       - deliver\r
+       - disconnect\r
+optional, standard but transport dependent methods are:\r
+       - tunnelCollapse\r
+       - tunnelInit\r
+\r
+Transports SHOULD be namespaced under the cometd object and transports MUST\r
+register themselves with cometd.connectionTypes\r
+\r
+here's a stub transport defintion:\r
+\r
+cometd.blahTransport = new function(){\r
+       this._connectionType="my-polling";\r
+       this._cometd=null;\r
+       this.lastTimestamp = null;\r
+\r
+       this.check = function(types, version, xdomain){\r
+               // summary:\r
+               //              determines whether or not this transport is suitable given a\r
+               //              list of transport types that the server supports\r
+               return dojo.lang.inArray(types, "blah");\r
+       }\r
+\r
+       this.startup = function(){\r
+               if(dojox.cometd._polling){ return; }\r
+               // FIXME: fill in startup routine here\r
+               dojox.cometd._polling = true;\r
+       }\r
+\r
+       this.sendMessages = function(message){\r
+               // FIXME: fill in message array sending logic\r
+       }\r
+\r
+       this.deliver = function(message){\r
+       }\r
+\r
+       this.disconnect = function(){\r
+       }\r
+}\r
+cometd.connectionTypes.register("blah", cometd.blahTransport.check, cometd.blahTransport);\r
+*/\r
+\r
+dojox.cometd.longPollTransport = new function(){\r
+       this._connectionType="long-polling";\r
+       this._cometd=null;\r
+\r
+       this.check = function(types, version, xdomain){\r
+               return ((!xdomain)&&(dojo.indexOf(types, "long-polling") >= 0));\r
+       }\r
+\r
+       this.tunnelInit = function(){\r
+               var message = {\r
+                       channel:        "/meta/connect",\r
+                       clientId:       this._cometd.clientId,\r
+                       connectionType: this._connectionType,\r
+                       id:     ""+this._cometd.messageId++\r
+               };\r
+               message=this._cometd._extendOut(message);\r
+               this.openTunnelWith({message: dojo.toJson([message])});\r
+       }\r
+\r
+       this.tunnelCollapse = function(){\r
+               // TODO handle transport specific advice\r
+               \r
+               if(!this._cometd._initialized){ return; }\r
+                       \r
+               if(this._cometd._advice){\r
+                       if(this._cometd._advice["reconnect"]=="none"){\r
+                               return;\r
+                       }\r
+                       if(     (this._cometd._advice["interval"])&&\r
+                               (this._cometd._advice.interval>0) ){\r
+                               setTimeout(dojo.hitch(this,function(){ this._connect(); }),this._cometd._advice.interval);\r
+                       }else{\r
+                               this._connect();\r
+                       }\r
+               }else{\r
+                       this._connect();\r
+               }\r
+       }\r
+\r
+       this._connect = function(){\r
+               if(!this._cometd._initialized){ return; }\r
+               if(this._cometd._polling) {\r
+                       console.debug("wait for poll to complete or fail");\r
+                       return;\r
+               }\r
+                       \r
+               if(     (this._cometd._advice) &&\r
+                       (this._cometd._advice["reconnect"]=="handshake")\r
+               ){\r
+                       this._cometd._connected=false;\r
+                       this._initialized = false;\r
+                       this._cometd.init(this._cometd.url,this._cometd._props);\r
+               }else if(this._cometd._connected){\r
+                       var message={\r
+                               channel:        "/meta/connect",\r
+                               connectionType: this._connectionType,\r
+                               clientId:       this._cometd.clientId,\r
+                               id:     ""+this._cometd.messageId++\r
+                       };\r
+                       message=this._cometd._extendOut(message);\r
+                       this.openTunnelWith({message: dojo.toJson([message])});\r
+               }\r
+       }\r
+\r
+       this.deliver = function(message){\r
+               // Nothing to do\r
+       }\r
+\r
+       this.openTunnelWith = function(content, url){\r
+               this._cometd._polling = true;\r
+               var d = dojo.xhrPost({\r
+                       url: (url||this._cometd.url),\r
+                       content: content,\r
+                       handleAs: this._cometd.handleAs,\r
+                       load: dojo.hitch(this, function(data){\r
+                               this._cometd._polling = false;\r
+                               this._cometd.deliver(data);\r
+                               this.tunnelCollapse();\r
+                       }),\r
+                       error: dojo.hitch(this, function(err){\r
+                               this._cometd._polling = false;\r
+                               console.debug("tunnel opening failed:", err);\r
+                               dojo.publish("/cometd/meta", [{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);\r
+                               this._cometd._backoff();\r
+                               this.tunnelCollapse();\r
+                       })\r
+               });\r
+       }\r
+\r
+       this.sendMessages = function(messages){\r
+               for(var i=0; i<messages.length; i++){\r
+                       messages[i].clientId = this._cometd.clientId;\r
+                       messages[i].id = ""+this._cometd.messageId++;\r
+                       messages[i]=this._cometd._extendOut(messages[i]);\r
+               }\r
+               return dojo.xhrPost({\r
+                       url: this._cometd.url||djConfig["cometdRoot"],\r
+                       handleAs: this._cometd.handleAs,\r
+                       load: dojo.hitch(this._cometd, "deliver"),\r
+                       content: {\r
+                               message: dojo.toJson(messages)\r
+                       }\r
+               });\r
+       }\r
+\r
+       this.startup = function(handshakeData){\r
+               if(this._cometd._connected){ return; }\r
+               this.tunnelInit();\r
+       }\r
+\r
+       this.disconnect = function(){\r
+               var message={\r
+                       channel:        "/meta/disconnect",\r
+                       clientId:       this._cometd.clientId,\r
+                       id:     ""+this._cometd.messageId++\r
+               };\r
+               message=this._cometd._extendOut(message);\r
+               dojo.xhrPost({\r
+                       url: this._cometd.url||djConfig["cometdRoot"],\r
+                       handleAs: this._cometd.handleAs,\r
+                       content: {\r
+                               message: dojo.toJson([message])\r
+                       }\r
+               });\r
+       }\r
+}\r
+\r
+dojox.cometd.callbackPollTransport = new function(){\r
+       this._connectionType = "callback-polling";\r
+       this._cometd = null;\r
+\r
+       this.check = function(types, version, xdomain){\r
+               // we handle x-domain!\r
+               return (dojo.indexOf(types, "callback-polling") >= 0);\r
+       }\r
+\r
+       this.tunnelInit = function(){\r
+               var message = {\r
+                       channel:        "/meta/connect",\r
+                       clientId:       this._cometd.clientId,\r
+                       connectionType: this._connectionType,\r
+                       id:     ""+this._cometd.messageId++\r
+               };\r
+               message = this._cometd._extendOut(message);             \r
+               this.openTunnelWith({\r
+                       message: dojo.toJson([message])\r
+               });\r
+       }\r
+\r
+       this.tunnelCollapse = dojox.cometd.longPollTransport.tunnelCollapse;\r
+       this._connect = dojox.cometd.longPollTransport._connect;\r
+       this.deliver = dojox.cometd.longPollTransport.deliver;\r
+\r
+       this.openTunnelWith = function(content, url){\r
+               this._cometd._polling = true;\r
+               dojo.io.script.get({\r
+                       load: dojo.hitch(this, function(data){\r
+                               this._cometd._polling = false;\r
+                               this._cometd.deliver(data);\r
+                               this.tunnelCollapse();\r
+                       }),\r
+                       error: dojo.hitch(this, function(err){\r
+                               this._cometd._polling = false;\r
+                               console.debug("tunnel opening failed:", err);\r
+                               dojo.publish("/cometd/meta", [{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);\r
+                               this._cometd._backoff();\r
+                               this.tunnelCollapse();\r
+                       }),\r
+                       url: (url||this._cometd.url),\r
+                       content: content,\r
+                       callbackParamName: "jsonp"\r
+               });\r
+       }\r
+\r
+       this.sendMessages = function(/*array*/ messages){\r
+               for(var i=0; i<messages.length; i++){\r
+                       messages[i].clientId = this._cometd.clientId;\r
+                       messages[i].id = ""+this._cometd.messageId++;\r
+                       messages[i]=this._cometd._extendOut(messages[i]);\r
+               }\r
+               var bindArgs = {\r
+                       url: this._cometd.url||djConfig["cometdRoot"],\r
+                       load: dojo.hitch(this._cometd, "deliver"),\r
+                       callbackParamName: "jsonp",\r
+                       content: { message: dojo.toJson( messages ) }\r
+               };\r
+               return dojo.io.script.get(bindArgs);\r
+       }\r
+\r
+       this.startup = function(handshakeData){\r
+               if(this._cometd._connected){ return; }\r
+               this.tunnelInit();\r
+       }\r
+\r
+       this.disconnect = dojox.cometd.longPollTransport.disconnect;\r
+       \r
+       this.disconnect = function(){\r
+               var message={\r
+                       channel:"/meta/disconnect",\r
+                       clientId:this._cometd.clientId,\r
+                       id:""+this._cometd.messageId++\r
+               };\r
+               message=this._cometd._extendOut(message);               \r
+               dojo.io.script.get({\r
+                       url: this._cometd.url||djConfig["cometdRoot"],\r
+                       callbackParamName: "jsonp",\r
+                       content: {\r
+                               message: dojo.toJson([message])\r
+                       }\r
+               });\r
+       }\r
+}\r
+dojox.cometd.connectionTypes.register("long-polling", dojox.cometd.longPollTransport.check, dojox.cometd.longPollTransport);\r
+dojox.cometd.connectionTypes.register("callback-polling", dojox.cometd.callbackPollTransport.check, dojox.cometd.callbackPollTransport);\r
+\r
+dojo.addOnUnload(dojox.cometd,"_onUnload");\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojox.cometd"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojox.cometd"] = true;\r
+// stub loader for the cometd module since no implementation code is allowed to live in top-level files\r
+dojo.provide("dojox.cometd");\r
+\r
+\r
+}\r
+\r
diff --git a/webapps/cometd/examples/dojo/dojox/cometd.xd.js b/webapps/cometd/examples/dojo/dojox/cometd.xd.js
new file mode 100644 (file)
index 0000000..f6d6453
--- /dev/null
@@ -0,0 +1,20 @@
+/*\r
+       Copyright (c) 2004-2007, The Dojo Foundation\r
+       All Rights Reserved.\r
+\r
+       Licensed under the Academic Free License version 2.1 or above OR the\r
+       modified BSD license. For more information on Dojo licensing, see:\r
+\r
+               http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing\r
+*/\r
+\r
+/*\r
+       This is a compiled version of Dojo, built for deployment and not for\r
+       development. To get an editable version, please visit:\r
+\r
+               http://dojotoolkit.org\r
+\r
+       for documentation and information on getting the source.\r
+*/\r
+\r
+dojo._xdResourceLoaded({depends:[["provide","dojo.AdapterRegistry"],["provide","dojo.io.script"],["provide","dojox.cometd._base"],["provide","dojox.cometd"]],defineResource:function(_1){if(!_1._hasResource["dojo.AdapterRegistry"]){_1._hasResource["dojo.AdapterRegistry"]=true;_1.provide("dojo.AdapterRegistry");_1.AdapterRegistry=function(_2){this.pairs=[];this.returnWrappers=_2||false;};_1.extend(_1.AdapterRegistry,{register:function(_3,_4,_5,_6,_7){this.pairs[((_7)?"unshift":"push")]([_3,_4,_5,_6]);},match:function(){for(var i=0;i<this.pairs.length;i++){var _9=this.pairs[i];if(_9[1].apply(this,arguments)){if((_9[3])||(this.returnWrappers)){return _9[2];}else{return _9[2].apply(this,arguments);}}}throw new Error("No match found");},unregister:function(_a){for(var i=0;i<this.pairs.length;i++){var _c=this.pairs[i];if(_c[0]==_a){this.pairs.splice(i,1);return true;}}return false;}});}if(!_1._hasResource["dojo.io.script"]){_1._hasResource["dojo.io.script"]=true;_1.provide("dojo.io.script");_1.io.script={get:function(_d){var _e=this._makeScriptDeferred(_d);var _f=_e.ioArgs;_1._ioAddQueryToUrl(_f);this.attach(_f.id,_f.url);_1._ioWatch(_e,this._validCheck,this._ioCheck,this._resHandle);return _e;},attach:function(id,url){var _12=_1.doc.createElement("script");_12.type="text/javascript";_12.src=url;_12.id=id;_1.doc.getElementsByTagName("head")[0].appendChild(_12);},remove:function(id){_1._destroyElement(_1.byId(id));if(this["jsonp_"+id]){delete this["jsonp_"+id];}},_makeScriptDeferred:function(_14){var dfd=_1._ioSetArgs(_14,this._deferredCancel,this._deferredOk,this._deferredError);var _16=dfd.ioArgs;_16.id="dojoIoScript"+(this._counter++);_16.canDelete=false;if(_14.callbackParamName){_16.query=_16.query||"";if(_16.query.length>0){_16.query+="&";}_16.query+=_14.callbackParamName+"=dojo.io.script.jsonp_"+_16.id+"._jsonpCallback";_16.canDelete=true;dfd._jsonpCallback=this._jsonpCallback;this["jsonp_"+_16.id]=dfd;}return dfd;},_deferredCancel:function(dfd){dfd.canceled=true;if(dfd.ioArgs.canDelete){_1.io.script._deadScripts.push(dfd.ioArgs.id);}},_deferredOk:function(dfd){if(dfd.ioArgs.canDelete){_1.io.script._deadScripts.push(dfd.ioArgs.id);}if(dfd.ioArgs.json){return dfd.ioArgs.json;}else{return dfd.ioArgs;}},_deferredError:function(_19,dfd){if(dfd.ioArgs.canDelete){if(_19.dojoType=="timeout"){_1.io.script.remove(dfd.ioArgs.id);}else{_1.io.script._deadScripts.push(dfd.ioArgs.id);}}console.debug("dojo.io.script error",_19);return _19;},_deadScripts:[],_counter:1,_validCheck:function(dfd){var _1c=_1.io.script;var _1d=_1c._deadScripts;if(_1d&&_1d.length>0){for(var i=0;i<_1d.length;i++){_1c.remove(_1d[i]);}_1.io.script._deadScripts=[];}return true;},_ioCheck:function(dfd){if(dfd.ioArgs.json){return true;}var _20=dfd.ioArgs.args.checkString;if(_20&&eval("typeof("+_20+") != 'undefined'")){return true;}return false;},_resHandle:function(dfd){if(_1.io.script._ioCheck(dfd)){dfd.callback(dfd);}else{dfd.errback(new Error("inconceivable dojo.io.script._resHandle error"));}},_jsonpCallback:function(_22){this.ioArgs.json=_22;}};}if(!_1._hasResource["dojox.cometd._base"]){_1._hasResource["dojox.cometd._base"]=true;_1.provide("dojox.cometd._base");dojox.cometd=new function(){this.DISCONNECTED="DISCONNECTED";this.CONNECTING="CONNECTING";this.CONNECTED="CONNECTED";this.DISCONNECTING="DISCONNECING";this._initialized=false;this._connected=false;this._polling=false;this.connectionTypes=new _1.AdapterRegistry(true);this.version="1.0";this.minimumVersion="0.9";this.clientId=null;this.messageId=0;this.batch=0;this._isXD=false;this.handshakeReturn=null;this.currentTransport=null;this.url=null;this.lastMessage=null;this._messageQ=[];this.handleAs="json-comment-optional";this._advice={};this._maxInterval=30000;this._backoffInterval=1000;this._deferredSubscribes={};this._deferredUnsubscribes={};this._subscriptions=[];this._extendInList=[];this._extendOutList=[];this.state=function(){return this._initialized?(this._connected?this.CONNECTED:this.CONNECTING):(this._connected?this.DISCONNECTING:this.DISCONNECTED);};this.init=function(_23,_24,_25){_24=_24||{};_24.version=this.version;_24.minimumVersion=this.minimumVersion;_24.channel="/meta/handshake";_24.id=""+this.messageId++;this.url=_23||djConfig["cometdRoot"];if(!this.url){console.debug("no cometd root specified in djConfig and no root passed");return null;}var _26="^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";var _27=(""+window.location).match(new RegExp(_26));if(_27[4]){var tmp=_27[4].split(":");var _29=tmp[0];var _2a=tmp[1]||"80";_27=this.url.match(new RegExp(_26));if(_27[4]){tmp=_27[4].split(":");var _2b=tmp[0];var _2c=tmp[1]||"80";this._isXD=((_2b!=_29)||(_2c!=_2a));}}if(!this._isXD){if(_24.ext){if(_24.ext["json-comment-filtered"]!==true&&_24.ext["json-comment-filtered"]!==false){_24.ext["json-comment-filtered"]=true;}}else{_24.ext={"json-comment-filtered":true};}}_24=this._extendOut(_24);var _2d={url:this.url,handleAs:this.handleAs,content:{"message":_1.toJson([_24])},load:_1.hitch(this,function(msg){this._finishInit(msg);}),error:_1.hitch(this,function(e){console.debug("handshake error!:",e);this._finishInit([{}]);})};if(_25){_1.mixin(_2d,_25);}this._props=_24;for(var _30 in this._subscriptions){for(var sub in this._subscriptions[_30]){if(this._subscriptions[_30][sub].topic){_1.unsubscribe(this._subscriptions[_30][sub].topic);}}}this._messageQ=[];this._subscriptions=[];this._initialized=true;this.batch=0;this.startBatch();var r;if(this._isXD){_2d.callbackParamName="jsonp";r=_1.io.script.get(_2d);}else{r=_1.xhrPost(_2d);}_1.publish("/cometd/meta",[{cometd:this,action:"handshake",successful:true,state:this.state()}]);return r;};this.publish=function(_33,_34,_35){var _36={data:_34,channel:_33};if(_35){_1.mixin(_36,_35);}this._sendMessage(_36);};this.subscribe=function(_37,_38,_39){if(_38){var _3a="/cometd"+_37;var _3b=this._subscriptions[_3a];if(!_3b||_3b.length==0){_3b=[];this._sendMessage({channel:"/meta/subscribe",subscription:_37});var _ds=this._deferredSubscribes;_ds[_37]=new _1.Deferred();if(_ds[_37]){_ds[_37].cancel();delete _ds[_37];}}for(var i in _3b){if(_3b[i].objOrFunc===_38&&(!_3b[i].funcName&&!_39||_3b[i].funcName==_39)){return null;}}var _3e=_1.subscribe(_3a,_38,_39);_3b.push({topic:_3e,objOrFunc:_38,funcName:_39});this._subscriptions[_3a]=_3b;}return this._deferredSubscribes[_37];};this.unsubscribe=function(_3f,_40,_41){var _42="/cometd"+_3f;var _43=this._subscriptions[_42];if(!_43||_43.length==0){return null;}var s=0;for(var i in _43){var sb=_43[i];if((!_40)||(sb.objOrFunc===_40&&(!sb.funcName&&!_41||sb.funcName==_41))){_1.unsubscribe(_43[i].topic);delete _43[i];}else{s++;}}if(s==0){delete this._subscriptions[_42];this._sendMessage({channel:"/meta/unsubscribe",subscription:_3f});this._deferredUnsubscribes[_3f]=new _1.Deferred();if(this._deferredSubscribes[_3f]){this._deferredSubscribes[_3f].cancel();delete this._deferredSubscribes[_3f];}}return this._deferredUnsubscribes[_3f];};this.disconnect=function(){for(var _47 in this._subscriptions){for(var sub in this._subscriptions[_47]){if(this._subscriptions[_47][sub].topic){_1.unsubscribe(this._subscriptions[_47][sub].topic);}}}this._subscriptions=[];this._messageQ=[];if(this._initialized&&this.currentTransport){this._initialized=false;this.currentTransport.disconnect();}if(!this._polling){this._connected=false;_1.publish("/cometd/meta",[{cometd:this,action:"connect",successful:false,state:this.state()}]);}this._initialized=false;_1.publish("/cometd/meta",[{cometd:this,action:"disconnect",successful:true,state:this.state()}]);};this.subscribed=function(_49,_4a){};this.unsubscribed=function(_4b,_4c){};this.tunnelInit=function(_4d,_4e){};this.tunnelCollapse=function(){};this._backoff=function(){if(!this._advice||!this._advice.interval){this._advice={reconnect:"retry",interval:0};}if(this._advice.interval<this._maxInterval){this._advice.interval+=this._backoffInterval;}};this._finishInit=function(_4f){_4f=_4f[0];this.handshakeReturn=_4f;if(_4f["advice"]){this._advice=_4f.advice;}var _50=_4f.successful?_4f.successful:false;if(_4f.version<this.minimumVersion){console.debug("cometd protocol version mismatch. We wanted",this.minimumVersion,"but got",_4f.version);_50=false;this._advice.reconnect="none";}if(_50){this.currentTransport=this.connectionTypes.match(_4f.supportedConnectionTypes,_4f.version,this._isXD);this.currentTransport._cometd=this;this.currentTransport.version=_4f.version;this.clientId=_4f.clientId;this.tunnelInit=_1.hitch(this.currentTransport,"tunnelInit");this.tunnelCollapse=_1.hitch(this.currentTransport,"tunnelCollapse");this.currentTransport.startup(_4f);}_1.publish("/cometd/meta",[{cometd:this,action:"handshook",successful:_50,state:this.state()}]);if(!_50){console.debug("cometd init failed");this._backoff();if(this._advice&&this._advice["reconnect"]=="none"){console.debug("cometd reconnect: none");}else{if(this._advice&&this._advice["interval"]&&this._advice.interval>0){setTimeout(_1.hitch(this,function(){this.init(cometd.url,this._props);}),this._advice.interval);}else{this.init(this.url,this._props);}}}};this._extendIn=function(_51){var m=_51;_1.forEach(dojox.cometd._extendInList,function(f){var n=f(m);if(n){m=n;}});return m;};this._extendOut=function(_55){var m=_55;_1.forEach(dojox.cometd._extendOutList,function(f){var n=f(m);if(n){m=n;}});return m;};this.deliver=function(_59){_1.forEach(_59,this._deliver,this);return _59;};this._deliver=function(_5a){_5a=this._extendIn(_5a);if(!_5a["channel"]){if(_5a["success"]!==true){console.debug("cometd error: no channel for message!",_5a);return;}}this.lastMessage=_5a;if(_5a.advice){this._advice=_5a.advice;}var _5b=null;if((_5a["channel"])&&(_5a.channel.length>5)&&(_5a.channel.substr(0,5)=="/meta")){switch(_5a.channel){case "/meta/connect":if(_5a.successful&&!this._connected){this._connected=this._initialized;this.endBatch();}else{if(!this._initialized){this._connected=false;}}_1.publish("/cometd/meta",[{cometd:this,action:"connect",successful:_5a.successful,state:this.state()}]);break;case "/meta/subscribe":_5b=this._deferredSubscribes[_5a.subscription];if(!_5a.successful){if(_5b){_5b.errback(new Error(_5a.error));}return;}dojox.cometd.subscribed(_5a.subscription,_5a);if(_5b){_5b.callback(true);}break;case "/meta/unsubscribe":_5b=this._deferredUnsubscribes[_5a.subscription];if(!_5a.successful){if(_5b){_5b.errback(new Error(_5a.error));}return;}this.unsubscribed(_5a.subscription,_5a);if(_5b){_5b.callback(true);}break;}}this.currentTransport.deliver(_5a);if(_5a.data){try{var _5c="/cometd"+_5a.channel;_1.publish(_5c,[_5a]);}catch(e){console.debug(e);}}};this._sendMessage=function(_5d){if(this.currentTransport&&this._connected&&this.batch==0){return this.currentTransport.sendMessages([_5d]);}else{this._messageQ.push(_5d);return null;}};this.startBatch=function(){this.batch++;};this.endBatch=function(){if(--this.batch<=0&&this.currentTransport&&this._connected){this.batch=0;var _5e=this._messageQ;this._messageQ=[];if(_5e.length>0){this.currentTransport.sendMessages(_5e);}}};this._onUnload=function(){_1.addOnUnload(dojox.cometd,"disconnect");};};dojox.cometd.longPollTransport=new function(){this._connectionType="long-polling";this._cometd=null;this.check=function(_5f,_60,_61){return ((!_61)&&(_1.indexOf(_5f,"long-polling")>=0));};this.tunnelInit=function(){var _62={channel:"/meta/connect",clientId:this._cometd.clientId,connectionType:this._connectionType,id:""+this._cometd.messageId++};_62=this._cometd._extendOut(_62);this.openTunnelWith({message:_1.toJson([_62])});};this.tunnelCollapse=function(){if(!this._cometd._initialized){return;}if(this._cometd._advice){if(this._cometd._advice["reconnect"]=="none"){return;}if((this._cometd._advice["interval"])&&(this._cometd._advice.interval>0)){setTimeout(_1.hitch(this,function(){this._connect();}),this._cometd._advice.interval);}else{this._connect();}}else{this._connect();}};this._connect=function(){if(!this._cometd._initialized){return;}if(this._cometd._polling){console.debug("wait for poll to complete or fail");return;}if((this._cometd._advice)&&(this._cometd._advice["reconnect"]=="handshake")){this._cometd._connected=false;this._initialized=false;this._cometd.init(this._cometd.url,this._cometd._props);}else{if(this._cometd._connected){var _63={channel:"/meta/connect",connectionType:this._connectionType,clientId:this._cometd.clientId,id:""+this._cometd.messageId++};_63=this._cometd._extendOut(_63);this.openTunnelWith({message:_1.toJson([_63])});}}};this.deliver=function(_64){};this.openTunnelWith=function(_65,url){this._cometd._polling=true;var d=_1.xhrPost({url:(url||this._cometd.url),content:_65,handleAs:this._cometd.handleAs,load:_1.hitch(this,function(_68){this._cometd._polling=false;this._cometd.deliver(_68);this.tunnelCollapse();}),error:_1.hitch(this,function(err){this._cometd._polling=false;console.debug("tunnel opening failed:",err);_1.publish("/cometd/meta",[{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);this._cometd._backoff();this.tunnelCollapse();})});};this.sendMessages=function(_6a){for(var i=0;i<_6a.length;i++){_6a[i].clientId=this._cometd.clientId;_6a[i].id=""+this._cometd.messageId++;_6a[i]=this._cometd._extendOut(_6a[i]);}return _1.xhrPost({url:this._cometd.url||djConfig["cometdRoot"],handleAs:this._cometd.handleAs,load:_1.hitch(this._cometd,"deliver"),content:{message:_1.toJson(_6a)}});};this.startup=function(_6c){if(this._cometd._connected){return;}this.tunnelInit();};this.disconnect=function(){var _6d={channel:"/meta/disconnect",clientId:this._cometd.clientId,id:""+this._cometd.messageId++};_6d=this._cometd._extendOut(_6d);_1.xhrPost({url:this._cometd.url||djConfig["cometdRoot"],handleAs:this._cometd.handleAs,content:{message:_1.toJson([_6d])}});};};dojox.cometd.callbackPollTransport=new function(){this._connectionType="callback-polling";this._cometd=null;this.check=function(_6e,_6f,_70){return (_1.indexOf(_6e,"callback-polling")>=0);};this.tunnelInit=function(){var _71={channel:"/meta/connect",clientId:this._cometd.clientId,connectionType:this._connectionType,id:""+this._cometd.messageId++};_71=this._cometd._extendOut(_71);this.openTunnelWith({message:_1.toJson([_71])});};this.tunnelCollapse=dojox.cometd.longPollTransport.tunnelCollapse;this._connect=dojox.cometd.longPollTransport._connect;this.deliver=dojox.cometd.longPollTransport.deliver;this.openTunnelWith=function(_72,url){this._cometd._polling=true;_1.io.script.get({load:_1.hitch(this,function(_74){this._cometd._polling=false;this._cometd.deliver(_74);this.tunnelCollapse();}),error:_1.hitch(this,function(err){this._cometd._polling=false;console.debug("tunnel opening failed:",err);_1.publish("/cometd/meta",[{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);this._cometd._backoff();this.tunnelCollapse();}),url:(url||this._cometd.url),content:_72,callbackParamName:"jsonp"});};this.sendMessages=function(_76){for(var i=0;i<_76.length;i++){_76[i].clientId=this._cometd.clientId;_76[i].id=""+this._cometd.messageId++;_76[i]=this._cometd._extendOut(_76[i]);}var _78={url:this._cometd.url||djConfig["cometdRoot"],load:_1.hitch(this._cometd,"deliver"),callbackParamName:"jsonp",content:{message:_1.toJson(_76)}};return _1.io.script.get(_78);};this.startup=function(_79){if(this._cometd._connected){return;}this.tunnelInit();};this.disconnect=dojox.cometd.longPollTransport.disconnect;this.disconnect=function(){var _7a={channel:"/meta/disconnect",clientId:this._cometd.clientId,id:""+this._cometd.messageId++};_7a=this._cometd._extendOut(_7a);_1.io.script.get({url:this._cometd.url||djConfig["cometdRoot"],callbackParamName:"jsonp",content:{message:_1.toJson([_7a])}});};};dojox.cometd.connectionTypes.register("long-polling",dojox.cometd.longPollTransport.check,dojox.cometd.longPollTransport);dojox.cometd.connectionTypes.register("callback-polling",dojox.cometd.callbackPollTransport.check,dojox.cometd.callbackPollTransport);_1.addOnUnload(dojox.cometd,"_onUnload");}if(!_1._hasResource["dojox.cometd"]){_1._hasResource["dojox.cometd"]=true;_1.provide("dojox.cometd");}}});\r
diff --git a/webapps/cometd/examples/dojo/dojox/cometd.xd.js.uncompressed.js b/webapps/cometd/examples/dojo/dojox/cometd.xd.js.uncompressed.js
new file mode 100644 (file)
index 0000000..8ec1c36
--- /dev/null
@@ -0,0 +1,1149 @@
+dojo._xdResourceLoaded({\r
+depends: [["provide", "dojo.AdapterRegistry"],\r
+["provide", "dojo.io.script"],\r
+["provide", "dojox.cometd._base"],\r
+["provide", "dojox.cometd"]],\r
+defineResource: function(dojo){/*\r
+       Copyright (c) 2004-2007, The Dojo Foundation\r
+       All Rights Reserved.\r
+\r
+       Licensed under the Academic Free License version 2.1 or above OR the\r
+       modified BSD license. For more information on Dojo licensing, see:\r
+\r
+               http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing\r
+*/\r
+\r
+/*\r
+       This is a compiled version of Dojo, built for deployment and not for\r
+       development. To get an editable version, please visit:\r
+\r
+               http://dojotoolkit.org\r
+\r
+       for documentation and information on getting the source.\r
+*/\r
+\r
+if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo.AdapterRegistry"] = true;\r
+dojo.provide("dojo.AdapterRegistry");\r
+\r
+dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){\r
+       //      summary:\r
+       //              A registry to make contextual calling/searching easier.\r
+       //      description:\r
+       //              Objects of this class keep list of arrays in the form [name, check,\r
+       //              wrap, directReturn] that are used to determine what the contextual\r
+       //              result of a set of checked arguments is. All check/wrap functions\r
+       //              in this registry should be of the same arity.\r
+       //      example:\r
+       //      |       // create a new registry\r
+       //      |       var reg = new dojo.AdapterRegistry();\r
+       //      |       reg.register("handleString",\r
+       //      |               dojo.isString,\r
+       //      |               function(str){\r
+       //      |                       // do something with the string here\r
+       //      |               }\r
+       //      |       );\r
+       //      |       reg.register("handleArr",\r
+       //      |               dojo.isArray,\r
+       //      |               function(arr){\r
+       //      |                       // do something with the array here\r
+       //      |               }\r
+       //      |       );\r
+       //      |\r
+       //      |       // now we can pass reg.match() *either* an array or a string and\r
+       //      |       // the value we pass will get handled by the right function\r
+       //      |       reg.match("someValue"); // will call the first function\r
+       //      |       reg.match(["someValue"]); // will call the second\r
+\r
+       this.pairs = [];\r
+       this.returnWrappers = returnWrappers || false; // Boolean\r
+}\r
+\r
+dojo.extend(dojo.AdapterRegistry, {\r
+       register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){\r
+               //      summary: \r
+               //              register a check function to determine if the wrap function or\r
+               //              object gets selected\r
+               //      name:\r
+               //              a way to identify this matcher.\r
+               //      check:\r
+               //              a function that arguments are passed to from the adapter's\r
+               //              match() function.  The check function should return true if the\r
+               //              given arguments are appropriate for the wrap function.\r
+               //      directReturn:\r
+               //              If directReturn is true, the value passed in for wrap will be\r
+               //              returned instead of being called. Alternately, the\r
+               //              AdapterRegistry can be set globally to "return not call" using\r
+               //              the returnWrappers property. Either way, this behavior allows\r
+               //              the registry to act as a "search" function instead of a\r
+               //              function interception library.\r
+               //      override:\r
+               //              If override is given and true, the check function will be given\r
+               //              highest priority. Otherwise, it will be the lowest priority\r
+               //              adapter.\r
+               this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]);\r
+       },\r
+\r
+       match: function(/* ... */){\r
+               // summary:\r
+               //              Find an adapter for the given arguments. If no suitable adapter\r
+               //              is found, throws an exception. match() accepts any number of\r
+               //              arguments, all of which are passed to all matching functions\r
+               //              from the registered pairs.\r
+               for(var i = 0; i < this.pairs.length; i++){\r
+                       var pair = this.pairs[i];\r
+                       if(pair[1].apply(this, arguments)){\r
+                               if((pair[3])||(this.returnWrappers)){\r
+                                       return pair[2];\r
+                               }else{\r
+                                       return pair[2].apply(this, arguments);\r
+                               }\r
+                       }\r
+               }\r
+               throw new Error("No match found");\r
+       },\r
+\r
+       unregister: function(name){\r
+               // summary: Remove a named adapter from the registry\r
+\r
+               // FIXME: this is kind of a dumb way to handle this. On a large\r
+               // registry this will be slow-ish and we can use the name as a lookup\r
+               // should we choose to trade memory for speed.\r
+               for(var i = 0; i < this.pairs.length; i++){\r
+                       var pair = this.pairs[i];\r
+                       if(pair[0] == name){\r
+                               this.pairs.splice(i, 1);\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+});\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojo.io.script"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojo.io.script"] = true;\r
+dojo.provide("dojo.io.script");\r
+\r
+/*=====\r
+dojo.io.script.__ioArgs = function(kwArgs){\r
+       //      summary:\r
+       //              All the properties described in the dojo.__ioArgs type, apply to this\r
+       //              type as well, EXCEPT "handleAs". It is not applicable to\r
+       //              dojo.io.script.get() calls, since it is implied by the usage of\r
+       //              "callbackParamName" (response will be a JSONP call returning JSON)\r
+       //              or "checkString" (response is pure JavaScript defined in\r
+       //              the body of the script that was attached). The following additional\r
+       //              properties are allowed for dojo.io.script.get():\r
+       //      callbackParamName: String\r
+       //              The URL parameter name that indicates the JSONP callback string.\r
+       //              For instance, when using Yahoo JSONP calls it is normally, \r
+       //              callbackParamName: "callback". For AOL JSONP calls it is normally \r
+       //              callbackParamName: "c".\r
+       //      checkString: String\r
+       //              A string of JavaScript that when evaluated like so: \r
+       //              "typeof(" + checkString + ") != 'undefined'"\r
+       //              being true means that the script fetched has been loaded. \r
+       //              Do not use this if doing a JSONP type of call (use callbackParamName instead).\r
+}\r
+=====*/\r
+\r
+dojo.io.script = {\r
+       get: function(/*dojo.io.script.__ioArgs*/args){\r
+               //      summary:\r
+               //              sends a get request using a dynamically created script tag.\r
+               var dfd = this._makeScriptDeferred(args);\r
+               var ioArgs = dfd.ioArgs;\r
+               dojo._ioAddQueryToUrl(ioArgs);\r
+\r
+               this.attach(ioArgs.id, ioArgs.url);\r
+               dojo._ioWatch(dfd, this._validCheck, this._ioCheck, this._resHandle);\r
+               return dfd;\r
+       },\r
+\r
+       attach: function(/*String*/id, /*String*/url){\r
+               //      summary:\r
+               //              creates a new <script> tag pointing to the specified URL and\r
+               //              adds it to the document.\r
+               //      description:\r
+               //              Attaches the script element to the DOM.  Use this method if you\r
+               //              just want to attach a script to the DOM and do not care when or\r
+               //              if it loads.\r
+               var element = dojo.doc.createElement("script");\r
+               element.type = "text/javascript";\r
+               element.src = url;\r
+               element.id = id;\r
+               dojo.doc.getElementsByTagName("head")[0].appendChild(element);\r
+       },\r
+\r
+       remove: function(/*String*/id){\r
+               //summary: removes the script element with the given id.\r
+               dojo._destroyElement(dojo.byId(id));\r
+               \r
+               //Remove the jsonp callback on dojo.io.script, if it exists.\r
+               if(this["jsonp_" + id]){\r
+                       delete this["jsonp_" + id];\r
+               }\r
+       },\r
+\r
+       _makeScriptDeferred: function(/*Object*/args){\r
+               //summary: \r
+               //              sets up a Deferred object for an IO request.\r
+               var dfd = dojo._ioSetArgs(args, this._deferredCancel, this._deferredOk, this._deferredError);\r
+\r
+               var ioArgs = dfd.ioArgs;\r
+               ioArgs.id = "dojoIoScript" + (this._counter++);\r
+               ioArgs.canDelete = false;\r
+\r
+               //Special setup for jsonp case\r
+               if(args.callbackParamName){\r
+                       //Add the jsonp parameter.\r
+                       ioArgs.query = ioArgs.query || "";\r
+                       if(ioArgs.query.length > 0){\r
+                               ioArgs.query += "&";\r
+                       }\r
+                       ioArgs.query += args.callbackParamName + "=dojo.io.script.jsonp_" + ioArgs.id + "._jsonpCallback";\r
+\r
+                       //Setup the Deferred to have the jsonp callback.\r
+                       ioArgs.canDelete = true;\r
+                       dfd._jsonpCallback = this._jsonpCallback;\r
+                       this["jsonp_" + ioArgs.id] = dfd;\r
+               }\r
+               return dfd; // dojo.Deferred\r
+       },\r
+       \r
+       _deferredCancel: function(/*Deferred*/dfd){\r
+               //summary: canceller function for dojo._ioSetArgs call.\r
+\r
+               //DO NOT use "this" and expect it to be dojo.io.script.\r
+               dfd.canceled = true;\r
+               if(dfd.ioArgs.canDelete){\r
+                       dojo.io.script._deadScripts.push(dfd.ioArgs.id);\r
+               }\r
+       },\r
+\r
+       _deferredOk: function(/*Deferred*/dfd){\r
+               //summary: okHandler function for dojo._ioSetArgs call.\r
+\r
+               //DO NOT use "this" and expect it to be dojo.io.script.\r
+\r
+               //Add script to list of things that can be removed.             \r
+               if(dfd.ioArgs.canDelete){\r
+                       dojo.io.script._deadScripts.push(dfd.ioArgs.id);\r
+               }\r
+\r
+               if(dfd.ioArgs.json){\r
+                       //Make sure to *not* remove the json property from the\r
+                       //Deferred, so that the Deferred can still function correctly\r
+                       //after the response is received.\r
+                       return dfd.ioArgs.json;\r
+               }else{\r
+                       //FIXME: cannot return the dfd here, otherwise that stops\r
+                       //the callback chain in Deferred. So return the ioArgs instead.\r
+                       //This doesn't feel right.\r
+                       return dfd.ioArgs;\r
+               }\r
+       },\r
+       \r
+       _deferredError: function(/*Error*/error, /*Deferred*/dfd){\r
+               //summary: errHandler function for dojo._ioSetArgs call.\r
+\r
+               if(dfd.ioArgs.canDelete){\r
+                       //DO NOT use "this" and expect it to be dojo.io.script.\r
+                       if(error.dojoType == "timeout"){\r
+                               //For timeouts, remove the script element immediately to\r
+                               //avoid a response from it coming back later and causing trouble.\r
+                               dojo.io.script.remove(dfd.ioArgs.id);\r
+                       }else{\r
+                               dojo.io.script._deadScripts.push(dfd.ioArgs.id);\r
+                       }\r
+               }\r
+               console.debug("dojo.io.script error", error);\r
+               return error;\r
+       },\r
+\r
+       _deadScripts: [],\r
+       _counter: 1,\r
+\r
+       _validCheck: function(/*Deferred*/dfd){\r
+               //summary: inflight check function to see if dfd is still valid.\r
+\r
+               //Do script cleanup here. We wait for one inflight pass\r
+               //to make sure we don't get any weird things by trying to remove a script\r
+               //tag that is part of the call chain (IE 6 has been known to\r
+               //crash in that case).\r
+               var _self = dojo.io.script;\r
+               var deadScripts = _self._deadScripts;\r
+               if(deadScripts && deadScripts.length > 0){\r
+                       for(var i = 0; i < deadScripts.length; i++){\r
+                               //Remove the script tag\r
+                               _self.remove(deadScripts[i]);\r
+                       }\r
+                       dojo.io.script._deadScripts = [];\r
+               }\r
+\r
+               return true;\r
+       },\r
+\r
+       _ioCheck: function(/*Deferred*/dfd){\r
+               //summary: inflight check function to see if IO finished.\r
+\r
+               //Check for finished jsonp\r
+               if(dfd.ioArgs.json){\r
+                       return true;\r
+               }\r
+\r
+               //Check for finished "checkString" case.\r
+               var checkString = dfd.ioArgs.args.checkString;\r
+               if(checkString && eval("typeof(" + checkString + ") != 'undefined'")){\r
+                       return true;\r
+               }\r
+\r
+               return false;\r
+       },\r
+\r
+       _resHandle: function(/*Deferred*/dfd){\r
+               //summary: inflight function to handle a completed response.\r
+               if(dojo.io.script._ioCheck(dfd)){\r
+                       dfd.callback(dfd);\r
+               }else{\r
+                       //This path should never happen since the only way we can get\r
+                       //to _resHandle is if _ioCheck is true.\r
+                       dfd.errback(new Error("inconceivable dojo.io.script._resHandle error"));\r
+               }\r
+       },\r
+\r
+       _jsonpCallback: function(/*JSON Object*/json){\r
+               //summary: \r
+               //              generic handler for jsonp callback. A pointer to this function\r
+               //              is used for all jsonp callbacks.  NOTE: the "this" in this\r
+               //              function will be the Deferred object that represents the script\r
+               //              request.\r
+               this.ioArgs.json = json;\r
+       }\r
+}\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojox.cometd._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojox.cometd._base"] = true;\r
+dojo.provide("dojox.cometd._base");\r
+\r
+\r
+\r
+\r
+/*\r
+ * this file defines Comet protocol client. Actual message transport is\r
+ * deferred to one of several connection type implementations. The default is a\r
+ * long-polling implementation. A single global object named "dojox.cometd" is\r
+ * used to mediate for these connection types in order to provide a stable\r
+ * interface.\r
+ *\r
+ * extensions modules may be loaded (eg "dojox.cometd.timestamp", that use\r
+ * the cometd._extendInList and cometd._extendOutList fields to provide functions\r
+ * that extend and handling incoming and outgoing messages.\r
+ */\r
+\r
+dojox.cometd = new function(){\r
+       \r
+       // cometd states:\r
+       this.DISCONNECTED="DISCONNECTED";       // _initialized==false  && _connected==false\r
+       this.CONNECTING="CONNECTING";           // _initialized==true   && _connected==false (handshake sent)\r
+       this.CONNECTED="CONNECTED";             // _initialized==true   && _connected==true (first successful connect)\r
+       this.DISCONNECTING="DISCONNECING";      // _initialized==false  && _connected==true (disconnect sent)\r
+       \r
+       this._initialized = false;\r
+       this._connected = false;\r
+       this._polling = false;\r
+\r
+       this.connectionTypes = new dojo.AdapterRegistry(true);\r
+\r
+       this.version="1.0";\r
+       this.minimumVersion="0.9";\r
+       this.clientId=null;\r
+       this.messageId=0;\r
+       this.batch=0;\r
+\r
+       this._isXD = false;\r
+       this.handshakeReturn=null;\r
+       this.currentTransport=null;\r
+       this.url = null;\r
+       this.lastMessage=null;\r
+       this._messageQ=[];\r
+       this.handleAs="json-comment-optional";\r
+       this._advice={};\r
+       this._maxInterval=30000;\r
+       this._backoffInterval=1000;\r
+       this._deferredSubscribes={};\r
+       this._deferredUnsubscribes={};\r
+       this._subscriptions=[];\r
+       this._extendInList=[];  // List of functions invoked before delivering messages\r
+       this._extendOutList=[]; // List of functions invoked before sending messages\r
+\r
+       this.state = function() {\r
+               return this._initialized?(this._connected?this.CONNECTED:this.CONNECTING):(this._connected?this.DISCONNECTING:this.DISCONNECTED);\r
+       }\r
+\r
+       this.init = function(   /*String*/      root,\r
+                               /*Object|null */ props,\r
+                               /*Object|null */ bargs){        // return: dojo.Deferred\r
+               //      summary:\r
+               //              Initialize the cometd implementation of the Bayeux protocol\r
+               //      description:\r
+               //              Initialize the cometd implementation of the Bayeux protocol by\r
+               //              sending a handshake message. The cometd state will be changed to CONNECTING\r
+               //              until a handshake response is received and the first successful connect message\r
+               //              has returned.\r
+               //              The protocol state changes may be monitored\r
+               //              by subscribing to the dojo topic "/cometd/meta" where events are\r
+               //              published in the form {cometd:this,action:"handshake",successful:true,state:this.state()}\r
+               //      root:\r
+               //              The URL of the cometd server. If the root is absolute, the host\r
+               //              is examined to determine if xd transport is needed. Otherwise the\r
+               //              same domain is assumed.\r
+               //      props:\r
+               //              An optional object that is used as the basis of the handshake message\r
+               //      bargs:\r
+               //              An optional object of bind args mixed in with the send of the handshake\r
+               //      example:\r
+               //      |       dojox.cometd.init("/cometd");\r
+               //      |       dojox.cometd.init("http://xdHost/cometd",{ext:{user:"fred",pwd:"secret"}});\r
+\r
+\r
+               // FIXME: if the root isn't from the same host, we should automatically\r
+               // try to select an XD-capable transport\r
+               props = props||{};\r
+               // go ask the short bus server what we can support\r
+               props.version = this.version;\r
+               props.minimumVersion = this.minimumVersion;\r
+               props.channel = "/meta/handshake";\r
+               props.id = ""+this.messageId++;\r
+\r
+               this.url = root||djConfig["cometdRoot"];\r
+               if(!this.url){\r
+                       console.debug("no cometd root specified in djConfig and no root passed");\r
+                       return null;\r
+               }\r
+\r
+               // Are we x-domain? borrowed from dojo.uri.Uri in lieu of fixed host and port properties\r
+               var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";\r
+               var parts = (""+window.location).match(new RegExp(regexp));\r
+               if(parts[4]){\r
+                       var tmp = parts[4].split(":");\r
+                       var thisHost = tmp[0];\r
+                       var thisPort = tmp[1]||"80"; // FIXME: match 443\r
+\r
+                       parts = this.url.match(new RegExp(regexp));\r
+                       if(parts[4]){\r
+                               tmp = parts[4].split(":");\r
+                               var urlHost = tmp[0];\r
+                               var urlPort = tmp[1]||"80";\r
+                               this._isXD = ((urlHost != thisHost)||(urlPort != thisPort));\r
+                       }\r
+               }\r
+\r
+               if(!this._isXD){\r
+                       if(props.ext){\r
+                               if(props.ext["json-comment-filtered"]!==true && props.ext["json-comment-filtered"]!==false){\r
+                                       props.ext["json-comment-filtered"] = true;\r
+                               }\r
+                       }else{\r
+                               props.ext = { "json-comment-filtered": true };\r
+                       }\r
+               }\r
+\r
+               props=this._extendOut(props);\r
+\r
+               var bindArgs = {\r
+                       url: this.url,\r
+                       handleAs: this.handleAs,\r
+                       content: { "message": dojo.toJson([props]) },\r
+                       load: dojo.hitch(this,function(msg){\r
+                               this._finishInit(msg);\r
+                       }),\r
+                       error: dojo.hitch(this,function(e){\r
+                               console.debug("handshake error!:",e);\r
+                               this._finishInit([{}]);\r
+                       })\r
+               };\r
+\r
+               if(bargs){\r
+                       dojo.mixin(bindArgs, bargs);\r
+               }\r
+               this._props=props;\r
+               for(var tname in this._subscriptions){\r
+                       for(var sub in this._subscriptions[tname]){\r
+                               if(this._subscriptions[tname][sub].topic){\r
+                                       dojo.unsubscribe(this._subscriptions[tname][sub].topic);\r
+                               }\r
+                       }\r
+               }\r
+               this._messageQ = [];\r
+               this._subscriptions = [];\r
+               this._initialized=true;\r
+               this.batch=0;\r
+               this.startBatch();\r
+               \r
+               var r;\r
+               // if xdomain, then we assume jsonp for handshake\r
+               if(this._isXD){\r
+                       bindArgs.callbackParamName="jsonp";\r
+                       r= dojo.io.script.get(bindArgs);\r
+               } else\r
+                       r = dojo.xhrPost(bindArgs);\r
+               dojo.publish("/cometd/meta", [{cometd:this,action:"handshake",successful:true,state:this.state()}]);\r
+               return r;\r
+       }\r
+       \r
+       \r
+       this.publish = function(/*String*/channel, /*Object */data, /*Object|null */properties){\r
+               // summary:\r
+               //              publishes the passed message to the cometd server for delivery\r
+               //              on the specified topic\r
+               // channel:\r
+               //              the destination channel for the message\r
+               // data:\r
+               //              a JSON object containing the message "payload"\r
+               // properties:\r
+               //              Optional. Other meta-data to be mixed into the top-level of the\r
+               //              message\r
+               var message = {\r
+                       data: data,\r
+                       channel: channel\r
+               };\r
+               if(properties){\r
+                       dojo.mixin(message, properties);\r
+               }\r
+               this._sendMessage(message);\r
+       }\r
+\r
+       \r
+       this.subscribe = function(      /*String */     channel,\r
+                                       /*Object */     objOrFunc,\r
+                                       /*String */     funcName){ // return: dojo.Deferred\r
+               // summary:\r
+               //              inform the server of this client's interest in channel\r
+               // channel:\r
+               //              name of the cometd channel to subscribe to\r
+               // objOrFunc:\r
+               //              an object scope for funcName or the name or reference to a\r
+               //              function to be called when messages are delivered to the\r
+               //              channel\r
+               // funcName:\r
+               //              the second half of the objOrFunc/funcName pair for identifying\r
+               //              a callback function to notifiy upon channel message delivery\r
+\r
+               if(objOrFunc){\r
+                       var tname = "/cometd"+channel;\r
+                       var subs=this._subscriptions[tname];\r
+                       if(!subs || subs.length==0){\r
+                               subs=[];\r
+                               this._sendMessage({\r
+                                       channel: "/meta/subscribe",\r
+                                       subscription: channel\r
+                               });\r
+                               \r
+                               var _ds = this._deferredSubscribes;\r
+                               _ds[channel] = new dojo.Deferred();\r
+                               if(_ds[channel]){\r
+                                       _ds[channel].cancel();\r
+                                       delete _ds[channel];\r
+                               }\r
+                       }\r
+                       \r
+                       for (var i in subs){\r
+                               if (subs[i].objOrFunc===objOrFunc&&(!subs[i].funcName&&!funcName||subs[i].funcName==funcName))\r
+                                       return null;\r
+                       }\r
+                       \r
+                       var topic = dojo.subscribe(tname, objOrFunc, funcName);\r
+                       subs.push({ \r
+                               topic: topic, \r
+                               objOrFunc: objOrFunc, \r
+                               funcName: funcName\r
+                       });\r
+                       this._subscriptions[tname] =subs;\r
+               }\r
+               return this._deferredSubscribes[channel];\r
+       }\r
+\r
+\r
+\r
+       this.unsubscribe = function(    /*string*/      channel,\r
+                                       /*object|null*/ objOrFunc,\r
+                                       /*string|null*/ funcName){\r
+               // summary:\r
+               //              inform the server of this client's disinterest in channel\r
+               // channel:\r
+               //              name of the cometd channel to unsubscribe from\r
+               // objOrFunc:\r
+               //              an object scope for funcName or the name or reference to a\r
+               //              function to be called when messages are delivered to the\r
+               //              channel. If null then all subscribers to the channel are unsubscribed.\r
+               // funcName:\r
+               //              the second half of the objOrFunc/funcName pair for identifying\r
+               //              a callback function to notifiy upon channel message delivery\r
+               \r
+               var tname = "/cometd"+channel;\r
+               var subs=this._subscriptions[tname];\r
+               if(!subs || subs.length==0){\r
+                       return null;\r
+               }\r
+\r
+               var s=0;\r
+               for(var i in subs){\r
+                       var sb=subs[i];\r
+                       if( (!objOrFunc) ||\r
+                               (\r
+                                       sb.objOrFunc===objOrFunc &&\r
+                                       (!sb.funcName && !funcName || sb.funcName==funcName)\r
+                               )\r
+                       ){\r
+                               dojo.unsubscribe(subs[i].topic);\r
+                               delete subs[i];\r
+                       }else{\r
+                               s++;\r
+                       }\r
+               }\r
+               \r
+               if(s==0){\r
+                       delete this._subscriptions[tname];\r
+                       this._sendMessage({\r
+                               channel: "/meta/unsubscribe",\r
+                               subscription: channel\r
+                       });\r
+                       this._deferredUnsubscribes[channel] = new dojo.Deferred();\r
+                       if (this._deferredSubscribes[channel]){\r
+                               this._deferredSubscribes[channel].cancel();\r
+                               delete this._deferredSubscribes[channel];\r
+                       }\r
+               }\r
+               return this._deferredUnsubscribes[channel];\r
+       }\r
+       \r
+       \r
+       this.disconnect = function(){\r
+               //      summary:\r
+               //              Disconnect from the server.\r
+               //      description:\r
+               //              Disconnect from the server by sending a disconnect message\r
+               //      example:\r
+               //      |       dojox.cometd.disconnect();\r
+\r
+               for(var tname in this._subscriptions){\r
+                       for(var sub in this._subscriptions[tname]){\r
+                               if(this._subscriptions[tname][sub].topic){\r
+                                       dojo.unsubscribe(this._subscriptions[tname][sub].topic);\r
+                               }\r
+                       }\r
+               }\r
+               this._subscriptions = [];\r
+               this._messageQ = [];\r
+               if(this._initialized && this.currentTransport){\r
+                       this._initialized=false;\r
+                       this.currentTransport.disconnect();\r
+               }\r
+               if(!this._polling) {\r
+                       this._connected=false;\r
+                       dojo.publish("/cometd/meta", [{cometd:this,action:"connect",successful:false,state:this.state()}]);\r
+               }\r
+               this._initialized=false;\r
+               dojo.publish("/cometd/meta", [{cometd:this,action:"disconnect",successful:true,state:this.state()}]);\r
+       }\r
+\r
+       \r
+       // public extension points\r
+       \r
+       this.subscribed = function(     /*String*/channel, /*Object*/message){ }\r
+\r
+       this.unsubscribed = function(/*String*/channel, /*Object*/message){ }\r
+\r
+\r
+       // private methods (TODO name all with leading _)\r
+\r
+       this.tunnelInit = function(childLocation, childDomain){\r
+               // placeholder - replaced by _finishInit\r
+       }\r
+       \r
+       this.tunnelCollapse = function(){\r
+               // placeholder - replaced by _finishInit\r
+       }\r
+       \r
+       this._backoff = function(){\r
+               if(!this._advice || !this._advice.interval){\r
+                       this._advice={reconnect:"retry",interval:0}; // TODO Is this good advice?\r
+               }\r
+               if(this._advice.interval<this._maxInterval){\r
+                       this._advice.interval+=this._backoffInterval;\r
+               }\r
+       }\r
+\r
+       this._finishInit = function(data){\r
+               //      summary:\r
+               //              Handle the handshake return from the server and initialize\r
+               //              connection if all is OK\r
+               data = data[0];\r
+               this.handshakeReturn = data;\r
+               \r
+               // remember any advice\r
+               if(data["advice"]){\r
+                       this._advice = data.advice;\r
+               }\r
+\r
+               var successful=data.successful?data.successful:false;\r
+               \r
+               // check version\r
+               if(data.version < this.minimumVersion){\r
+                       console.debug("cometd protocol version mismatch. We wanted", this.minimumVersion, "but got", data.version);\r
+                       successful=false;\r
+                       this._advice.reconnect="none";\r
+               }\r
+               \r
+               // If all OK\r
+               if(successful){\r
+                       // pick a transport\r
+                       this.currentTransport = this.connectionTypes.match(\r
+                               data.supportedConnectionTypes,\r
+                               data.version,\r
+                               this._isXD\r
+                       );\r
+                       // initialize the transport\r
+                       this.currentTransport._cometd = this;\r
+                       this.currentTransport.version = data.version;\r
+                       this.clientId = data.clientId;\r
+                       this.tunnelInit = dojo.hitch(this.currentTransport, "tunnelInit");\r
+                       this.tunnelCollapse = dojo.hitch(this.currentTransport, "tunnelCollapse");\r
+                       this.currentTransport.startup(data);\r
+               }\r
+\r
+               dojo.publish("/cometd/meta", [{cometd:this,action:"handshook",successful:successful,state:this.state()}]);\r
+\r
+               // If there is a problem\r
+               if(!successful){\r
+                       console.debug("cometd init failed");\r
+                       this._backoff();\r
+                       // follow advice\r
+                       if(this._advice && this._advice["reconnect"]=="none"){\r
+                               console.debug("cometd reconnect: none");\r
+                       }else if(this._advice && this._advice["interval"] && this._advice.interval>0 ){\r
+                               setTimeout(\r
+                                       dojo.hitch(this, function(){ this.init(cometd.url,this._props); }),\r
+                                       this._advice.interval\r
+                               );\r
+                       }else{\r
+                               this.init(this.url,this._props);\r
+                       }\r
+               }\r
+       }\r
+\r
+       this._extendIn = function(message){\r
+               // Handle extensions for inbound messages\r
+               var m=message;\r
+               dojo.forEach(dojox.cometd._extendInList, function(f){\r
+                       var n=f(m);\r
+                       if(n)m=n;\r
+               });\r
+               return m;\r
+       }\r
+\r
+       this._extendOut= function(message){\r
+               // Handle extensions for inbound messages\r
+               var m=message;\r
+               dojo.forEach(dojox.cometd._extendOutList, function(f){\r
+                       var n=f(m);\r
+                       if(n)m=n;\r
+               });\r
+               return m;\r
+       }\r
+\r
+\r
+       this.deliver = function(messages){\r
+               dojo.forEach(messages, this._deliver, this);\r
+               return messages;\r
+       }\r
+\r
+       this._deliver = function(message){\r
+               // dipatch events along the specified path\r
+               \r
+               message=this._extendIn(message);\r
+\r
+               if(!message["channel"]){\r
+                       if(message["success"] !== true){\r
+                               console.debug("cometd error: no channel for message!", message);\r
+                               return;\r
+                       }\r
+               }\r
+               this.lastMessage = message;\r
+\r
+               if(message.advice){\r
+                       this._advice = message.advice; // TODO maybe merge?\r
+               }\r
+\r
+               // check to see if we got a /meta channel message that we care about\r
+               var deferred=null;\r
+               if(     (message["channel"]) &&\r
+                       (message.channel.length > 5)&&\r
+                       (message.channel.substr(0, 5) == "/meta")){\r
+                       // check for various meta topic actions that we need to respond to\r
+                       switch(message.channel){\r
+                               case "/meta/connect":\r
+                                       if(message.successful && !this._connected){\r
+                                               this._connected = this._initialized;\r
+                                               this.endBatch();\r
+                                       } else if(!this._initialized){\r
+                                               this._connected = false; // finish disconnect\r
+                                       }\r
+                                       dojo.publish("/cometd/meta",[{cometd:this,action:"connect",successful:message.successful,state:this.state()}]);\r
+                                       break;\r
+                               case "/meta/subscribe":\r
+                                       deferred = this._deferredSubscribes[message.subscription];\r
+                                       if(!message.successful){\r
+                                               if(deferred){\r
+                                                       deferred.errback(new Error(message.error));\r
+                                               }\r
+                                               return;\r
+                                       }\r
+                                       dojox.cometd.subscribed(message.subscription, message);\r
+                                       if(deferred){\r
+                                               deferred.callback(true);\r
+                                       }\r
+                                       break;\r
+                               case "/meta/unsubscribe":\r
+                                       deferred = this._deferredUnsubscribes[message.subscription];\r
+                                       if(!message.successful){\r
+                                               if(deferred){\r
+                                                       deferred.errback(new Error(message.error));\r
+                                               }\r
+                                               return;\r
+                                       }\r
+                                       this.unsubscribed(message.subscription, message);\r
+                                       if(deferred){\r
+                                               deferred.callback(true);\r
+                                       }\r
+                                       break;\r
+                       }\r
+               }\r
+               \r
+               // send the message down for processing by the transport\r
+               this.currentTransport.deliver(message);\r
+\r
+               if(message.data){\r
+                       // dispatch the message to any locally subscribed listeners\r
+                       try {\r
+                               var tname = "/cometd"+message.channel;\r
+                               dojo.publish(tname, [ message ]);\r
+                       }catch(e){\r
+                               console.debug(e);\r
+                       }\r
+               }\r
+       }\r
+\r
+       this._sendMessage = function(/* object */ message){\r
+               if(this.currentTransport && this._connected && this.batch==0){\r
+                       return this.currentTransport.sendMessages([message]);\r
+               }\r
+               else{\r
+                       this._messageQ.push(message);\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       this.startBatch = function(){\r
+               this.batch++;\r
+       }\r
+\r
+       this.endBatch = function(){\r
+               if(--this.batch <= 0 && this.currentTransport && this._connected){\r
+                       this.batch=0;\r
+\r
+                       var messages=this._messageQ;\r
+                       this._messageQ=[];\r
+                       if(messages.length>0){\r
+                               this.currentTransport.sendMessages(messages);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       this._onUnload = function(){\r
+               // make this the last of the onUnload method\r
+               dojo.addOnUnload(dojox.cometd,"disconnect");\r
+       }\r
+}\r
+\r
+/*\r
+transport objects MUST expose the following methods:\r
+       - check\r
+       - startup\r
+       - sendMessages\r
+       - deliver\r
+       - disconnect\r
+optional, standard but transport dependent methods are:\r
+       - tunnelCollapse\r
+       - tunnelInit\r
+\r
+Transports SHOULD be namespaced under the cometd object and transports MUST\r
+register themselves with cometd.connectionTypes\r
+\r
+here's a stub transport defintion:\r
+\r
+cometd.blahTransport = new function(){\r
+       this._connectionType="my-polling";\r
+       this._cometd=null;\r
+       this.lastTimestamp = null;\r
+\r
+       this.check = function(types, version, xdomain){\r
+               // summary:\r
+               //              determines whether or not this transport is suitable given a\r
+               //              list of transport types that the server supports\r
+               return dojo.lang.inArray(types, "blah");\r
+       }\r
+\r
+       this.startup = function(){\r
+               if(dojox.cometd._polling){ return; }\r
+               // FIXME: fill in startup routine here\r
+               dojox.cometd._polling = true;\r
+       }\r
+\r
+       this.sendMessages = function(message){\r
+               // FIXME: fill in message array sending logic\r
+       }\r
+\r
+       this.deliver = function(message){\r
+       }\r
+\r
+       this.disconnect = function(){\r
+       }\r
+}\r
+cometd.connectionTypes.register("blah", cometd.blahTransport.check, cometd.blahTransport);\r
+*/\r
+\r
+dojox.cometd.longPollTransport = new function(){\r
+       this._connectionType="long-polling";\r
+       this._cometd=null;\r
+\r
+       this.check = function(types, version, xdomain){\r
+               return ((!xdomain)&&(dojo.indexOf(types, "long-polling") >= 0));\r
+       }\r
+\r
+       this.tunnelInit = function(){\r
+               var message = {\r
+                       channel:        "/meta/connect",\r
+                       clientId:       this._cometd.clientId,\r
+                       connectionType: this._connectionType,\r
+                       id:     ""+this._cometd.messageId++\r
+               };\r
+               message=this._cometd._extendOut(message);\r
+               this.openTunnelWith({message: dojo.toJson([message])});\r
+       }\r
+\r
+       this.tunnelCollapse = function(){\r
+               // TODO handle transport specific advice\r
+               \r
+               if(!this._cometd._initialized){ return; }\r
+                       \r
+               if(this._cometd._advice){\r
+                       if(this._cometd._advice["reconnect"]=="none"){\r
+                               return;\r
+                       }\r
+                       if(     (this._cometd._advice["interval"])&&\r
+                               (this._cometd._advice.interval>0) ){\r
+                               setTimeout(dojo.hitch(this,function(){ this._connect(); }),this._cometd._advice.interval);\r
+                       }else{\r
+                               this._connect();\r
+                       }\r
+               }else{\r
+                       this._connect();\r
+               }\r
+       }\r
+\r
+       this._connect = function(){\r
+               if(!this._cometd._initialized){ return; }\r
+               if(this._cometd._polling) {\r
+                       console.debug("wait for poll to complete or fail");\r
+                       return;\r
+               }\r
+                       \r
+               if(     (this._cometd._advice) &&\r
+                       (this._cometd._advice["reconnect"]=="handshake")\r
+               ){\r
+                       this._cometd._connected=false;\r
+                       this._initialized = false;\r
+                       this._cometd.init(this._cometd.url,this._cometd._props);\r
+               }else if(this._cometd._connected){\r
+                       var message={\r
+                               channel:        "/meta/connect",\r
+                               connectionType: this._connectionType,\r
+                               clientId:       this._cometd.clientId,\r
+                               id:     ""+this._cometd.messageId++\r
+                       };\r
+                       message=this._cometd._extendOut(message);\r
+                       this.openTunnelWith({message: dojo.toJson([message])});\r
+               }\r
+       }\r
+\r
+       this.deliver = function(message){\r
+               // Nothing to do\r
+       }\r
+\r
+       this.openTunnelWith = function(content, url){\r
+               this._cometd._polling = true;\r
+               var d = dojo.xhrPost({\r
+                       url: (url||this._cometd.url),\r
+                       content: content,\r
+                       handleAs: this._cometd.handleAs,\r
+                       load: dojo.hitch(this, function(data){\r
+                               this._cometd._polling = false;\r
+                               this._cometd.deliver(data);\r
+                               this.tunnelCollapse();\r
+                       }),\r
+                       error: dojo.hitch(this, function(err){\r
+                               this._cometd._polling = false;\r
+                               console.debug("tunnel opening failed:", err);\r
+                               dojo.publish("/cometd/meta", [{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);\r
+                               this._cometd._backoff();\r
+                               this.tunnelCollapse();\r
+                       })\r
+               });\r
+       }\r
+\r
+       this.sendMessages = function(messages){\r
+               for(var i=0; i<messages.length; i++){\r
+                       messages[i].clientId = this._cometd.clientId;\r
+                       messages[i].id = ""+this._cometd.messageId++;\r
+                       messages[i]=this._cometd._extendOut(messages[i]);\r
+               }\r
+               return dojo.xhrPost({\r
+                       url: this._cometd.url||djConfig["cometdRoot"],\r
+                       handleAs: this._cometd.handleAs,\r
+                       load: dojo.hitch(this._cometd, "deliver"),\r
+                       content: {\r
+                               message: dojo.toJson(messages)\r
+                       }\r
+               });\r
+       }\r
+\r
+       this.startup = function(handshakeData){\r
+               if(this._cometd._connected){ return; }\r
+               this.tunnelInit();\r
+       }\r
+\r
+       this.disconnect = function(){\r
+               var message={\r
+                       channel:        "/meta/disconnect",\r
+                       clientId:       this._cometd.clientId,\r
+                       id:     ""+this._cometd.messageId++\r
+               };\r
+               message=this._cometd._extendOut(message);\r
+               dojo.xhrPost({\r
+                       url: this._cometd.url||djConfig["cometdRoot"],\r
+                       handleAs: this._cometd.handleAs,\r
+                       content: {\r
+                               message: dojo.toJson([message])\r
+                       }\r
+               });\r
+       }\r
+}\r
+\r
+dojox.cometd.callbackPollTransport = new function(){\r
+       this._connectionType = "callback-polling";\r
+       this._cometd = null;\r
+\r
+       this.check = function(types, version, xdomain){\r
+               // we handle x-domain!\r
+               return (dojo.indexOf(types, "callback-polling") >= 0);\r
+       }\r
+\r
+       this.tunnelInit = function(){\r
+               var message = {\r
+                       channel:        "/meta/connect",\r
+                       clientId:       this._cometd.clientId,\r
+                       connectionType: this._connectionType,\r
+                       id:     ""+this._cometd.messageId++\r
+               };\r
+               message = this._cometd._extendOut(message);             \r
+               this.openTunnelWith({\r
+                       message: dojo.toJson([message])\r
+               });\r
+       }\r
+\r
+       this.tunnelCollapse = dojox.cometd.longPollTransport.tunnelCollapse;\r
+       this._connect = dojox.cometd.longPollTransport._connect;\r
+       this.deliver = dojox.cometd.longPollTransport.deliver;\r
+\r
+       this.openTunnelWith = function(content, url){\r
+               this._cometd._polling = true;\r
+               dojo.io.script.get({\r
+                       load: dojo.hitch(this, function(data){\r
+                               this._cometd._polling = false;\r
+                               this._cometd.deliver(data);\r
+                               this.tunnelCollapse();\r
+                       }),\r
+                       error: dojo.hitch(this, function(err){\r
+                               this._cometd._polling = false;\r
+                               console.debug("tunnel opening failed:", err);\r
+                               dojo.publish("/cometd/meta", [{cometd:this._cometd,action:"connect",successful:false,state:this._cometd.state()}]);\r
+                               this._cometd._backoff();\r
+                               this.tunnelCollapse();\r
+                       }),\r
+                       url: (url||this._cometd.url),\r
+                       content: content,\r
+                       callbackParamName: "jsonp"\r
+               });\r
+       }\r
+\r
+       this.sendMessages = function(/*array*/ messages){\r
+               for(var i=0; i<messages.length; i++){\r
+                       messages[i].clientId = this._cometd.clientId;\r
+                       messages[i].id = ""+this._cometd.messageId++;\r
+                       messages[i]=this._cometd._extendOut(messages[i]);\r
+               }\r
+               var bindArgs = {\r
+                       url: this._cometd.url||djConfig["cometdRoot"],\r
+                       load: dojo.hitch(this._cometd, "deliver"),\r
+                       callbackParamName: "jsonp",\r
+                       content: { message: dojo.toJson( messages ) }\r
+               };\r
+               return dojo.io.script.get(bindArgs);\r
+       }\r
+\r
+       this.startup = function(handshakeData){\r
+               if(this._cometd._connected){ return; }\r
+               this.tunnelInit();\r
+       }\r
+\r
+       this.disconnect = dojox.cometd.longPollTransport.disconnect;\r
+       \r
+       this.disconnect = function(){\r
+               var message={\r
+                       channel:"/meta/disconnect",\r
+                       clientId:this._cometd.clientId,\r
+                       id:""+this._cometd.messageId++\r
+               };\r
+               message=this._cometd._extendOut(message);               \r
+               dojo.io.script.get({\r
+                       url: this._cometd.url||djConfig["cometdRoot"],\r
+                       callbackParamName: "jsonp",\r
+                       content: {\r
+                               message: dojo.toJson([message])\r
+                       }\r
+               });\r
+       }\r
+}\r
+dojox.cometd.connectionTypes.register("long-polling", dojox.cometd.longPollTransport.check, dojox.cometd.longPollTransport);\r
+dojox.cometd.connectionTypes.register("callback-polling", dojox.cometd.callbackPollTransport.check, dojox.cometd.callbackPollTransport);\r
+\r
+dojo.addOnUnload(dojox.cometd,"_onUnload");\r
+\r
+}\r
+\r
+if(!dojo._hasResource["dojox.cometd"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojox.cometd"] = true;\r
+// stub loader for the cometd module since no implementation code is allowed to live in top-level files\r
+dojo.provide("dojox.cometd");\r
+\r
+\r
+}\r
+\r
+\r
+}});\r
diff --git a/webapps/cometd/examples/dojo/dojox/cometd/session.js b/webapps/cometd/examples/dojo/dojox/cometd/session.js
new file mode 100644 (file)
index 0000000..917b643
--- /dev/null
@@ -0,0 +1,27 @@
+dojo.provide("dojox.cometd.session");\r
+dojo.require("dojox.cometd");\r
+\r
+dojox.cometd.session= new function(){\r
+        this._session=null;\r
+\r
+        this._in=function(msg){\r
+                var channel=msg.channel;\r
+                if (channel=="/service/ext/session"){\r
+                        this._session=msg.data;\r
+                }\r
+                return msg;\r
+        }\r
+\r
+        this._out=function(msg){\r
+                var channel=msg.channel;\r
+                if (channel=="/meta/handshake" && this._session!=null){\r
+                        if (!msg.ext)\r
+                                msg.ext={};\r
+                        msg.ext.session=this._session;\r
+                }\r
+                return msg;\r
+        }\r
+};\r
+\r
+dojox.cometd._extendInList.push(dojo.hitch(dojox.cometd.session,"_in"));\r
+dojox.cometd._extendOutList.push(dojo.hitch(dojox.cometd.session,"_out"));
\ No newline at end of file
diff --git a/webapps/cometd/examples/dojo/dojox/cometd/timestamp.js b/webapps/cometd/examples/dojo/dojox/cometd/timestamp.js
new file mode 100644 (file)
index 0000000..e7b937c
--- /dev/null
@@ -0,0 +1,9 @@
+if(!dojo._hasResource["dojox.cometd.timestamp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.\r
+dojo._hasResource["dojox.cometd.timestamp"] = true;\r
+dojo.provide("dojox.cometd.timestamp");\r
+dojo.require("dojox.cometd");\r
+\r
+// A cometd extension that adds a timestamp to every message\r
+dojox.cometd._extendOutList.push(function(msg){msg.timestamp=new Date().toUTCString();return msg});\r
+\r
+}\r
diff --git a/webapps/cometd/examples/dojo/dojox/cometd/timesync.js b/webapps/cometd/examples/dojo/dojox/cometd/timesync.js
new file mode 100644 (file)
index 0000000..c522c51
--- /dev/null
@@ -0,0 +1,114 @@
+dojo.provide("dojox.cometd.timesync");\r
+dojo.require("dojox.cometd");\r
+\r
+/**\r
+ * this file provides the time synchronization extension to cometd.\r
+ * Timesync allows the client and server to exchange time information on every \r
+ * handshake and connect message so that the client may calculate an approximate\r
+ * offset from it's own clock epoch to that of the server.\r
+ *\r
+ * With each handshake or connect, the extension sends timestamps within the \r
+ * ext field like: {ext:{timesync:{tc:12345567890},...},...}\r
+ * where ts is the timestamp in ms since 1970 of when the message was sent.\r
+ *\r
+ * A cometd server that supports timesync, should respond with and ext field\r
+ * like: {ext:{timesync:{tc:12345567890,ts:1234567900,p:123},...},...}\r
+ * where ts is the timestamp sent by the client, te is the timestamp on the server\r
+ * of when the message was received and p is the poll duration in ms - ie the\r
+ * time the server took before sending the response.\r
+ *\r
+ * On receipt of the response, the client is able to use current time to determine\r
+ * the total trip time, from which p is subtracted to determine an approximate\r
+ * two way network traversal time. The assumption is made that the network is\r
+ * symmetric for traversal time, so the offset between the two clocks is \r
+ * ts-tc-(now-tc-p)/2. In practise networks (and the cometd client/server software)\r
+ * is never perfectly symmetric, so accuracy is limited by the difference, which \r
+ * can be 10s of milliseconds.\r
+ *\r
+ * In order to smooth over any transient fluctuations, the extension keeps a sliding\r
+ * average of the offsets received. By default this is over 10 messages, but this can\r
+ * be changed with the dojox.cometd.timesync._window element.\r
+ */\r
+dojox.cometd.timesync= new function(){\r
+\r
+       this._window=10;// The window size for the sliding average of offset samples.\r
+       this.offset=0;  // The offset in ms between the clients clock and the servers clock. Add this to the local\r
+                       // time epoch to obtain server time.\r
+       this.samples=0; // The number of samples used to calculate the offset. If 0, the offset is not valid.\r
+       \r
+       this.getServerTime=function(){ // return: long\r
+               // Summary:\r
+               //      Calculate the current time on the server\r
+               // \r
+               return new Date().getTime()+this.offset;\r
+       }\r
+       \r
+       this.getServerDate=function(){ // return: Date\r
+               // Summary:\r
+               //      Calculate the current time on the server\r
+               // \r
+               return new Date(this.getServerTime());\r
+       }\r
+       \r
+       this.setTimeout=function(/*function*/call,/*long|Date*/atTimeOrDate){\r
+               // Summary:\r
+               //      Set a timeout function relative to server time\r
+               // call:\r
+               //      the function to call when the timeout occurs\r
+               // atTimeOrTime: \r
+               //      a long timestamp or a Date representing the server time at\r
+               //      which the timeout should occur.\r
+               \r
+               var ts=(atTimeOrDate instanceof Date)?atTimeOrDate.getTime():(0+atTimeOrDate);\r
+               var tc=ts-this.offset;\r
+               var interval=tc-new Date().getTime();\r
+               if (interval<=0)\r
+                       interval=1;\r
+               return setTimeout(call,interval);\r
+       }\r
+\r
+       this._in=function(/*Object*/msg){\r
+               // Summary:\r
+               //      Handle incoming messages for the timesync extension.\r
+               // description:\r
+               //      Look for ext:{timesync:{}} field and calculate offset if present.\r
+               // msg: \r
+               //      The incoming bayeux message\r
+               \r
+               var channel=msg.channel;\r
+               if (channel=="/meta/handshake" || channel=="/meta/connect"){\r
+                       if (msg.ext && msg.ext.timesync){\r
+                               var sync=msg.ext.timesync;\r
+                               var now=new Date().getTime();\r
+                               var offset=sync.ts-sync.tc-(now-sync.tc-sync.p)/2;\r
+                               \r
+                               if (this.samples++==0)\r
+                                       this.offset=offset;\r
+                               else\r
+                                       this.offset=(this.offset*(this._window-1)+offset)/this._window;\r
+                       }\r
+               }\r
+               return msg;\r
+       }\r
+\r
+       this._out=function(msg){\r
+               // Summary:\r
+               //      Handle outgoing messages for the timesync extension.\r
+               // description:\r
+               //      Look for handshake and connect messages and add the ext:{timesync:{}} fields\r
+               // msg: \r
+               //      The outgoing bayeux message\r
+               \r
+               var channel=msg.channel;\r
+               if (channel=="/meta/handshake" || channel=="/meta/connect"){\r
+                       var now=new Date().getTime();\r
+                       if (!msg.ext)\r
+                               msg.ext={};\r
+                       msg.ext.timesync={tc: now};\r
+               }\r
+               return msg;\r
+       }\r
+};\r
+\r
+dojox.cometd._extendInList.push(dojo.hitch(dojox.cometd.timesync,"_in"));\r
+dojox.cometd._extendOutList.push(dojo.hitch(dojox.cometd.timesync,"_out"));\r
diff --git a/webapps/cometd/examples/echo/index.html b/webapps/cometd/examples/echo/index.html
new file mode 100644 (file)
index 0000000..3425718
--- /dev/null
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\r
+    "http://www.w3.org/TR/html4/strict.dtd">\r
+<html>\r
+    <head>\r
+        <title>Comet echo RPC</title>\r
+        <link rel="stylesheet" type="text/css" href="chat/chat.css"></link>\r
+        <script type="text/javascript" src="../dojo/dojo/dojo.js.uncompressed.js"></script>\r
+        <script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>\r
+        <script type="text/javascript">\r
+            dojo.require("dojox.cometd");\r
+            dojo.require("dojox.cometd.timestamp");\r
+            $ = dojo.byId;\r
+\r
+            var echoBehaviours = { \r
+                '#phrase': {\r
+                    "found": function(e){\r
+                        e.setAttribute("autocomplete","OFF");\r
+                    },\r
+                    "onkeyup": function(e){\r
+                        if(e.keyCode == dojo.keys.ENTER){\r
+                            echoRpc($('phrase').value);\r
+                            $('phrase').value='';\r
+                            return false;\r
+                        }\r
+                        return true;\r
+                    }\r
+                },\r
+\r
+                '#sendB': {\r
+                    "onclick": function(e){\r
+                        echoRpc($('phrase').value);\r
+                        $('phrase').value='';\r
+                        return false;\r
+                    }\r
+                }\r
+            };\r
+\r
+\r
+            function setUp(){\r
+\r
+              var element=dojo.byId('phrase');\r
+              element.setAttribute("autocomplete","OFF");\r
+              dojo.connect(element, "onkeyup", function(e){   \r
+                        if(e.keyCode == dojo.keys.ENTER){\r
+                            echoRpc($('phrase').value);\r
+                            $('phrase').value='';\r
+                            return false;\r
+                        }\r
+                        return true;\r
+                    });\r
+              element=dojo.byId('sendB');\r
+              dojo.connect(element, "onclick", function(e){   \r
+                        echoRpc($('phrase').value);\r
+                        $('phrase').value='';\r
+                        return false;\r
+                    });\r
+\r
+\r
+\r
+              dojox.cometd.init(new String(document.location).replace(/http:\/\/[^\/]*/,'').replace(/\/examples\/.*$/,'')+"/cometd");\r
+              dojox.cometd.subscribe("/service/echo",echoRpcReturn);\r
+            }\r
+\r
+            function echoRpc(msg){\r
+                console.debug(msg);\r
+                dojox.cometd.publish("/service/echo", { msg: msg });\r
+            }\r
+            \r
+            function echoRpcReturn(msg){\r
+                dojo.byId("responses").innerHTML += (msg.timestamp?msg.timestamp:"")+" "+msg.channel+": "+msg.data.msg+"\n";\r
+            }\r
+\r
+            dojo.addOnLoad(setUp);\r
+        </script>\r
+    </head>\r
+    <body>\r
+        \r
+        <h1>Echo test</h1>\r
+        <p>\r
+            Echo data to ONLY this client using RPC style messaging over\r
+            cometd. Requires a server side component at /service/echo which echos\r
+            responses directly to the client.\r
+        </p>\r
+        <div>\r
+            Echo: <input id="phrase" type="text"></input> <input id="sendB" class="button" type="submit" name="join" value="Send"/>\r
+        </div>\r
+        <pre id="responses"></pre>\r
+    </body>\r
+</html>\r
diff --git a/webapps/cometd/examples/simplechat/cometdchat.htm b/webapps/cometd/examples/simplechat/cometdchat.htm
new file mode 100644 (file)
index 0000000..f6183fa
--- /dev/null
@@ -0,0 +1,113 @@
+<html>\r
+<header><title>Comet Simple Chat Application</title>\r
+\r
+<script type="text/javascript" src="../dojo/dojo/dojo.js"></script>\r
+<script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>\r
+\r
+<script type="text/javascript">\r
+\r
+dojo.require("dojox.cometd");\r
+\r
+dojo.addOnLoad(function() {\r
+  dojo.byId("message").style.visibility='hidden';\r
+  dojox.cometd.init("/cometd/cometd");\r
+});\r
+\r
+\r
+function trim(str) {\r
+    return str.replace(/(^\s+|\s+$)/g,'');\r
+}\r
+\r
+\r
+function clear() {\r
+  dojo.byId("msgtext").value = "";\r
+  dojo.byId("msgtext").focus();\r
+}\r
+\r
+\r
+function enterKeyHandler(e) {\r
+if (!e) e = window.event;\r
+   if (e.keyCode == 13) {\r
+      send(trim(dojo.byId("msgtext").value));\r
+      clear();\r
+   }\r
+\r
+}\r
+\r
+\r
+function connect() {\r
+  dojox.cometd.subscribe("/chat/demo", onMsgEvent);\r
+  dojo.byId("login").style.visibility='hidden';\r
+  dojo.byId("message").style.visibility='visible';\r
+  send("Has joined the chat room");\r
+  clear();\r
+  dojo.byId("msgtext").onkeydown = enterKeyHandler;\r
+  dojo.byId("myname").appendChild(document.createTextNode("-> " + dojo.byId("scrname").value + " <-"));\r
+}\r
+\r
+\r
+function onMsgEvent(event) {\r
+\r
+   // Break apart the text string into screen name and message parts.\r
+   var str = trim(event.data.msg);\r
+   var scrname = ""; \r
+   if (str.match(/^.*[|].*$/)) {\r
+       var spl = str.split("|");\r
+       scrname = spl[0];\r
+       str = " - " + spl[1];\r
+   }\r
+\r
+   // Insert the screen name in red and the message black into the DOM\r
+   var newP = document.createElement("p");\r
+   var fnt1 = document.createElement("font");\r
+   var attr1 = document.createAttribute("color");\r
+   attr1.nodeValue = "red";\r
+   fnt1.setAttributeNode(attr1);\r
+\r
+   var newT = document.createTextNode(scrname);\r
+   fnt1.appendChild(newT);\r
+\r
+   newP.appendChild(fnt1);  \r
+\r
+   var fnt2 = document.createElement("font");\r
+   var attr2 = document.createAttribute("color");\r
+   attr2.nodeValue = "black";\r
+   fnt2.setAttributeNode(attr2);\r
+\r
+   var newT2 = document.createTextNode(str);\r
+   fnt2.appendChild(newT2);\r
+\r
+   newP.appendChild(fnt2);  \r
+\r
+   dojo.byId("dialog").appendChild(newP)\r
+}\r
+\r
+\r
+function send(msg) {\r
+  var scrname = dojo.byId("scrname").value;\r
+  var evt = {'data': { 'msg': trim(scrname) + '|' + msg }};\r
+  onMsgEvent(evt);  // Echo local\r
+  dojox.cometd.publish("/chat/demo", evt.data);\r
+}\r
+\r
+\r
+</script>\r
+\r
+</head>\r
+</header>\r
+<body>\r
+<form>\r
+<div id="login">\r
+Screen name:&nbsp;<input type="text" id="scrname">\r
+<input type=Button Id=logbtn value=Connect onClick=connect()><br/>Type a screen name and click 'Connect'\r
+</div>\r
+\r
+<div id="dialog"></div>\r
+<hr/>\r
+<div id="message">\r
+<div id="myname"></div> Is my screen name<br/>\r
+<textarea rows="3" cols="50" id="msgtext"></textarea>[ENTER] sends message</div>\r
+</form>\r
+</body>\r
+</html>\r
+\r
diff --git a/webapps/cometd/examples/tictactoe/resources/0.gif b/webapps/cometd/examples/tictactoe/resources/0.gif
new file mode 100644 (file)
index 0000000..0198a16
Binary files /dev/null and b/webapps/cometd/examples/tictactoe/resources/0.gif differ
diff --git a/webapps/cometd/examples/tictactoe/resources/1.gif b/webapps/cometd/examples/tictactoe/resources/1.gif
new file mode 100644 (file)
index 0000000..9b5268a
Binary files /dev/null and b/webapps/cometd/examples/tictactoe/resources/1.gif differ
diff --git a/webapps/cometd/examples/tictactoe/resources/10.gif b/webapps/cometd/examples/tictactoe/resources/10.gif
new file mode 100644 (file)
index 0000000..6cc7a6c
Binary files /dev/null and b/webapps/cometd/examples/tictactoe/resources/10.gif differ
diff --git a/webapps/cometd/examples/tictactoe/resources/blank.gif b/webapps/cometd/examples/tictactoe/resources/blank.gif
new file mode 100644 (file)
index 0000000..0198a16
Binary files /dev/null and b/webapps/cometd/examples/tictactoe/resources/blank.gif differ
diff --git a/webapps/cometd/examples/tictactoe/resources/tictactoe.css b/webapps/cometd/examples/tictactoe/resources/tictactoe.css
new file mode 100644 (file)
index 0000000..008999d
--- /dev/null
@@ -0,0 +1,74 @@
+\r
+body {\r
+  background-color : #ffffcc;\r
+  text-align: center;\r
+}\r
+a:link {\r
+  color : black;\r
+}\r
+a:visited {\r
+  color : black;\r
+}\r
+a:hover {\r
+  color : yellow;\r
+}\r
+p.instructions {\r
+  background-color : #ccffcc;\r
+  padding-left : 20%;\r
+  padding-right : 20%;\r
+}\r
+table {\r
+  border: none;\r
+  margin: 0 auto;\r
+}\r
+td#cell0 {\r
+  padding: 10px ;\r
+  border-bottom: thin solid black;\r
+  border-right: thin solid black;\r
+}\r
+td#cell1 {\r
+  padding: 10px ;\r
+  border-bottom: thin solid black;\r
+  border-right: thin solid black;\r
+  border-left: thin solid black;\r
+}\r
+td#cell2 {\r
+  padding: 10px ;\r
+  border-left: thin solid black;\r
+  border-bottom: thin solid black;\r
+}\r
+td#cell3 {\r
+  padding: 10px ;\r
+  border-top: thin solid black;\r
+  border-right: thin solid black;\r
+  border-bottom: thin solid black;\r
+}\r
+td#cell4 {\r
+  padding: 10px ;\r
+  border-top: thin solid black;\r
+  border-right: thin solid black;\r
+  border-left: thin solid black;\r
+  border-bottom: thin solid black;\r
+}\r
+td#cell5 {\r
+  padding: 10px ;\r
+  border-top: thin solid black;\r
+  border-left: thin solid black;\r
+  border-bottom: thin solid black;\r
+}\r
+td#cell6 {\r
+  padding: 10px ;\r
+  border-top: thin solid black;\r
+  border-right: thin solid black;\r
+}\r
+td#cell7 {\r
+  padding: 10px ;\r
+  border-top: thin solid black;\r
+  border-right: thin solid black;\r
+  border-left: thin solid black;\r
+}\r
+td#cell8 {\r
+  padding: 10px ;\r
+  border-top: thin solid black;\r
+  border-left: thin solid black;\r
+}\r
diff --git a/webapps/cometd/examples/tictactoe/ttt1.htm b/webapps/cometd/examples/tictactoe/ttt1.htm
new file mode 100644 (file)
index 0000000..b6b560a
--- /dev/null
@@ -0,0 +1,252 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+    <head>
+        <title>Tic Tac Toe - Cometd Example</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <link rel="stylesheet" type="text/css" href="resources/tictactoe.css">
+
+        <script type="text/javascript" src="../dojo/dojo/dojo.js"></script>
+        <script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>
+
+        <script type="text/javascript">
+
+            dojo.require("dojox.cometd");
+
+            var X = 10;
+            var O = 1;
+   
+            var board = new Array(9);
+            var wins = new Array(8);
+
+            var turnNum = 0;
+            var winner = -1;
+
+            var iam = 0;
+
+            dojo.addOnLoad(function() {
+                initArrays();
+                dojox.cometd.init("/cometd/cometd");
+                dojox.cometd.subscribe("/demo/tictactoe", onMsgEvent);
+            });
+
+            function postMe(arg) {
+                var move = eval(arg);
+                if (iam == 0 && this.win() != -1) {
+                    dojo.byId("myrole").innerHTML = "Game is over.";
+                    return;
+                }
+                if (iam == 0 && turnNum > 1) {
+                    dojo.byId("gstatus").innerHTML = "Sorry, you will have to wait until this game ends.";
+                    return;
+                }
+                if (iam != 0 && this.whoseTurn() != iam) {
+                    dojo.byId("gstatus").innerHTML = "Not your turn.";
+                    return;
+                }
+                if (! this.turn(move)) {
+                    dojo.byId("gstatus").innerHTML = "Not a valid move try again";
+                    return;
+                }
+                var win = this.win();
+                var turn = this.whoseTurn();
+
+                var data = { 'move': move, 'win': win, 'turn': turn, 'board': board, 'turnNum': turnNum };
+                dojox.cometd.publish("/demo/tictactoe", data);
+                onMsgEvent({ 'data': data});
+                
+            };
+
+            function onMsgEvent(event) {
+                
+                var data = event.data
+
+                for (i = 0; i < 9; i++) {
+                    // 0 is blank, 10 is x, 1 is o
+                    dojo.byId("img" + i).src = "resources/" + data.board[i] + ".gif";
+                }
+                
+                var statusMsg;  
+                // -1 is unfinished, 0 is tie, 1 is X win, 2 is Y win
+                if (data.win == 0) {
+                    statusMsg = "It's a tie!";
+                } else if (data.win == 1) {
+                    statusMsg = "X wins!";
+                } else if (data.win == 2) {
+                    statusMsg = "O wins!";
+                } else if (data.win == -1 && data.turn == 10) {  
+                    statusMsg = "It's X's Turn";
+                } else if (data.win == -1 && data.turn == 1) {
+                    statusMsg = "It's O's Turn";
+                } else {
+                    statusMsg = "That's odd, it shouldn't get here";
+                }
+
+                if (iam == 0) {
+                    if (turnNum == 0) {
+                        dojo.byId("myrole").innerHTML = "You can join the game if you'd like.";
+                    } else {
+                        dojo.byId("myrole").innerHTML = "You are currently a spectator.";
+                    }
+                } else if (iam == X) {
+                    dojo.byId("myrole").innerHTML = "You are currently player 'X'.";
+                } else {
+                    dojo.byId("myrole").innerHTML = "You are currently player 'O'.";
+                }
+                
+                if (data.win != -1) {
+                    statusMsg = statusMsg + '<br><a href="ttt1.html">Restart the game</a>';
+                }
+        
+                // And write the status message out here -
+                dojo.byId("gstatus").innerHTML = statusMsg;
+
+                board = data.board;
+                winner = data.win;
+                turnNum = data.turnNum;
+            }
+
+            //return false if cell is an invalid move
+            function turn(cell) {
+                if (cell < 0 || cell > 8) return false; // invalid move
+                if (winner != -1) return false;
+                if (iam == 0) {
+                    if (iam == 0 && turnNum == 0) {
+                        iam = X;
+                    }
+                    else {
+                        iam = O;
+                    }
+                }
+                if (board[cell] != 0) {
+                    return false; // invalid move
+                }
+                turnNum++;
+                if (turnNum % 2 == 1) { //then X
+                    board[cell] = X;
+                } else { // else O
+                    board[cell] = O;
+                }
+                return true;
+            }
+
+            function whoseTurn() {
+                if (turnNum == 0 || turnNum % 2 == 0 ) {
+                    return X;
+                } else return O;
+            }
+
+    
+            function done() {
+                return (turnNum > 8);
+            }
+            
+            
+            // return -1 for no win, 0 for tie, 1 for x win, 2 for o win
+            function win() {
+                if (winner != -1) {
+                    return winner;
+                }
+                for (i = 0 ; i < 8; i++) {
+                    var winSum = board[wins[i][0]] + board[wins[i][1]] + board[wins[i][2]];
+                    if (winSum == 3) {
+                        winner = 2;
+                    } else if (winSum == 30) {
+                        winner = 1;
+                    }
+                }
+                if (winner == -1 && turnNum > 8) {
+                    winner = 0;
+                } 
+                return winner;
+            }
+
+            function initArrays() {
+
+                wins[0] = new Array(3);
+                wins[0][0] = 0;
+                wins[0][1] = 1;
+                wins[0][2] = 2;
+
+                wins[1] = new Array(3);
+                wins[1][0] = 3;
+                wins[1][1] = 4;
+                wins[1][2] = 5;
+
+                wins[2] = new Array(3);
+                wins[2][0] = 6;
+                wins[2][1] = 7;
+                wins[2][2] = 8;
+
+                wins[3] = new Array(3);
+                wins[3][0] = 0;
+                wins[3][1] = 3;
+                wins[3][2] = 6;
+
+                wins[4] = new Array(3);
+                wins[4][0] = 1;
+                wins[4][1] = 4;
+                wins[4][2] = 7;
+
+                wins[5] = new Array(3);
+                wins[5][0] = 2;
+                wins[5][1] = 5;
+                wins[5][2] = 8;
+
+                wins[6] = new Array(3);
+                wins[6][0] = 0;
+                wins[6][1] = 4;
+                wins[6][2] = 8;
+
+                wins[7] = new Array(3);
+                wins[7][0] = 2;
+                wins[7][1] = 4;
+                wins[7][2] = 6;
+
+                board[0] = 0;
+                board[1] = 0;
+                board[2] = 0;
+                board[3] = 0;
+                board[4] = 0;
+                board[5] = 0;
+                board[6] = 0;
+                board[7] = 0;
+                board[8] = 0;
+            }
+
+        </script>
+        
+    </head>
+    <body>
+        
+        <h1>Tic Tac Toe</h1>
+        <table>
+            <tr>
+                <td id="cell0"><img id="img0" src="resources/0.gif" 
+                                    onclick=postMe("0")></td>
+                <td id="cell1"><img id="img1" src="resources/0.gif" 
+                                    onclick=postMe("1")></td>
+                <td id="cell2"><img id="img2" src="resources/0.gif" 
+                                    onclick=postMe("2")></td>
+            </tr>
+            <tr>
+                <td id="cell3"><img id="img3" src="resources/0.gif" 
+                                    onclick=postMe("3")></td>
+                <td id="cell4"><img id="img4" src="resources/0.gif" 
+                                    onclick=postMe("4")></td>
+                <td id="cell5"><img id="img5" src="resources/0.gif" 
+                                    onclick=postMe("5")></td>
+            </tr>
+            <tr>
+                <td id="cell6"><img id="img6" src="resources/0.gif" 
+                                    onclick=postMe("6")></td>
+                <td id="cell7"><img id="img7" src="resources/0.gif" 
+                                    onclick=postMe("7")></td>
+                <td id="cell8"><img id="img8" src="resources/0.gif" 
+                                    onclick=postMe("8")></td>
+            </tr>      
+        </table>
+        <h2 id="gstatus">New game, click to start.</h2><br/>
+        <h2 id="myrole"></h2>
+    </body>
+</html>
diff --git a/webapps/cometd/examples/timesync/index.html b/webapps/cometd/examples/timesync/index.html
new file mode 100644 (file)
index 0000000..eaf506d
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\r
+    "http://www.w3.org/TR/html4/strict.dtd">\r
+<html>\r
+    <head>\r
+        <title>Comet echo RPC</title>\r
+        <link rel="stylesheet" type="text/css" href="chat/chat.css"></link>\r
+        <script type="text/javascript" src="../dojo/dojo/dojo.js.uncompressed.js"></script>\r
+        <script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script>\r
+        <script type="text/javascript">\r
+            dojo.require("dojox.cometd");\r
+            dojo.require("dojox.cometd.timesync");\r
+\r
+            dojox.cometd.init(new String(document.location).replace(/http:\/\/[^\/]*/,'').replace(/\/examples\/.*$/,'')+"/cometd");\r
+\r
+            var next=0;\r
+            var tick=function(){\r
+               var now=dojox.cometd.timesync.getServerDate();\r
+               dojo.byId("time").innerHTML=now.toUTCString();\r
+               \r
+               if (next==0)\r
+                   next=1000*(1.5+now.getTime()/1000).toFixed();\r
+               else\r
+                   next=next+1000;\r
+               dojox.cometd.timesync.setTimeout(tick,next+25);\r
+           }\r
+\r
+            setTimeout(tick,1000);\r
+        </script>\r
+    </head>\r
+    <body>\r
+        \r
+        <h1>TimeSync test</h1>\r
+        <p>\r
+            This test uses the timesync extension to calculate a timeoffset with the server.\r
+           The time displayed below is the server time and should be the same for all clients no matter what their local clocks are set to. \r
+           The accuracy should improve as each connect response is received.\r
+        </p>\r
+        <div id="time">\r
+        </div>\r
+    </body>\r
+</html>\r
diff --git a/webapps/cometd/index.html b/webapps/cometd/index.html
new file mode 100644 (file)
index 0000000..29e4616
--- /dev/null
@@ -0,0 +1,7 @@
+\r
+<h1>Cometd demo</h1>\r
+\r
+<p>\r
+Try the <a href="examples/simplechat/cometdchat.htm">Simple Chat Demo</a>.</br>\r
+Try the <a href="examples/tictactoe/ttt1.htm">Tic-Tac-Toe Demo</a>.</br>\r
+</p>\r
index 20a39d5..8f1321c 100644 (file)
@@ -33,7 +33,7 @@
 
 <body>
 <section name="Tomcat Trunk ">
-
+  
   <subsection name="ha">
     <changelog>
      <fix><rev>671650</rev><br/><bug>45279</bug><br/>