From 86cf6875ae30ae1fb0bd4377894fec54875a98bf Mon Sep 17 00:00:00 2001 From: fhanik Date: Tue, 2 Sep 2008 20:00:36 +0000 Subject: [PATCH] Added in Bayeux implementation, as per 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 --- .classpath | 19 +- COPYRIGHT | 5 + build.properties.default | 5 + build.xml | 2 + extras.xml | 62 +- java/org/apache/cometd/bayeux/Bayeux.java | 242 + java/org/apache/cometd/bayeux/Channel.java | 103 + java/org/apache/cometd/bayeux/Client.java | 91 + java/org/apache/cometd/bayeux/DataFilter.java | 38 + java/org/apache/cometd/bayeux/Listener.java | 45 + java/org/apache/cometd/bayeux/Message.java | 68 + java/org/apache/cometd/bayeux/SecurityPolicy.java | 28 + java/org/apache/tomcat/bayeux/BayeuxException.java | 39 + java/org/apache/tomcat/bayeux/BayeuxRequest.java | 54 + java/org/apache/tomcat/bayeux/BayeuxServlet.java | 236 + java/org/apache/tomcat/bayeux/ChannelImpl.java | 189 + java/org/apache/tomcat/bayeux/ClientImpl.java | 278 + java/org/apache/tomcat/bayeux/HttpError.java | 43 + java/org/apache/tomcat/bayeux/MessageImpl.java | 80 + java/org/apache/tomcat/bayeux/RequestBase.java | 251 + java/org/apache/tomcat/bayeux/RequestFactory.java | 48 + java/org/apache/tomcat/bayeux/TomcatBayeux.java | 176 + .../tomcat/bayeux/request/MetaConnectRequest.java | 125 + .../bayeux/request/MetaDisconnectRequest.java | 105 + .../bayeux/request/MetaHandshakeRequest.java | 116 + .../bayeux/request/MetaSubscribeRequest.java | 130 + .../bayeux/request/MetaUnsubscribeRequest.java | 130 + .../tomcat/bayeux/request/PublishRequest.java | 140 + .../cometd/bayeux/samples/EchoChatClient.java | 111 + webapps/cometd/WEB-INF/filters.json | 27 + webapps/cometd/WEB-INF/web.xml | 26 + webapps/cometd/examples/chat/chat.css | 57 + webapps/cometd/examples/chat/chat.js | 139 + webapps/cometd/examples/chat/index.html | 26 + webapps/cometd/examples/dojo/dojo/dojo.js | 20 + .../examples/dojo/dojo/dojo.js.uncompressed.js | 8165 +++++++++++++++++++ webapps/cometd/examples/dojo/dojo/dojo.xd.js | 20 + .../examples/dojo/dojo/dojo.xd.js.uncompressed.js | 8203 ++++++++++++++++++++ webapps/cometd/examples/dojo/dojox/cometd.js | 20 + .../examples/dojo/dojox/cometd.js.uncompressed.js | 1142 +++ webapps/cometd/examples/dojo/dojox/cometd.xd.js | 20 + .../dojo/dojox/cometd.xd.js.uncompressed.js | 1149 +++ .../cometd/examples/dojo/dojox/cometd/session.js | 27 + .../cometd/examples/dojo/dojox/cometd/timestamp.js | 9 + .../cometd/examples/dojo/dojox/cometd/timesync.js | 114 + webapps/cometd/examples/echo/index.html | 89 + webapps/cometd/examples/simplechat/cometdchat.htm | 113 + webapps/cometd/examples/tictactoe/resources/0.gif | Bin 0 -> 92 bytes webapps/cometd/examples/tictactoe/resources/1.gif | Bin 0 -> 2596 bytes webapps/cometd/examples/tictactoe/resources/10.gif | Bin 0 -> 2586 bytes .../cometd/examples/tictactoe/resources/blank.gif | Bin 0 -> 92 bytes .../examples/tictactoe/resources/tictactoe.css | 74 + webapps/cometd/examples/tictactoe/ttt1.htm | 252 + webapps/cometd/examples/timesync/index.html | 41 + webapps/cometd/index.html | 7 + webapps/docs/changelog.xml | 2 +- 56 files changed, 22682 insertions(+), 19 deletions(-) create mode 100644 COPYRIGHT create mode 100644 java/org/apache/cometd/bayeux/Bayeux.java create mode 100644 java/org/apache/cometd/bayeux/Channel.java create mode 100644 java/org/apache/cometd/bayeux/Client.java create mode 100644 java/org/apache/cometd/bayeux/DataFilter.java create mode 100644 java/org/apache/cometd/bayeux/Listener.java create mode 100644 java/org/apache/cometd/bayeux/Message.java create mode 100644 java/org/apache/cometd/bayeux/SecurityPolicy.java create mode 100644 java/org/apache/tomcat/bayeux/BayeuxException.java create mode 100644 java/org/apache/tomcat/bayeux/BayeuxRequest.java create mode 100644 java/org/apache/tomcat/bayeux/BayeuxServlet.java create mode 100644 java/org/apache/tomcat/bayeux/ChannelImpl.java create mode 100644 java/org/apache/tomcat/bayeux/ClientImpl.java create mode 100644 java/org/apache/tomcat/bayeux/HttpError.java create mode 100644 java/org/apache/tomcat/bayeux/MessageImpl.java create mode 100644 java/org/apache/tomcat/bayeux/RequestBase.java create mode 100644 java/org/apache/tomcat/bayeux/RequestFactory.java create mode 100644 java/org/apache/tomcat/bayeux/TomcatBayeux.java create mode 100644 java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java create mode 100644 java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java create mode 100644 java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java create mode 100644 java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java create mode 100644 java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java create mode 100644 java/org/apache/tomcat/bayeux/request/PublishRequest.java create mode 100644 test/org/apache/cometd/bayeux/samples/EchoChatClient.java create mode 100644 webapps/cometd/WEB-INF/filters.json create mode 100644 webapps/cometd/WEB-INF/web.xml create mode 100644 webapps/cometd/examples/chat/chat.css create mode 100644 webapps/cometd/examples/chat/chat.js create mode 100644 webapps/cometd/examples/chat/index.html create mode 100644 webapps/cometd/examples/dojo/dojo/dojo.js create mode 100644 webapps/cometd/examples/dojo/dojo/dojo.js.uncompressed.js create mode 100644 webapps/cometd/examples/dojo/dojo/dojo.xd.js create mode 100644 webapps/cometd/examples/dojo/dojo/dojo.xd.js.uncompressed.js create mode 100644 webapps/cometd/examples/dojo/dojox/cometd.js create mode 100644 webapps/cometd/examples/dojo/dojox/cometd.js.uncompressed.js create mode 100644 webapps/cometd/examples/dojo/dojox/cometd.xd.js create mode 100644 webapps/cometd/examples/dojo/dojox/cometd.xd.js.uncompressed.js create mode 100644 webapps/cometd/examples/dojo/dojox/cometd/session.js create mode 100644 webapps/cometd/examples/dojo/dojox/cometd/timestamp.js create mode 100644 webapps/cometd/examples/dojo/dojox/cometd/timesync.js create mode 100644 webapps/cometd/examples/echo/index.html create mode 100644 webapps/cometd/examples/simplechat/cometdchat.htm create mode 100644 webapps/cometd/examples/tictactoe/resources/0.gif create mode 100644 webapps/cometd/examples/tictactoe/resources/1.gif create mode 100644 webapps/cometd/examples/tictactoe/resources/10.gif create mode 100644 webapps/cometd/examples/tictactoe/resources/blank.gif create mode 100644 webapps/cometd/examples/tictactoe/resources/tictactoe.css create mode 100644 webapps/cometd/examples/tictactoe/ttt1.htm create mode 100644 webapps/cometd/examples/timesync/index.html create mode 100644 webapps/cometd/index.html diff --git a/.classpath b/.classpath index 334c80ed0..889a4b2c4 100644 --- a/.classpath +++ b/.classpath @@ -1,27 +1,12 @@ - - + + diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 000000000..0453866ea --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,5 @@ +****For the bayeux implementation********* +* Copyright 2007-2008 Guy Molinari +* Copyright 2007-2008 Filip Hanik +* Copyright 2007 Dojo Foundation +* Copyright 2007 Mort Bay Consulting Pty. Ltd. diff --git a/build.properties.default b/build.properties.default index 096ba49c3..fd57b9854 100644 --- a/build.properties.default +++ b/build.properties.default @@ -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 diff --git a/build.xml b/build.xml index 0a100dc30..92e3578f5 100644 --- a/build.xml +++ b/build.xml @@ -112,6 +112,8 @@ + + diff --git a/extras.xml b/extras.xml index 6d6b44a1c..b7d7bbb14 100644 --- a/extras.xml +++ b/extras.xml @@ -76,6 +76,11 @@ + + + + + @@ -220,7 +225,62 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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} + + + + diff --git a/java/org/apache/cometd/bayeux/Bayeux.java b/java/org/apache/cometd/bayeux/Bayeux.java new file mode 100644 index 000000000..5385d325d --- /dev/null +++ b/java/org/apache/cometd/bayeux/Bayeux.java @@ -0,0 +1,242 @@ +/* + * 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. + */ +package org.apache.cometd.bayeux; + +import java.util.List; + +/** Bayeux Interface.
+ * This interface represents the server side API for the Bayeux messaging protocol. + * Bayeux is a simple subscribe/publish/receive methodology, not far from JMS, but much simplified.
+ * It is used both by the actual implementation and by server side clients.
+ * Server side clients use this to create, retrieve and subscribe to channels. + * Server side clients are represented, just like remote clients, through the Client interface. + *
+ * The Bayeux implementations is intended to be thread safe and multiple threads may simultaneously call Bayeux methods. + *
+ * The Bayeux object, is the starting point for any cometd application relying on the Bayeux object. + * Dependent on the container, the Bayeux object will be stored in the javax.servlet.ServletContext object + * as an attribute under the name Bayeux.DOJOX_COMETD_BAYEUX
+ * To retrieve this object, one would simply call
+ * Bayeux bx = (Bayeux)getServletContext().getAttribute(Bayeux.DOJOX_COMETD_BAYEUX); + *

+ * The Bayeux protocol is pretty straight forward and includes a bunch of messaging that is not needed to be known to clients, + * both server side and remote clients. + * This object gets initialized by a container dependent servlet, and the servlet then handles all Bayeux communication from the client. + * Remote messsages are delivered to channels, and to server side clients using the Listener interface.
+ *
+ * A Bayeux session is active as long as the webapp hosting the Bayeux object is active.
+ * When the webapplication shuts down, the Bayeux object will unsubscribe all clients and remove all the active channels. + * + * @author Greg Wilkins + * @author Filip Hanik + */ +public interface Bayeux { + + /**Meta definitions for channels*/ + public static final String META="/meta"; + /**Meta definitions for channels*/ + public static final String META_SLASH="/meta/"; + /**Meta definitions for channels - connect message*/ + public static final String META_CONNECT="/meta/connect"; + /**Meta definitions for channels - client messsage*/ + public static final String META_CLIENT="/meta/client"; + /**Meta definitions for channels - disconnect messsage*/ + public static final String META_DISCONNECT="/meta/disconnect"; + /**Meta definitions for channels - handshake messsage*/ + public static final String META_HANDSHAKE="/meta/handshake"; + /**Meta definitions for channels - ping messsage*/ + public static final String META_PING="/meta/ping"; + /**Meta definitions for channels - reconnect messsage + * @deprecated + */ + public static final String META_RECONNECT="/meta/reconnect"; + /**Meta definitions for channels - status messsage*/ + public static final String META_STATUS="/meta/status"; + /**Meta definitions for channels - subscribe messsage*/ + public static final String META_SUBSCRIBE="/meta/subscribe"; + /**Meta definitions for channels - unsubscribe messsage*/ + public static final String META_UNSUBSCRIBE="/meta/unsubscribe"; + /*Field names inside Bayeux messages*/ + /**Field names inside Bayeux messages - clientId field*/ + public static final String CLIENT_FIELD="clientId"; + /**Field names inside Bayeux messages - data field*/ + public static final String DATA_FIELD="data"; + /**Field names inside Bayeux messages - channel field*/ + public static final String CHANNEL_FIELD="channel"; + /**Field names inside Bayeux messages - id field*/ + public static final String ID_FIELD="id"; + /**Field names inside Bayeux messages - error field*/ + public static final String ERROR_FIELD="error"; + /**Field names inside Bayeux messages - timestamp field*/ + public static final String TIMESTAMP_FIELD="timestamp"; + /**Field names inside Bayeux messages - transport field*/ + public static final String TRANSPORT_FIELD="transport"; + /**Field names inside Bayeux messages - advice field*/ + public static final String ADVICE_FIELD="advice"; + /**Field names inside Bayeux messages - successful field*/ + public static final String SUCCESSFUL_FIELD="successful"; + /**Field names inside Bayeux messages - subscription field*/ + public static final String SUBSCRIPTION_FIELD="subscription"; + /**Field names inside Bayeux messages - ext field*/ + public static final String EXT_FIELD="ext"; + /**Field names inside Bayeux messages - connectionType field*/ + public static final String CONNECTION_TYPE_FIELD="connectionType"; + /**Field names inside Bayeux messages - version field*/ + public static final String VERSION_FIELD="version"; + /**Field names inside Bayeux messages - minimumVersion field*/ + public static final String MIN_VERSION_FIELD="minimumVersion"; + /**Field names inside Bayeux messages - supportedConnectionTypes field*/ + public static final String SUPP_CONNECTION_TYPE_FIELD="supportedConnectionTypes"; + /**Field names inside Bayeux messages - json-comment-filtered field*/ + public static final String JSON_COMMENT_FILTERED_FIELD="json-comment-filtered"; + /**Field names inside Bayeux messages - reconnect field*/ + public static final String RECONNECT_FIELD = "reconnect"; + /**Field names inside Bayeux messages - interval field*/ + public static final String INTERVAL_FIELD = "interval"; + /**Field values inside Bayeux messages - retry response*/ + public static final String RETRY_RESPONSE = "retry"; + /**Field values inside Bayeux messages - handshake response*/ + public static final String HANDSHAKE_RESPONSE = "handshake"; + /**Field values inside Bayeux messages - none response*/ + public static final String NONE_RESPONSE = "none"; + /**Service channel names-starts with*/ + public static final String SERVICE="/service"; + /**Service channel names-trailing slash*/ + public static final String SERVICE_SLASH="/service/"; + /*Transport types*/ + /**Transport types - long polling*/ + public static final String TRANSPORT_LONG_POLL="long-polling"; + /**Transport types - callback polling*/ + public static final String TRANSPORT_CALLBACK_POLL="callback-polling"; + /**Transport types - iframe*/ + public static final String TRANSPORT_IFRAME="iframe"; + /**Transport types - flash*/ + public static final String TRANSPORT_FLASH="flash"; + /** ServletContext attribute name used to obtain the Bayeux object */ + public static final String DOJOX_COMETD_BAYEUX="dojox.cometd.bayeux"; + /*http field names*/ + /**http helpers - text/json content type*/ + public static final String JSON_CONTENT_TYPE="text/json"; + /**http helpers - parameter name for json message*/ + public static final String MESSAGE_PARAMETER="message"; + /**http helpers - name of the jsonp parameter*/ + public static final String JSONP_PARAMETER="jsonp"; + /**http helpers - default name of the jsonp callback function*/ + public static final String JSONP_DEFAULT_NAME="jsonpcallback"; + + /*--Client----------------------------------------------------------- */ + /** + * Creates a new server side client. This method is to be invoked + * by server side objects only. You cannot create a remote client by using this method. + * A client represents an entity that can subscribe to channels and publish and receive messages + * through these channels + * @param idprefix String - the prefix string for the id generated, can be null + * @param listener Listener - a callback object to be called when messages are to be delivered to the new client + * @return Client - returns an implementation of the client interface. + */ + public Client newClient(String idprefix, Listener listener); + + /** + * retrieve a client based on an ID. Will return null if the client doesn't exist. + * @param clientid String + * @return Client-null if the client doesn't exist.returns the client if it does. + */ + public Client getClient(String clientid); + + /** + * Returns a non modifiable list of all the clients that are currently active + * in this Bayeux session + * @return List - a list containing all clients. The List can not be modified. + */ + public List getClients(); + + /** + * Returns true if a client with the given id exists.
+ * Same as executing getClient(id)!=null. + * @param clientId String + * @return boolean - true if the client exists + */ + public boolean hasClient(String clientId); + + /** + * Removes the client all together. + * This will unsubscribe the client to any channels it may be subscribed to + * and remove it from the list. + * @param client Client + * @return Client - returns the client that was removed, or null if no client was removed. + */ + public Client remove(Client client); + + + /*--Channel---------------------------------------------------------- */ + /** + * Returns the channel for a given channel id. + * If the channel doesn't exist, and the create parameter is set to true, + * the channel will be created and added to the list of active channels.
+ * if create is set to false, and the channel doesn't exist, null will be returned. + * @param channelId String - the id of the channel to be retrieved or created + * @param create boolean - true if the Bayeux impl should create the channel + * @return Channel - null if create is set to false and the channel doesn't exist, + * otherwise it returns a channel object. + */ + public Channel getChannel(String channelId, boolean create); + + /** + * Returns a list of currently active channels in this Bayeux session. + * @return List + */ + public List getChannels(); + + /** + * Removes a channel from the Bayeux object. + * This will also unsubscribe all the clients currently subscribed to the + * the channel. + * @param channel Channel - the channel to be removed + * @return Channel - returns the channel that was removed, or null if no channel was removed. + */ + public Channel remove(Channel channel); + + /** + * returns true if a channel with the given channelId exists. + *
Same as executing Bayeux.getChannel(channelId,false)!=null + * @param channelId String + * @return boolean - true if the channel exists. + */ + public boolean hasChannel(String channelId); + + /* --Message---------------------------------------------------------- */ + /** + * Creates a new message to be sent by a server side client. + * @return Message - returns a new Message object, that has a unique id. + */ + public Message newMessage(Client from); + + + /*--Security policy----------------------------------------------------------- */ + /** + * Returns the security policy associated with this Bayeux session + * @return SecurityPolicy + */ + public SecurityPolicy getSecurityPolicy(); + + /** + * Sets the security policy to be used in this Bayeux session + * @param securityPolicy SecurityPolicy + */ + public void setSecurityPolicy(SecurityPolicy securityPolicy); + +} \ 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 index 000000000..34af32878 --- /dev/null +++ b/java/org/apache/cometd/bayeux/Channel.java @@ -0,0 +1,103 @@ +/* + * 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. + */ +package org.apache.cometd.bayeux; + +import java.util.List; + +/** + * A Bayeux Channel represents a channel used to receive messages from and to publish messages to. + * In order to publish messages to or receive messages from, one must subscribe to the channel. + * This is easily done by invoking the subscribe method. + * A channel is created by calling the Bayeux.getChannel(channelId,true) method. + * A channel can be created either server side by invoking the getChannel, or client side + * by using the /meta/subscribe message without a wildcard. + * @author Greg Wilkins + * @author Filip Hanik + */ +public interface Channel +{ + /** + * Returns the id for this channel. The id is unique within bayeux session. + * @return String - will never be null. + */ + public String getId(); + + /** + * Publishes a message to all the subscribers of this channel. + * The from is contained within the message, by calling + * msg.getClient() + * @param data - the message to be published, can not be null. + */ + public void publish(Message msg); + + /** + * Publishes more than one message to all the subscribers of this channel. + * The from is contained within the message, by calling + * msg[x].getClient() + * @param data - the message to be published, can not be null. + */ + public void publish(Message[] msgs); + + /** + * Non persistent channels are removed when the last subscription is + * removed. Persistent channels survive periods without any subscribers. + * @return true if the Channel will persist without any subscription. + */ + public boolean isPersistent(); + + /** + * @param persistent true if the Channel will persist without any subscription. + * @see isPersistent + */ + public void setPersistent(boolean persistent); + + /** + * Subscribes a client to a channel. + * @param subscriber - the client to be subscribed. If the client + * already is subscribed, this call will not create a duplicate subscription. + */ + public void subscribe(Client subscriber); + + /** + * Unsubscribes a client from a channel + * @param subscriber - the client to be subscribed. + * @return - returns the client that was unsubscribed, or null if the client wasn't subscribed. + */ + public Client unsubscribe(Client subscriber); + + /** + * returns a non modifiable list of all the subscribers to this + * channel. + * @return a list of subscribers + */ + public List getSubscribers(); + + /** + * Adds a data filter to this channel. All messages received by this channel + * will run through this filter. + * @param filter Filter + */ + public void addFilter(DataFilter filter); + + /** + * Removes a filter from this channel. + * returns the filter that was removed, or null if the filter wasn't in the channel. + * @param filter Filter + * @return Filter - null if no filter was removed otherwise it returns the filter that was removed. + */ + public DataFilter removeFilter(DataFilter filter); +} \ 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 index 000000000..3d6a8d89a --- /dev/null +++ b/java/org/apache/cometd/bayeux/Client.java @@ -0,0 +1,91 @@ +/* + * 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. + */ + +package org.apache.cometd.bayeux; + + + +/** A Bayeux Client. + *

+ * A client may subscribe to channels and publish messages to channels. + * Client instances should not be directly created by uses, but should + * be obtained via the {@link Bayeux#getClient(String)} or {@link Bayeux#newClient(String, Listener)} + * methods. + *

+ *

+ * Three types of client may be represented by this interface: + *

  • The server representation of a remote client connected via HTTP, + * automatically created by the Bayeux server when a connect message comes in
  • + *
  • A server side client, created by the application using the {@link Bayeux#newClient(String, Listener)} method
  • + *
  • A java client connected to a remote Bayeux server - not implemented
  • + * + * @author Greg Wilkins + * @author Filip Hanik + */ +public interface Client +{ + /** + * Returns a unique id for this client. The id is unique within this Bayeux session. + * @return String - will not be null + */ + public String getId(); + + /** + * Returns true if this client is holding messages to be delivered to the remote client. + * This method always returns false for local clients, since messages are delivered instantly using the + * Listener(callback) object + * @return boolean + */ + public boolean hasMessages(); + + /** + * Deliver a message to this client only + * Deliver a message directly to the client. The message is not + * filtered or published to a channel. + * @param message + */ + public void deliver(Message message); + + /** + * Deliver a batch of messages to this client only + * Deliver a batch messages directly to the client. The messages are not + * filtered or published to a channel. + * @param message + */ + public void deliver(Message[] message); + + /** + * @return True if the client is local. False if this client is either a remote HTTP client or + * a java client to a remote server. + */ + public boolean isLocal(); + + /** + * Starts a batch, no messages will be delivered until endBatch is called. + * Batches can be nested, and messages will only be delivered after + * the last endBatch has been called. + */ + public void startBatch(); + + /** + * Ends a batch. since batches can be nested, messages will only be delivered + * after the endBatch has been called as many times as startBatch has. + */ + public void endBatch(); + + +} \ 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 index 000000000..d632aec3e --- /dev/null +++ b/java/org/apache/cometd/bayeux/DataFilter.java @@ -0,0 +1,38 @@ +/* + * 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. + */ +package org.apache.cometd.bayeux; + +/** + * Data Filter
    + * Data filters are used to transform data as it is sent to a Channel. + * Messages are filtered as the message is published to a channel, invoking the + * {@link Channel#publish(Message)} method.
    + * This method gets invoked in two different scenarios, the first being when a message is received from + * a remote client, and the Bayeux implementation invokes the publish method directly. + * The second scenario is when a local client invokes {@link Channel#publish(Message)} directly in the local JVM. + * @author Greg Wilkins + * @author Filip Hanik + * + */ +public interface DataFilter +{ + /** + * Runs a message through the filter. Filtering can only modify an existing object, it can not replace it. + * @param data Message - the message to be filtered, may not be null + */ + public void filter(Message data); +} diff --git a/java/org/apache/cometd/bayeux/Listener.java b/java/org/apache/cometd/bayeux/Listener.java new file mode 100644 index 000000000..cce2018fa --- /dev/null +++ b/java/org/apache/cometd/bayeux/Listener.java @@ -0,0 +1,45 @@ +/* + * 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. + */ +package org.apache.cometd.bayeux; + +/** + * Cometd Listener interface.
    + * For local clients, in order to receive messages, they pass in a callback object + * when the local client is created using the {@link Bayeux#newClient(String,Listener)} method. + * This callback object, implementing the Listener interface, is used to deliver messages to local, in JVM, clients. + * @author Greg Wilkins + * @author Filip Hanik + * + */ +public interface Listener +{ + /** + * This method is called when the client is removed (explicitly or from a timeout) + * @param timeout - true if the client was removed from a timeout + * false if it was removed explicitly. + */ + public void removed(boolean timeout); + + /** + * Invoked when a message is delivered to the client. + * The message contains the message itself, as well as what channel this message came through + * and who the sender is. If someone invoked {@link Client#deliver(Message)} then the channel reference will + * be null. + * @param msg + */ + public void deliver(Message[] msg); +} diff --git a/java/org/apache/cometd/bayeux/Message.java b/java/org/apache/cometd/bayeux/Message.java new file mode 100644 index 000000000..26ba64084 --- /dev/null +++ b/java/org/apache/cometd/bayeux/Message.java @@ -0,0 +1,68 @@ +/* + * 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. + */ +package org.apache.cometd.bayeux; + +import java.util.Map; + +/** + * A Bayeux Message
    + * A Bayeux message is a Map of String/Object key value pairs representing the data in the message. + * The message contains information about the channel it was published through and who the sender was + * + * @author Greg Wilkins + * @author Filip Hanik + */ +public interface Message extends Map +{ + /** + * Returns a reference to the client that sent this message + * @return Client - may be null + */ + public Client getClient(); + /** + * Returns a reference to the channel that this message was published throuhg + * @return Channel - may be null + */ + public Channel getChannel(); + /** + * Returns the unique id of this message + * @return String + */ + public String getId(); + + /** + * Sets the time to live in milliseconds. If the message hasn't been delivered + * when the time passed after the creation time is longer than the TTL the message will + * expire and removed from any delivery queues. + * @param ttl long + */ + public void setTTL(long ttl); + + /** + * Returns the time to live (in milliseconds) for this message + * @return long + */ + public long getTTL(); + + /** + * returns the timestamp in milliseconds(System.currentTimeMillis()) of when this message was created. + * @return long + */ + public long getCreationTime(); +} + + diff --git a/java/org/apache/cometd/bayeux/SecurityPolicy.java b/java/org/apache/cometd/bayeux/SecurityPolicy.java new file mode 100644 index 000000000..930f605ea --- /dev/null +++ b/java/org/apache/cometd/bayeux/SecurityPolicy.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +package org.apache.cometd.bayeux; + +/** + * @author Greg Wilkins + */ +public interface SecurityPolicy +{ + boolean canHandshake(Message message); + boolean canCreate(Client client,String channel,Message message); + boolean canSubscribe(Client client,String channel,Message messsage); + boolean canPublish(Client client,String channel,Message messsage); +} diff --git a/java/org/apache/tomcat/bayeux/BayeuxException.java b/java/org/apache/tomcat/bayeux/BayeuxException.java new file mode 100644 index 000000000..d201c5eba --- /dev/null +++ b/java/org/apache/tomcat/bayeux/BayeuxException.java @@ -0,0 +1,39 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; +/** + * + * @author Filip Hanik + * @version 1.0 + */ +public class BayeuxException extends Exception { + public BayeuxException() { + super(); + } + + public BayeuxException(String message) { + super(message); + } + + public BayeuxException(String message, Throwable cause) { + super(message, cause); + } + + public BayeuxException(Throwable cause) { + super(cause); + } +} \ 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 index 000000000..6e93c9921 --- /dev/null +++ b/java/org/apache/tomcat/bayeux/BayeuxRequest.java @@ -0,0 +1,54 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; + +import org.apache.tomcat.bayeux.HttpError; + +/** + * An interface that defines methods for managing Bayeux request meta + * messages. + * + * @author Guy A. Molinari + * @author Filip Hanik + * @version 0.9 + */ +public interface BayeuxRequest { + + public static final String LAST_REQ_ATTR = "org.apache.cometd.bayeux.last_request"; + public static final String CURRENT_REQ_ATTR = "org.apache.cometd.bayeux.current_request"; + public static final String JSON_MSG_ARRAY = "org.apache.cometd.bayeux.json_msg_array"; + + /** + * Validates a specific request. + * This method must be called prior to process() + * as a request can do pre processing in the validate method. + *
    + * Should the validation fail, an error object is returned + * containing an error message, and potentially a stack trace + * if an exception was generated + * @return HttpError - null if no error was detected, an HttpError object containing information about the error. + */ + public HttpError validate(); + + /** + * processes a remote client Bayeux message + * @param prevops - the operation requested by the previous request, in case of chained requests. + * @return int - returns the interest operation for a CometEvent. Currently not used + * @throws BayeuxException - if an error was detected, and the appropriate error response couldn't be delivered to the client. + */ + public int process(int prevops) throws BayeuxException; +} diff --git a/java/org/apache/tomcat/bayeux/BayeuxServlet.java b/java/org/apache/tomcat/bayeux/BayeuxServlet.java new file mode 100644 index 000000000..c230336cc --- /dev/null +++ b/java/org/apache/tomcat/bayeux/BayeuxServlet.java @@ -0,0 +1,236 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; + +import java.io.IOException; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.CometEvent; +import org.apache.catalina.CometProcessor; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Bayeux; + +/** + * + * @author Filip Hanik + * @author Guy Molinari + * @version 1.0 + */ +public class BayeuxServlet implements CometProcessor { + + /** + * Attribute to hold the TomcatBayeux object in the servlet context + */ + public static final String TOMCAT_BAYEUX_ATTR = Bayeux.DOJOX_COMETD_BAYEUX; + + /** + * Logger object + */ + protected static Log log = LogFactory.getLog(BayeuxServlet.class); + + /** + * Servlet config - for future use + */ + protected ServletConfig servletConfig; + + /** + * Reference to the global TomcatBayeux object + */ + protected TomcatBayeux tb; + + /** + * Upon servlet destruction, the servlet will clean up the + * TomcatBayeux object and terminate any outstanding events. + */ + public void destroy() { + servletConfig = null; + //to do, close all outstanding comet events + //tb.destroy(); + tb = null;//TO DO, close everything down + + } + + /** + * Returns the preconfigured connection timeout. + * If no timeout has been configured as a servlet init parameter named timeout + * then the default of 2min will be used. + * @return int - the timeout for a connection in milliseconds + */ + protected int getTimeout() { + String timeoutS = servletConfig.getInitParameter("timeout"); + int timeout = 120*1000; //2 min + try { + timeout = Integer.parseInt(timeoutS); + }catch (NumberFormatException nfe) { + //ignore, we have a default value + } + return timeout; + } + + protected int getReconnectInterval() { + String rs = servletConfig.getInitParameter("reconnectInterval"); + int rct = 5000; //5 seconds + try { + rct = Integer.parseInt(rs); + }catch (NumberFormatException nfe) { + //ignore, we have a default value + } + return rct; + } + + + public void event(CometEvent cometEvent) throws IOException, ServletException { + CometEvent.EventType type = cometEvent.getEventType(); + if (log.isDebugEnabled()) { + log.debug("["+Thread.currentThread().getName()+"] Received Comet Event type="+type+" subtype:"+cometEvent.getEventSubType()); + } + synchronized (cometEvent) { + if (type==CometEvent.EventType.BEGIN) { + //begin event, set the timeout + cometEvent.setTimeout(getTimeout()); + //checkBayeux(cometEvent); - READ event should always come + } else if (type==CometEvent.EventType.READ) { + checkBayeux(cometEvent); + } else if (type==CometEvent.EventType.ERROR) { + tb.remove(cometEvent); + cometEvent.close(); + } else if (type==CometEvent.EventType.END) { + tb.remove(cometEvent); + cometEvent.close(); + }//end if + + }//synchronized + }//event + + /** + * + * @param cometEvent CometEvent + * @return boolean - true if we comet event stays open + * @throws IOException + * @throws UnsupportedOperationException + */ + protected void checkBayeux(CometEvent cometEvent) throws IOException, UnsupportedOperationException { + //we actually have data. + //data can be text/json or + if (Bayeux.JSON_CONTENT_TYPE.equals(cometEvent.getHttpServletRequest().getContentType())) { + //read and decode the bytes according to content length + log.warn("["+Thread.currentThread().getName()+"] JSON encoding not supported, will throw an exception and abort the request."); + int contentlength = cometEvent.getHttpServletRequest().getContentLength(); + throw new UnsupportedOperationException("Decoding "+Bayeux.JSON_CONTENT_TYPE+" not yet implemented."); + } else { //GET method or application/x-www-form-urlencoded + String message = cometEvent.getHttpServletRequest().getParameter(Bayeux.MESSAGE_PARAMETER); + if (log.isTraceEnabled()) { + log.trace("["+Thread.currentThread().getName()+"] Received JSON message:"+message); + } + try { + int action = handleBayeux(message, cometEvent); + if (log.isDebugEnabled()) { + log.debug("["+Thread.currentThread().getName()+"] Bayeux handling complete, action result="+action); + } + if (action<=0) { + cometEvent.close(); + } + }catch (Exception x) { + x.printStackTrace(); + tb.remove(cometEvent); + log.error(x); + cometEvent.close(); + } + } + } + + protected int handleBayeux(String message, CometEvent event) throws IOException, ServletException { + int result = 0; + if (message==null || message.length()==0) return result; + try { + BayeuxRequest request = null; + //a message can be an array of messages + JSONArray jsArray = new JSONArray(message); + for (int i = 0; i < jsArray.length(); i++) { + JSONObject msg = jsArray.getJSONObject(i); + + if (log.isDebugEnabled()) { + log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message:"+msg); + } + request = RequestFactory.getRequest(tb,event,msg); + if (log.isDebugEnabled()) { + log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message using request:"+request); + } + result = request.process(result); + if (log.isDebugEnabled()) { + log.debug("["+Thread.currentThread().getName()+"] Processing bayeux message result:"+result); + } + } + if (result>0 && request!=null) { + event.getHttpServletRequest().setAttribute(BayeuxRequest.LAST_REQ_ATTR, request); + ClientImpl ci = (ClientImpl)tb.getClient(((RequestBase)request).getClientId()); + ci.addCometEvent(event); + if (log.isDebugEnabled()) { + log.debug("["+Thread.currentThread().getName()+"] Done bayeux message added to request attribute"); + } + } else if (result == 0 && request!=null) { + RequestBase.deliver(event,(ClientImpl)tb.getClient(((RequestBase)request).getClientId())); + if (log.isDebugEnabled()) { + log.debug("["+Thread.currentThread().getName()+"] Done bayeux message, delivered to client"); + } + } + + }catch (JSONException x) { + log.error(x);//to do impl error handling + result = -1; + }catch (BayeuxException x) { + log.error(x); //to do impl error handling + result = -1; + } + return result; + } + + public ServletConfig getServletConfig() { + return servletConfig; + } + + public String getServletInfo() { + return "Tomcat/BayeuxServlet/1.0"; + } + + public void init(ServletConfig servletConfig) throws ServletException { + + this.servletConfig = servletConfig; + ServletContext ctx = servletConfig.getServletContext(); + if (ctx.getAttribute(TOMCAT_BAYEUX_ATTR)==null) + ctx.setAttribute(TOMCAT_BAYEUX_ATTR,new TomcatBayeux()); + this.tb = (TomcatBayeux)ctx.getAttribute(TOMCAT_BAYEUX_ATTR); + tb.setReconnectInterval(getReconnectInterval()); + } + + public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { + if (servletResponse instanceof HttpServletResponse) { + ( (HttpServletResponse) servletResponse).sendError(500, "Misconfigured Tomcat server, must be configured to support Comet operations."); + } else { + throw new ServletException("Misconfigured Tomcat server, must be configured to support Comet operations for the Bayeux protocol."); + } + } +} diff --git a/java/org/apache/tomcat/bayeux/ChannelImpl.java b/java/org/apache/tomcat/bayeux/ChannelImpl.java new file mode 100644 index 000000000..e1e08a8a0 --- /dev/null +++ b/java/org/apache/tomcat/bayeux/ChannelImpl.java @@ -0,0 +1,189 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; + +import java.util.LinkedList; + +import org.apache.cometd.bayeux.Channel; +import org.apache.cometd.bayeux.Client; +import org.apache.cometd.bayeux.DataFilter; +import java.util.Collections; +import java.util.List; +import org.apache.cometd.bayeux.Message; +import java.util.Iterator; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +/** + * + * @author Filip Hanik + * @version 1.0 + */ +public class ChannelImpl implements Channel { + + protected static Log log = LogFactory.getLog(ChannelImpl.class); + + /** + * The unique id of this channel + */ + protected String id = null; + + /** + * A list of the current subscribers + */ + protected LinkedList subscribers = new LinkedList(); + + /** + * A list of the current filters + */ + protected LinkedList filters = new LinkedList(); + + /** + * Is this channel persistent, default value is true + */ + protected boolean persistent = true; + + /** + * Creates a new channel + * @param id String - the id of the channel, can not be null + */ + protected ChannelImpl(String id) { + assert id != null; + this.id = id; + } + + /** + * returns the id of this channel + * @return String + */ + public String getId() { + return id; + } + + /** + * Returns true if this channel matches the pattern to its id. + * The channel pattern can be a complete name like /service/mychannel + * or it can be a wild card pattern like /service/app2/** + * @param pattern String according to the Bayeux specification section 2.2.1 Channel Globbing, can not be null. + * @return boolean true if the id of this channel matches the pattern + */ + public boolean matches(String pattern) { + if (pattern == null) + throw new NullPointerException("Channel pattern must not be null."); + if (getId().equals(pattern)) + return true; + int wildcardPos = pattern.indexOf("/*"); + if (wildcardPos == -1) + return false; + boolean multiSegment = pattern.indexOf("**") != -1; + String leadSubstring = pattern.substring(0, wildcardPos); + if (leadSubstring == null) + return false; + if (multiSegment) + return getId().startsWith(leadSubstring); + else { + if (getId().length() <= wildcardPos + 2) + return false; + return !(getId().substring(wildcardPos + 2).contains("/")); + } + } + + + + /** + * @return returns a non modifiable list of the subscribers for this channel. + */ + public List getSubscribers() { + return Collections.unmodifiableList(subscribers); + } + + /** + * @return true if the Channel will persist without any subscription. + */ + public boolean isPersistent() { + return persistent; + } + + public void publish(Message msg) { + publish(new Message[] {msg}); + } + + public void publish(Message[] msgs) { + if (msgs==null) return; + MessageImpl[] imsgs = new MessageImpl[msgs.length]; + for (int i=0; msgs!=null && i it = filters.iterator(); it.hasNext(); ) { + it.next().filter(msg); + } + imsgs[i] = msg; + } + //deliver it to the clients + for (Iterator it = subscribers.iterator(); it.hasNext(); ) { + ClientImpl c = (ClientImpl)it.next(); + c.deliverInternal(this,imsgs); + } + + } + + public void setPersistent(boolean persistent) { + this.persistent = persistent; + } + + public void subscribe(Client subscriber) { + if (!subscribers.contains((subscriber))) { + subscribers.addLast(subscriber); + ((ClientImpl)subscriber).subscribed(this); + } + } + + public Client unsubscribe(Client subscriber) { + if (subscribers.remove(subscriber)) { + ((ClientImpl)subscriber).unsubscribed(this); + return subscriber; + } else + return null; + } + + public void addFilter(DataFilter filter) { + if (!filters.contains(filter)) + filters.addLast(filter); + } + + public DataFilter removeFilter(DataFilter filter) { + if ( filters.remove(filter) ) return filter; + else return null; + } + + public String toString() { + StringBuffer buf = new StringBuffer(super.toString()); + buf.append("; channelId=").append(getId()); + return buf.toString(); + } + +} \ 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 index 000000000..f1ab929be --- /dev/null +++ b/java/org/apache/tomcat/bayeux/ClientImpl.java @@ -0,0 +1,278 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.apache.catalina.CometEvent; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Bayeux; +import org.apache.cometd.bayeux.Client; +import org.apache.cometd.bayeux.Listener; +import org.apache.cometd.bayeux.Message; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.HashMap; +import java.util.ArrayList; + +public class ClientImpl implements Client { + + public static final int SUPPORT_CALLBACK_POLL = 0x1; + public static final int SUPPORT_LONG_POLL = 0x2; + + public static final String COMET_EVENT_ATTR = "org.apache.cometd.bayeux.client"; + + protected static Log log = LogFactory.getLog(ClientImpl.class); + + protected static LinkedList EMPTY_LIST = new LinkedList(); + /** + * queued message for remote clients. + */ + protected LinkedList messages = null; + + /** + * + */ + protected Queue events = new LinkedList(); + + /** + * Unique id representing this client + */ + protected String id; + + /** + * supported connection types, defaults to long-polling + */ + protected int supportedConnTypes = SUPPORT_LONG_POLL | SUPPORT_CALLBACK_POLL; + + /** + * The desired connection type + */ + protected int desirectConnType = SUPPORT_LONG_POLL; + + /** + * Does this client use json-comment-filtered messages + */ + protected boolean useJsonFiltered = false; + + /** + * Same JVM clients, get local=true + */ + protected boolean local; + + /** + * The callback object for local clients + */ + protected Listener listener; + + protected AtomicInteger nrofsubscriptions = new AtomicInteger(0); + + protected ClientImpl(String id, boolean local) { + this.id = id; + this.local = local; + if (!local) messages = new LinkedList(); + } + + protected ClientImpl(String id, CometEvent event) { + this(id,false); + events = new ConcurrentLinkedQueue(); + addCometEvent(event); + } + + public synchronized void deliver(Message message) { + deliverInternal(null,new MessageImpl[] {(MessageImpl)message}); + } + + public synchronized void deliver(Message[] message) { + deliverInternal(null,message); + } + + protected synchronized void deliverInternal(ChannelImpl channel, MessageImpl message) { + deliverInternal(channel,new MessageImpl[] {message}); + } + + protected synchronized void deliverInternal(ChannelImpl channel, Message[] msgs) { + if (isLocal()) { + //local clients must have a listener + ArrayList list = new ArrayList(); + for (int i=0; msgs!=null && i 0; + } + } + + public boolean isLocal() { + return local; + } + + public int getSupportedConnTypes() { + return supportedConnTypes; + } + + public int getDesirectConnType() { + return desirectConnType; + } + + public boolean useJsonFiltered() { + return useJsonFiltered; + } + + public void setListener(Listener listener) { + this.listener = listener; + } + + public void setSupportedConnTypes(int supportedConnTypes) { + this.supportedConnTypes = supportedConnTypes; + } + + public void setUseJsonFiltered(boolean useJsonFiltered) { + this.useJsonFiltered = useJsonFiltered; + } + + public void setDesirectConnType(int desirectConnType) { + this.desirectConnType = desirectConnType; + } + + public boolean supportsCallbackPoll() { + return (supportedConnTypes & SUPPORT_CALLBACK_POLL) == SUPPORT_CALLBACK_POLL; + } + + public boolean supportsLongPoll() { + return (supportedConnTypes & SUPPORT_LONG_POLL) == SUPPORT_LONG_POLL; + } + + public synchronized List takeMessages() { + if (isLocal()) return null; + if (messages.size()==0) return EMPTY_LIST; + List result = new LinkedList(messages); + messages.clear(); + return result; + } + + public String toString() { + StringBuffer buf = new StringBuffer(super.toString()); + buf.append(" id=").append(getId()); + return buf.toString(); + } + + public boolean isSubscribed() { + return nrofsubscriptions.get()>0; + } + + protected synchronized boolean addCometEvent(CometEvent event) { + boolean result = false; + if (!events.contains(event)) { + events.add(event); + result = true; + } + event.getHttpServletRequest().setAttribute(COMET_EVENT_ATTR,this); + return result; + } + + protected synchronized boolean removeCometEvent(CometEvent event) { + boolean result = events.remove(event); + event.getHttpServletRequest().removeAttribute(COMET_EVENT_ATTR); + return result; + } + + + protected void subscribed(ChannelImpl ch) { + nrofsubscriptions.addAndGet(1); + } + + protected void unsubscribed(ChannelImpl ch) { + nrofsubscriptions.addAndGet(-1); + } + + public void startBatch(){ + //noop until improved + } + public void endBatch() { + //noop until improved + } + +} diff --git a/java/org/apache/tomcat/bayeux/HttpError.java b/java/org/apache/tomcat/bayeux/HttpError.java new file mode 100644 index 000000000..55ff2c75f --- /dev/null +++ b/java/org/apache/tomcat/bayeux/HttpError.java @@ -0,0 +1,43 @@ +package org.apache.tomcat.bayeux; + +public class HttpError { + private int code; + private String status; + private Throwable cause; + public HttpError(int code, String status, Throwable cause) { + this.code = code; + this.status = status; + this.cause = cause; + } + + public void setCode(int code) { + this.code = code; + } + + public void setStatus(String status) { + this.status = status; + } + + public void setCause(Throwable exception) { + this.cause = exception; + } + + public int getCode() { + return code; + } + + public String getStatus() { + return status; + } + + public Throwable getCause() { + return cause; + } + + public String toString() { + if (cause != null) + return code + ":" + status + " - [" + cause + "]"; + else + return code + ":" + status; + } +} diff --git a/java/org/apache/tomcat/bayeux/MessageImpl.java b/java/org/apache/tomcat/bayeux/MessageImpl.java new file mode 100644 index 000000000..53d5342d2 --- /dev/null +++ b/java/org/apache/tomcat/bayeux/MessageImpl.java @@ -0,0 +1,80 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; + +import java.util.HashMap; + +import org.apache.cometd.bayeux.Channel; +import org.apache.cometd.bayeux.Client; +import org.apache.cometd.bayeux.Message; + +public class MessageImpl extends HashMap implements Message { + + protected Channel channel; + protected Client client; + protected String id; + private long TTL = 1000*60*5; //5min is the default TTL for a message + protected long creationTime = System.currentTimeMillis(); + + public Object clone() { + MessageImpl copy = new MessageImpl(id); + copy.putAll(this); + copy.channel = channel; + copy.client = client; + copy.id = id; + copy.creationTime = creationTime; + copy.TTL = TTL; + return copy; + } + + protected MessageImpl(String id) { + assert id != null; + this.id = id; + } + + public Channel getChannel() { + return channel; + } + + public Client getClient() { + return client; + } + + public long getCreationTime() { + return creationTime; + } + + public long getTTL() { + return TTL; + } + + public String getId() { + return id; + } + + protected void setChannel(Channel channel) { + this.channel = channel; + } + + protected void setClient(Client client) { + this.client = client; + } + + public void setTTL(long TTL) { + this.TTL = TTL; + } +} \ 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 index 000000000..19df7fc1c --- /dev/null +++ b/java/org/apache/tomcat/bayeux/RequestBase.java @@ -0,0 +1,251 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; +import java.util.Date; +import java.text.SimpleDateFormat; +import javax.servlet.ServletException; + +import org.apache.catalina.CometEvent; +import org.apache.tomcat.bayeux.HttpError; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Bayeux; +import org.apache.cometd.bayeux.Message; + +/** + * Common functionality and member variables for all Bayeux requests. + * + * @author Guy A. Molinari + * @author Filip Hanik + * @version 0.9 + * + */ +public abstract class RequestBase implements BayeuxRequest { + + protected static final SimpleDateFormat timestampFmt = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + static { + timestampFmt.setTimeZone(TimeZone.getTimeZone("GMT")); + } + //message properties, combined for all messages + protected TomcatBayeux tomcatBayeux; + protected String channel; + protected String id; + protected String clientId; + protected String version = null; + protected String[] suppConnTypes = null; + protected int suppConnTypesFlag = 0; + protected int desiredConnTypeFlag = 0; + protected String minVersion = null; + protected String subscription = null; + protected String data = null; + protected String conType = null; + protected LinkedHashMap ext = new LinkedHashMap (); + + + protected CometEvent event; + + protected HashMap response = null; + + protected static Log log = LogFactory.getLog(RequestBase.class); + + protected int reconnectInterval; + + protected RequestBase(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException { + this.tomcatBayeux = tb; + this.event = event; + channel = jsReq.optString(Bayeux.CHANNEL_FIELD); + id = jsReq.optString(Bayeux.ID_FIELD); + clientId = jsReq.optString(Bayeux.CLIENT_FIELD); + version = jsReq.optString(Bayeux.VERSION_FIELD); + minVersion = jsReq.optString(Bayeux.MIN_VERSION_FIELD); + conType = jsReq.optString(Bayeux.CONNECTION_TYPE_FIELD); + subscription = jsReq.optString(Bayeux.SUBSCRIPTION_FIELD); + data = jsReq.optString(Bayeux.DATA_FIELD); + reconnectInterval = tb.getReconnectInterval(); + if (jsReq.has(Bayeux.EXT_FIELD)) { + JSONObject jext = jsReq.getJSONObject(Bayeux.EXT_FIELD); + for (Iterator i = jext.keys(); i.hasNext(); ) { + String key = i.next(); + ext.put(key, jext.get(key)); + }//for + }//end if + + if (jsReq.has(Bayeux.SUPP_CONNECTION_TYPE_FIELD)) { + JSONArray types = jsReq.getJSONArray(Bayeux.SUPP_CONNECTION_TYPE_FIELD); + suppConnTypes = new String[types.length()]; + for (int i = 0; i < types.length(); i++) { + suppConnTypes[i] = types.getString(i); + if (Bayeux.TRANSPORT_CALLBACK_POLL.equals(suppConnTypes[i])) + suppConnTypesFlag = suppConnTypesFlag|ClientImpl.SUPPORT_CALLBACK_POLL; + else if (Bayeux.TRANSPORT_LONG_POLL.equals(suppConnTypes[i])) + suppConnTypesFlag = suppConnTypesFlag|ClientImpl.SUPPORT_LONG_POLL; + }//for + }//end if + + if (conType!=null) { + if (Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType)) + desiredConnTypeFlag = ClientImpl.SUPPORT_CALLBACK_POLL; + else if (Bayeux.TRANSPORT_LONG_POLL.equals(conType)) + desiredConnTypeFlag = ClientImpl.SUPPORT_LONG_POLL; + }//end if + + //due to the fact that the javascript doesn't send up a required field + //we have to fake it + suppConnTypesFlag = ClientImpl.SUPPORT_CALLBACK_POLL | ClientImpl.SUPPORT_LONG_POLL; + + } + + public HttpError validate() { + HttpError result = null; +// if (clientId == null) { +// result = new HttpError(401,"No Client ID.", null); +// } + return result; + } + + public TomcatBayeux getTomcatBayeux() { + return tomcatBayeux; + } + + public String getChannel() { + return channel; + } + + public String getId() { + return id; + } + + public String getClientId() { + return clientId; + } + + public LinkedHashMap getExt() { + return ext; + } + + public CometEvent getEvent() { + return event; + } + + protected static void deliver(CometEvent event, ClientImpl to) throws IOException, ServletException, BayeuxException { + JSONArray jarray = getJSONArray(event,true); + if ( jarray == null ) throw new BayeuxException("No message to send!"); + String jsonstring = jarray.toString(); + if (log.isDebugEnabled()) { + log.debug("["+Thread.currentThread().getName()+"] Delivering message to[" + to + "] message:" + jsonstring); + } + + PrintWriter out = event.getHttpServletResponse().getWriter(); + if (to==null) { + //do nothing + }else if ( (to.getDesirectConnType() == 0 && to.supportsLongPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_LONG_POLL) { + if (to.useJsonFiltered()) + out.print("/*"); + } else if ( (to.getDesirectConnType() == 0 && to.supportsCallbackPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_CALLBACK_POLL) { + String jsonp = event.getHttpServletRequest().getParameter(Bayeux.JSONP_PARAMETER); + if (jsonp == null) + jsonp = Bayeux.JSONP_DEFAULT_NAME; + out.print(jsonp); + out.print('('); + } else { + throw new BayeuxException("Client doesn't support any appropriate connection type."); + } + out.print(jsonstring); + if ( to == null ) { + //do nothing + } else if ( (to.getDesirectConnType() == 0 && to.supportsLongPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_LONG_POLL) { + if (to.useJsonFiltered()) + out.print("*/"); + } else if ( (to.getDesirectConnType() == 0 && to.supportsCallbackPoll()) || to.getDesirectConnType() == ClientImpl.SUPPORT_CALLBACK_POLL) { + out.print(");"); + } + out.flush(); + event.getHttpServletResponse().flushBuffer(); + + + } + + protected static JSONArray getJSONArray(CometEvent event, boolean nullok) { + synchronized(event) { + JSONArray jarray = (JSONArray) event.getHttpServletRequest().getAttribute(JSON_MSG_ARRAY); + if (jarray == null && (!nullok)) { + jarray = new JSONArray(); + event.getHttpServletRequest().setAttribute(JSON_MSG_ARRAY, jarray); + } + return jarray; + } + } + + protected JSONArray getJSONArray() { + return getJSONArray(event,false); + } + + protected void addToDeliveryQueue(ClientImpl to, JSONObject msg) throws IOException, ServletException, BayeuxException { + synchronized (event) { + getJSONArray().put(msg); + } + } + + protected void flushMessages(ClientImpl client) throws BayeuxException { + List msgs = client.takeMessages(); + synchronized (event) { + try { + for (Iterator it = msgs.iterator(); it.hasNext(); ){ + MessageImpl msg = (MessageImpl)it.next(); + Map map = new HashMap(); + map.put(Bayeux.CHANNEL_FIELD,msg.getChannel().getId()); + if (msg.getClient()!=null) map.put(Bayeux.CLIENT_FIELD,msg.getClient().getId()); + map.put(Bayeux.DATA_FIELD,msg); + JSONObject obj = new JSONObject(map); + addToDeliveryQueue(client, obj); + } + } catch (ServletException x) { + throw new BayeuxException(x); + } catch (IOException x) { + throw new BayeuxException(x); + } + } + } + + public int process(int prevops) throws BayeuxException { + event.getHttpServletRequest().setAttribute(CURRENT_REQ_ATTR,this); + return prevops; + } + + public int getReconnectInterval() { + return reconnectInterval; + } + + public String getTimeStamp() { + return timestampFmt.format(new Date(System.currentTimeMillis())); + } + +} diff --git a/java/org/apache/tomcat/bayeux/RequestFactory.java b/java/org/apache/tomcat/bayeux/RequestFactory.java new file mode 100644 index 000000000..f2765e661 --- /dev/null +++ b/java/org/apache/tomcat/bayeux/RequestFactory.java @@ -0,0 +1,48 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; + +import org.json.JSONObject; +import org.apache.tomcat.bayeux.request.MetaHandshakeRequest; +import org.apache.catalina.CometEvent; +import org.json.JSONException; +import org.apache.tomcat.bayeux.request.MetaConnectRequest; +import org.apache.tomcat.bayeux.request.MetaDisconnectRequest; +import org.apache.tomcat.bayeux.request.MetaSubscribeRequest; +import org.apache.tomcat.bayeux.request.MetaUnsubscribeRequest; +import org.apache.tomcat.bayeux.request.PublishRequest; +import org.apache.cometd.bayeux.Bayeux; + +public class RequestFactory { + + public static BayeuxRequest getRequest(TomcatBayeux tomcatBayeux, CometEvent event, JSONObject msg) throws JSONException { + String channel = msg.optString(Bayeux.CHANNEL_FIELD); + if (Bayeux.META_HANDSHAKE.equals(channel)) { + return new MetaHandshakeRequest(tomcatBayeux,event,msg); + }else if (Bayeux.META_CONNECT.equals(channel)) { + return new MetaConnectRequest(tomcatBayeux,event,msg); + }else if (Bayeux.META_DISCONNECT.equals(channel)) { + return new MetaDisconnectRequest(tomcatBayeux,event,msg); + }else if (Bayeux.META_SUBSCRIBE.equals(channel)) { + return new MetaSubscribeRequest(tomcatBayeux,event,msg); + }else if (Bayeux.META_UNSUBSCRIBE.equals(channel)) { + return new MetaUnsubscribeRequest(tomcatBayeux,event,msg); + } else { + return new PublishRequest(tomcatBayeux,event,msg); + } + } +} \ 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 index 000000000..efe226482 --- /dev/null +++ b/java/org/apache/tomcat/bayeux/TomcatBayeux.java @@ -0,0 +1,176 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +import org.apache.catalina.CometEvent; +import org.apache.catalina.tribes.util.Arrays; +import org.apache.catalina.tribes.util.UUIDGenerator; +import org.apache.cometd.bayeux.Bayeux; +import org.apache.cometd.bayeux.Channel; +import org.apache.cometd.bayeux.Client; +import org.apache.cometd.bayeux.Listener; +import org.apache.cometd.bayeux.Message; +import org.apache.cometd.bayeux.SecurityPolicy; +/** + * + * @author Filip Hanik + * @version 1.0 + */ +public class TomcatBayeux implements Bayeux { + + + protected int reconnectInterval = 5000; + /** + * a list of all active clients + */ + protected HashMap clients = new HashMap(); + + /** + * a list of all active channels + */ + protected LinkedHashMap channels = new LinkedHashMap(); + + /** + * security policy to be used. + */ + protected SecurityPolicy securityPolicy = null; + /** + * default client to use when we need to send an error message but don't have a client valid reference + */ + protected static ClientImpl errorClient = new ClientImpl("error-no-client",false); + + /** + * returns the default error client + * @return ClientImpl + */ + public static ClientImpl getErrorClient() { + return errorClient; + } + + protected TomcatBayeux() { + } + + /** + * should be invoked when the servlet is destroyed or when the context shuts down + */ + public void destroy() { + throw new UnsupportedOperationException("TomcatBayeux.destroy() not yet implemented"); + } + + public Channel getChannel(String channelId, boolean create) { + Channel result = channels.get(channelId); + if (result==null && create) { + result = new ChannelImpl(channelId); + channels.put(channelId,result); + } + return result; + } + + public Channel remove(Channel channel) { + return channels.remove(channel.getId()); + } + + public Client remove(Client client) { + if (client==null) return null; + for (Channel ch : getChannels()) { + ch.unsubscribe(client); + } + return clients.remove(client.getId()); + } + + public Client getClient(String clientId) { + return clients.get(clientId); + } + + public boolean hasClient(String clientId) { + return clients.containsKey(clientId); + } + + public List getClients() { + return java.util.Arrays.asList(clients.entrySet().toArray(new Client[0])); + } + + public SecurityPolicy getSecurityPolicy() { + return securityPolicy; + } + + public int getReconnectInterval() { + return reconnectInterval; + } + + public boolean hasChannel(String channel) { + return channels.containsKey(channel); + } + + public Client newClient(String idprefix, Listener listener, boolean local, CometEvent event) { + String id = createUUID(idprefix); + ClientImpl client = new ClientImpl(id, local); + client.setListener(listener); + clients.put(id, client); + return client; + } + + public Client newClient(String idprefix, Listener listener) { + assert listener!=null; + //if this method gets called, someone is using the API inside + //the JVM, this is a local client + return newClient(idprefix,listener,true, null); + } + + protected ClientImpl getClientImpl(CometEvent event) { + return (ClientImpl)event.getHttpServletRequest().getAttribute(ClientImpl.COMET_EVENT_ATTR); + } + + protected void remove(CometEvent event) { + ClientImpl client = getClientImpl(event); + if (client!=null) { + client.removeCometEvent(event); + } + } + + public String createUUID(String idprefix) { + if (idprefix==null) idprefix=""; + return idprefix + Arrays.toString(UUIDGenerator.randomUUID(false)); + } + + public List getChannels() { + return java.util.Arrays.asList(channels.entrySet().toArray(new Channel[0])); + } + + protected Message newMessage() { + String id = createUUID("msg-"); + return new MessageImpl(id); + } + + public Message newMessage(Client from) { + MessageImpl msg = (MessageImpl)newMessage(); + msg.setClient(from); + return msg; + } + public void setSecurityPolicy(SecurityPolicy securityPolicy) { + this.securityPolicy = securityPolicy; + } + + public void setReconnectInterval(int reconnectTimeout) { + this.reconnectInterval = reconnectTimeout; + } + +} diff --git a/java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java b/java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java new file mode 100644 index 000000000..f052e736f --- /dev/null +++ b/java/org/apache/tomcat/bayeux/request/MetaConnectRequest.java @@ -0,0 +1,125 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux.request; + +import java.io.IOException; +import java.util.HashMap; +import javax.servlet.ServletException; + +import org.apache.catalina.CometEvent; +import org.apache.tomcat.bayeux.HttpError; +import org.apache.tomcat.bayeux.BayeuxException; +import org.apache.tomcat.bayeux.BayeuxRequest; +import org.apache.tomcat.bayeux.ClientImpl; +import org.apache.tomcat.bayeux.TomcatBayeux; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Bayeux; +import org.apache.tomcat.bayeux.*; + +/****************************************************************************** + * Handshake request Bayeux message. + * + * @author Guy A. Molinari + * @author Filip Hanik + * @version 1.0 + * + */ +public class MetaConnectRequest extends RequestBase implements BayeuxRequest { + protected static HashMap responseTemplate = new HashMap(); + + static { + responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_CONNECT); + responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE); + responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap()); + } + + public MetaConnectRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException { + super(tb, event, jsReq); + if (clientId!=null && getTomcatBayeux().hasClient(clientId)) { + event.getHttpServletRequest().setAttribute("client",getTomcatBayeux().getClient(clientId)); + } + } + + + /** + * Check client request for validity. + * + * Per section 4.2.1 of the Bayuex spec a connect request must contain: + * 1) The "/meta/connect" channel identifier. + * 2) The clientId returned by the server after handshake. + * 3) The desired connectionType (must be one of the server's supported + * types returned by handshake response. + * + * @return HttpError This method returns null if no errors were found + */ + public HttpError validate() { + if(clientId==null|| (!getTomcatBayeux().hasClient(clientId))) + return new HttpError(400,"Client Id not valid.", null); + if (! (Bayeux.TRANSPORT_LONG_POLL.equals(conType) || Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType))) + return new HttpError(400,"Unsupported connection type.",null); + return null;//no error + } + + /** + * Transition to connected state, flushing pending messages if + * available. If there are pending subscriptions and no messages to + * flush then the connection is held until there is a pending publish + * event to be delivered to this client (Section 4.2.2 of spec). + */ + public int process(int prevops) throws BayeuxException { + prevops = super.process(prevops); + response = (HashMap)responseTemplate.clone(); + ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId); + boolean success = false; + HttpError error = validate(); + if (error == null) { + client.setDesirectConnType(desiredConnTypeFlag); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval()); + success = true; + }else { + response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE); + response.put(Bayeux.ERROR_FIELD, error.toString()); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.HANDSHAKE_RESPONSE); + if (client==null) client = TomcatBayeux.getErrorClient(); + } + response.put(Bayeux.CLIENT_FIELD, client.getId()); + response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp()); + try { + JSONObject obj = new JSONObject(response); + addToDeliveryQueue(client, obj); + } catch (ServletException x) { + throw new BayeuxException(x); + } catch (IOException x) { + throw new BayeuxException(x); + } + + //return immediately if there is no subscriptions + //so that we can process the next message + int result = client.isSubscribed()?1:0; + + if (success && client!=null && client.hasMessages()) { + //send out messages + flushMessages(client); + result = 0; //flush out the messages + } + + return result; + } +} + diff --git a/java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java b/java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java new file mode 100644 index 000000000..d191660c2 --- /dev/null +++ b/java/org/apache/tomcat/bayeux/request/MetaDisconnectRequest.java @@ -0,0 +1,105 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux.request; + +import java.io.IOException; +import java.util.HashMap; +import javax.servlet.ServletException; + +import org.apache.catalina.CometEvent; +import org.apache.tomcat.bayeux.HttpError; +import org.apache.tomcat.bayeux.BayeuxException; +import org.apache.tomcat.bayeux.BayeuxRequest; +import org.apache.tomcat.bayeux.ClientImpl; +import org.apache.tomcat.bayeux.TomcatBayeux; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Bayeux; +import org.apache.tomcat.bayeux.*; +import org.apache.cometd.bayeux.Channel; + +/****************************************************************************** + * Handshake request Bayeux message. + * + * @author Guy A. Molinari + * @author Filip Hanik + * @version 1.0 + * + */ +public class MetaDisconnectRequest extends RequestBase implements BayeuxRequest { + + protected static HashMap responseTemplate = new HashMap(); + + static { + responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_DISCONNECT); + responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE); + responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap()); + } + + public MetaDisconnectRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException { + super(tb, event, jsReq); + } + + + /** + * Check client request for validity. + * + * Per section 4.4.1 of the Bayuex spec a connect request must contain: + * 1) The "/meta/disconnect" channel identifier. + * 2) The clientId. + * + * @return HttpError This method returns null if no errors were found + */ + public HttpError validate() { + if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId))) + return new HttpError(400,"Client Id not valid.", null); +// if (! (Bayeux.TRANSPORT_LONG_POLL.equals(conType) || Bayeux.TRANSPORT_CALLBACK_POLL.equals(conType))) +// return new HttpError(400,"Unsupported connection type.",null); + return null;//no error + } + + /** + * Disconnect a client session. + */ + public int process(int prevops) throws BayeuxException { + prevops = super.process(prevops); + response = (HashMap)responseTemplate.clone(); + ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId); + HttpError error = validate(); + if (error == null) { + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry"); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval()); + }else { + getTomcatBayeux().remove(client); + response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE); + response.put(Bayeux.ERROR_FIELD, error.toString()); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "none"); + if (client==null) client = TomcatBayeux.getErrorClient(); + } + response.put(Bayeux.CLIENT_FIELD, client.getId()); + try { + JSONObject obj = new JSONObject(response); + addToDeliveryQueue(client, obj); + } catch (ServletException x) { + throw new BayeuxException(x); + } catch (IOException x) { + throw new BayeuxException(x); + } + return 0; + } +} + diff --git a/java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java b/java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java new file mode 100644 index 000000000..2835d1252 --- /dev/null +++ b/java/org/apache/tomcat/bayeux/request/MetaHandshakeRequest.java @@ -0,0 +1,116 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux.request; + +import java.io.IOException; +import java.util.HashMap; +import javax.servlet.ServletException; + +import org.apache.catalina.CometEvent; +import org.apache.tomcat.bayeux.HttpError; +import org.apache.tomcat.bayeux.BayeuxException; +import org.apache.tomcat.bayeux.BayeuxRequest; +import org.apache.tomcat.bayeux.ClientImpl; +import org.apache.tomcat.bayeux.TomcatBayeux; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Bayeux; +import org.apache.tomcat.bayeux.*; + +/****************************************************************************** + * Handshake request Bayeux message. + * + * @author Guy A. Molinari + * @author Filip Hanik + * @version 1.0 + * + */ +public class MetaHandshakeRequest extends RequestBase implements BayeuxRequest { + + protected static HashMap responseTemplate = new HashMap(); + + static { + responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_HANDSHAKE); + responseTemplate.put(Bayeux.VERSION_FIELD,"1.0"); + responseTemplate.put(Bayeux.SUPP_CONNECTION_TYPE_FIELD,new String[] { Bayeux.TRANSPORT_LONG_POLL, Bayeux.TRANSPORT_CALLBACK_POLL }); + responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE); + responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap()); + } + + public MetaHandshakeRequest(TomcatBayeux tomcatBayeux, CometEvent event, JSONObject jsReq) throws JSONException { + super(tomcatBayeux, event, jsReq); + } + + + public String getVersion() { return version; } + public String getMinimumVersion() { return minVersion; } + + + /** + * Check client request for validity. + * + * Per section 4.1.1 of the Bayuex spec a handshake request must contain: + * 1) The "/meta/handshake" channel identifier. + * 2) The version of the protocol supported by the client + * 3) The client's supported connection types. + * + * @return HttpError This method returns null if no errors were found + */ + public HttpError validate() { + boolean error = (version==null || version.length()==0); + if (!error) error = suppConnTypesFlag==0; + if (error) return new HttpError(400,"Invalid handshake request, supportedConnectionType field missing.",null); + else return null; + } + + /** + * Generate and return a client identifier. Return a list of + * supported connection types. Must be a subset of or identical to + * the list of types supported by the client. See section 4.1.2 of + * the Bayuex specification. + */ + public int process(int prevops) throws BayeuxException { + prevops = super.process(prevops); + response = (HashMap)responseTemplate.clone(); + ClientImpl client = null; + HttpError error = validate(); + if (error == null) { + client = (ClientImpl) getTomcatBayeux().newClient("http-", null, false,getEvent()); + clientId = client.getId(); + client.setSupportedConnTypes(suppConnTypesFlag); + client.setUseJsonFiltered(getExt().get(Bayeux.JSON_COMMENT_FILTERED_FIELD) != null); + response.put(Bayeux.CLIENT_FIELD, client.getId()); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval()); + }else { + response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE); + response.put(Bayeux.ERROR_FIELD, error.toString()); + client = TomcatBayeux.getErrorClient(); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.NONE_RESPONSE); + } + try { + JSONObject obj = new JSONObject(response); + addToDeliveryQueue(client, obj); + } catch (ServletException x) { + throw new BayeuxException(x); + } catch (IOException x) { + throw new BayeuxException(x); + } + return 0; + } +} + diff --git a/java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java b/java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java new file mode 100644 index 000000000..c16667a4e --- /dev/null +++ b/java/org/apache/tomcat/bayeux/request/MetaSubscribeRequest.java @@ -0,0 +1,130 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux.request; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import javax.servlet.ServletException; + +import org.apache.catalina.CometEvent; +import org.apache.tomcat.bayeux.HttpError; +import org.apache.tomcat.bayeux.BayeuxException; +import org.apache.tomcat.bayeux.BayeuxRequest; +import org.apache.tomcat.bayeux.ChannelImpl; +import org.apache.tomcat.bayeux.ClientImpl; +import org.apache.tomcat.bayeux.TomcatBayeux; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Channel; +import org.apache.cometd.bayeux.Bayeux; +import org.apache.tomcat.bayeux.*; + +/****************************************************************************** + * Handshake request Bayeux message. + * + * @author Guy A. Molinari + * @author Filip Hanik + * @version 1.0 + */ +public class MetaSubscribeRequest extends RequestBase implements BayeuxRequest { + + protected static HashMap responseTemplate = new HashMap(); + + static { + responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_SUBSCRIBE); + responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE); + responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap()); + } + + public MetaSubscribeRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException { + super(tb, event, jsReq); + } + + + /** + * Check client request for validity. + * + * Per section 4.5.1 of the Bayuex spec a connect request must contain: + * 1) The "/meta/subscribe" channel identifier. + * 2) The clientId. + * 3) The subscription. This is the name of the channel of interest, + * or a pattern. + * + * @return HttpError This method returns null if no errors were found + */ + public HttpError validate() { + if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId))) + return new HttpError(400,"Client Id not valid.", null); + if (subscription==null||subscription.length()==0) + return new HttpError(400,"Subscription missing.",null); + return null;//no error + } + + /** + * Register interest for one or more channels. Per section 2.2.1 of the + * Bayeux spec, a pattern may be specified. Assign client to matching + * channels and inverse client to channel reference. + */ + public int process(int prevops) throws BayeuxException { + prevops = super.process(prevops); + response = (HashMap)this.responseTemplate.clone(); + ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId); + HttpError error = validate(); + if (error == null) { + boolean wildcard = subscription.indexOf('*')!=-1; + boolean subscribed = false; + if (wildcard) { + List channels = getTomcatBayeux().getChannels(); + Iterator it = channels.iterator(); + while (it.hasNext()) { + ChannelImpl ch = (ChannelImpl)it.next(); + if (ch.matches(subscription)) { + ch.subscribe(client); + subscribed = true; + } + } + }else { + ChannelImpl ch = (ChannelImpl)getTomcatBayeux().getChannel(subscription,true); + ch.subscribe(client); + subscribed = true; + } + response.put(Bayeux.SUCCESSFUL_FIELD, Boolean.valueOf(subscribed)); + response.put(Bayeux.SUBSCRIPTION_FIELD,subscription); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry"); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval()); + }else { + response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE); + response.put(Bayeux.ERROR_FIELD, error.toString()); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "handshake"); + if (client==null) client = TomcatBayeux.getErrorClient(); + } + response.put(Bayeux.CLIENT_FIELD, client.getId()); + response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp()); + try { + JSONObject obj = new JSONObject(response); + addToDeliveryQueue(client, obj); + } catch (ServletException x) { + throw new BayeuxException(x); + } catch (IOException x) { + throw new BayeuxException(x); + } + return 0; + } +} + diff --git a/java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java b/java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java new file mode 100644 index 000000000..28969a08a --- /dev/null +++ b/java/org/apache/tomcat/bayeux/request/MetaUnsubscribeRequest.java @@ -0,0 +1,130 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux.request; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import javax.servlet.ServletException; + +import org.apache.catalina.CometEvent; +import org.apache.tomcat.bayeux.HttpError; +import org.apache.tomcat.bayeux.BayeuxException; +import org.apache.tomcat.bayeux.BayeuxRequest; +import org.apache.tomcat.bayeux.ChannelImpl; +import org.apache.tomcat.bayeux.ClientImpl; +import org.apache.tomcat.bayeux.TomcatBayeux; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Channel; +import org.apache.cometd.bayeux.Bayeux; +import org.apache.tomcat.bayeux.*; + +/****************************************************************************** + * Handshake request Bayeux message. + * + * @author Guy A. Molinari + * @author Filip Hanik + * @version 1.0 + * + */ +public class MetaUnsubscribeRequest extends RequestBase implements BayeuxRequest { + + protected static HashMap responseTemplate = new HashMap(); + + static { + responseTemplate.put(Bayeux.CHANNEL_FIELD,Bayeux.META_UNSUBSCRIBE); + responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE); + responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap()); + } + + public MetaUnsubscribeRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException { + super(tb, event, jsReq); + } + + + /** + * Check client request for validity. + * + * Per section 4.6.1 of the Bayuex spec a connect request must contain: + * 1) The "/meta/unsubscribe" channel identifier. + * 2) The clientId. + * 3) The subscription. This is the name of the channel of interest, + * or a pattern. + * + * @return HttpError This method returns null if no errors were found + */ + public HttpError validate() { + if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId))) + return new HttpError(400,"Client Id not valid.", null); + if (subscription==null||subscription.length()==0) + return new HttpError(400,"Subscription missing.",null); + return null;//no error + } + + /** + * De-register interest for one or more channels. Per section 2.2.1 of the + * Bayeux spec, a pattern may be specified. Sever relationships. + */ + public int process(int prevops) throws BayeuxException { + prevops = super.process(prevops); + response = (HashMap)responseTemplate.clone(); + ClientImpl client = (ClientImpl)getTomcatBayeux().getClient(clientId); + HttpError error = validate(); + if (error == null) { + boolean wildcard = subscription.indexOf('*')!=-1; + boolean unsubscribed = false; + if (wildcard) { + List channels = getTomcatBayeux().getChannels(); + Iterator it = channels.iterator(); + while (it.hasNext()) { + ChannelImpl ch = (ChannelImpl)it.next(); + if (ch.matches(subscription)) { + ch.unsubscribe(client); + unsubscribed = true; + } + } + }else { + ChannelImpl ch = (ChannelImpl)getTomcatBayeux().getChannel(subscription,true); + ch.unsubscribe(client); + unsubscribed = true; + } + response.put(Bayeux.SUCCESSFUL_FIELD, Boolean.valueOf(unsubscribed)); + response.put(Bayeux.SUBSCRIPTION_FIELD,subscription); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "retry"); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("interval", getReconnectInterval()); + }else { + response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE); + response.put(Bayeux.ERROR_FIELD, error.toString()); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put("reconnect", "handshake"); + if (client==null) client = TomcatBayeux.getErrorClient(); + } + response.put(Bayeux.CLIENT_FIELD, client.getId()); + response.put(Bayeux.TIMESTAMP_FIELD,getTimeStamp()); + try { + JSONObject obj = new JSONObject(response); + addToDeliveryQueue(client, obj); + } catch (ServletException x) { + throw new BayeuxException(x); + } catch (IOException x) { + throw new BayeuxException(x); + } + return 0; + } +} + diff --git a/java/org/apache/tomcat/bayeux/request/PublishRequest.java b/java/org/apache/tomcat/bayeux/request/PublishRequest.java new file mode 100644 index 000000000..8bb0dff7e --- /dev/null +++ b/java/org/apache/tomcat/bayeux/request/PublishRequest.java @@ -0,0 +1,140 @@ +/* + * 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. + */ +package org.apache.tomcat.bayeux.request; + +import java.io.IOException; +import java.util.HashMap; +import javax.servlet.ServletException; + +import org.apache.catalina.CometEvent; +import org.apache.tomcat.bayeux.HttpError; +import org.apache.tomcat.bayeux.BayeuxException; +import org.apache.tomcat.bayeux.BayeuxRequest; +import org.apache.tomcat.bayeux.ChannelImpl; +import org.apache.tomcat.bayeux.ClientImpl; +import org.apache.tomcat.bayeux.MessageImpl; +import org.apache.tomcat.bayeux.TomcatBayeux; +import org.json.JSONException; +import org.json.JSONObject; +import org.apache.cometd.bayeux.Bayeux; +import java.util.List; +import org.apache.cometd.bayeux.Message; +import java.util.Iterator; +import org.apache.tomcat.bayeux.*; + +/****************************************************************************** + * Handshake request Bayeux message. + * + * @author Guy A. Molinari + * @author Filip Hanik + * @version 1.0 + * + */ +public class PublishRequest extends RequestBase implements BayeuxRequest { + + JSONObject msgData = null; + + protected static HashMap responseTemplate = new HashMap(); + + static { + responseTemplate.put(Bayeux.SUCCESSFUL_FIELD,Boolean.TRUE); + responseTemplate.put(Bayeux.ADVICE_FIELD, new HashMap()); + } + + public PublishRequest(TomcatBayeux tb, CometEvent event, JSONObject jsReq) throws JSONException { + super(tb, event, jsReq); + } + + + /** + * Check client request for validity. + * + * Per section 5.1.1 of the Bayuex spec a connect request must contain: + * 1) The channel identifier of the channel for publication. + * 2) The data to send. + * + * @return HttpError This method returns null if no errors were found + */ + public HttpError validate() { + if(channel==null|| (!this.getTomcatBayeux().hasChannel(channel))) + return new HttpError(400,"Channel Id not valid.", null); + if(data==null || data.length()==0) + return new HttpError(400,"Message data missing.", null); + try { + this.msgData = new JSONObject(data); + }catch (JSONException x) { + return new HttpError(400,"Invalid JSON object in data attribute.",x); + } + if(clientId==null|| (!this.getTomcatBayeux().hasClient(clientId))) + return new HttpError(400,"Client Id not valid.", null); + return null;//no error + } + + /** + * Send the event message to all registered subscribers. + */ + public int process(int prevops) throws BayeuxException { + prevops = super.process(prevops); + response = (HashMap)responseTemplate.clone(); + ClientImpl client = clientId!=null?(ClientImpl)getTomcatBayeux().getClient(clientId): + (ClientImpl)event.getHttpServletRequest().getAttribute("client"); + boolean success = false; + HttpError error = validate(); + if (error == null) { + ChannelImpl chimpl = (ChannelImpl)getTomcatBayeux().getChannel(channel,false); + MessageImpl mimpl = (MessageImpl)getTomcatBayeux().newMessage(client); + + try { + String[] keys = JSONObject.getNames(msgData); + for (int i = 0; i < keys.length; i++) { + mimpl.put(keys[i], msgData.get(keys[i])); + } + success = true; + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.RETRY_RESPONSE); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.INTERVAL_FIELD, getReconnectInterval()); + }catch (JSONException x) { + if (log.isErrorEnabled()) log.error("Unable to parse:"+msgData,x); + throw new BayeuxException(x); + } + chimpl.publish(mimpl); + } + if(!success) { + response.put(Bayeux.SUCCESSFUL_FIELD,Boolean.FALSE); + response.put(Bayeux.ERROR_FIELD, error.toString()); + ((HashMap) response.get(Bayeux.ADVICE_FIELD)).put(Bayeux.RECONNECT_FIELD, Bayeux.HANDSHAKE_RESPONSE); + if (client==null) client = TomcatBayeux.getErrorClient(); + } + response.put(Bayeux.CHANNEL_FIELD,channel); + response.put(Bayeux.CLIENT_FIELD, client.getId()); + try { + JSONObject obj = new JSONObject(response); + addToDeliveryQueue(client, obj); + } catch (ServletException x) { + throw new BayeuxException(x); + } catch (IOException x) { + throw new BayeuxException(x); + } + + if (success && client!=null && client.hasMessages()) { + //send out messages + flushMessages(client); + } + + return 0; + } +} + diff --git a/test/org/apache/cometd/bayeux/samples/EchoChatClient.java b/test/org/apache/cometd/bayeux/samples/EchoChatClient.java new file mode 100644 index 000000000..3d7c393f1 --- /dev/null +++ b/test/org/apache/cometd/bayeux/samples/EchoChatClient.java @@ -0,0 +1,111 @@ +package org.apache.cometd.bayeux.samples; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletContextAttributeListener; +import javax.servlet.ServletContextAttributeEvent; +import org.apache.cometd.bayeux.Bayeux; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.cometd.bayeux.Client; +import org.apache.cometd.bayeux.Listener; +import org.apache.cometd.bayeux.Message; +import org.apache.cometd.bayeux.Channel; + +public class EchoChatClient implements ServletContextListener, ServletContextAttributeListener, Listener { + + static AtomicInteger counter = new AtomicInteger(0); + protected int id; + protected Bayeux b; + protected Client c; + protected boolean alive = true; + protected TimestampThread tt = new TimestampThread(); + public EchoChatClient() { + id = counter.incrementAndGet(); + System.out.println("new listener created with id:"+id); + } + + /** + * contextDestroyed + * + * @param servletContextEvent ServletContextEvent + * @todo Implement this javax.servlet.ServletContextListener method + */ + public void contextDestroyed(ServletContextEvent servletContextEvent) { + alive = false; + tt.interrupt(); + } + + /** + * contextInitialized + * + * @param servletContextEvent ServletContextEvent + * @todo Implement this javax.servlet.ServletContextListener method + */ + public void contextInitialized(ServletContextEvent servletContextEvent) { + } + + public void attributeAdded(ServletContextAttributeEvent scae) { + if (scae.getName().equals(Bayeux.DOJOX_COMETD_BAYEUX)) { + b = (Bayeux)scae.getValue(); + c = b.newClient("echochat-",this); + Channel ch = b.getChannel("/chat/demo",true); + ch.subscribe(c); + tt.start(); + } + } + + public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) { + } + + public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) { + } + + public void removed(boolean timeout) { + System.out.println("Client removed."); + } + + public void deliver(Message[] msgs) { + for (int i=0; msgs!=null && i + + Cometd Test WebApp + + + cometd + org.apache.tomcat.bayeux.BayeuxServlet + + timeout + 120000000 + + 1 + + + + cometd + /cometd/* + + + + + diff --git a/webapps/cometd/examples/chat/chat.css b/webapps/cometd/examples/chat/chat.css new file mode 100644 index 000000000..dfc1c8552 --- /dev/null +++ b/webapps/cometd/examples/chat/chat.css @@ -0,0 +1,57 @@ + +div +{ + border: 0px solid black; +} + +div#chatroom +{ + background-color: #e0e0e0; + border: 1px solid black; + width: 45em; +} + +div#chat +{ + height: 20ex; + overflow: auto; + background-color: #f0f0f0; + padding: 4px; + border: 0px solid black; +} + +div#input +{ + clear: both; + padding: 4px; + border: 0px solid black; + border-top: 1px solid black; +} + +input#phrase +{ + width:28em; + background-color: #e0f0f0; +} + +input#username +{ + width:14em; + background-color: #e0f0f0; +} + +div.hidden +{ + display: none; +} + +span.from +{ + font-weight: bold; +} + +span.alert +{ + font-style: italic; +} + diff --git a/webapps/cometd/examples/chat/chat.js b/webapps/cometd/examples/chat/chat.js new file mode 100644 index 000000000..62584347d --- /dev/null +++ b/webapps/cometd/examples/chat/chat.js @@ -0,0 +1,139 @@ +dojo.require("dojox.cometd"); +dojo.require("dojox.cometd.timestamp"); + +var room = { + _last: "", + _username: null, + _connected: true, + + join: function(name){ + + if(name == null || name.length==0 ){ + alert('Please enter a username!'); + }else{ + + dojox.cometd.init(new String(document.location).replace(/http:\/\/[^\/]*/,'').replace(/\/examples\/.*$/,'')+"/cometd"); + // dojox.cometd.init("http://127.0.0.2:8080/cometd"); + this._connected=true; + + this._username=name; + dojo.byId('join').className='hidden'; + dojo.byId('joined').className=''; + dojo.byId('phrase').focus(); + + // subscribe and join + dojox.cometd.startBatch(); + dojox.cometd.subscribe("/chat/demo", room, "_chat"); + dojox.cometd.publish("/chat/demo", { user: room._username, join: true, chat : room._username+" has joined"}); + dojox.cometd.endBatch(); + + // handle cometd failures while in the room + room._meta=dojo.subscribe("/cometd/meta",dojo.hitch(this,function(event){ + console.debug(event); + if (event.action=="handshake"){ + room._chat({data:{join:true,user:"SERVER",chat:"reinitialized"}}); + dojox.cometd.subscribe("/chat/demo", room, "_chat"); + } else if (event.action=="connect") { + if (event.successful && !this._connected) + room._chat({data:{leave:true,user:"SERVER",chat:"reconnected!"}}); + if (!event.successful && this._connected) + room._chat({data:{leave:true,user:"SERVER",chat:"disconnected!"}}); + this._connected=event.successful; + } + })); + } + }, + + leave: function(){ + if (room._username==null) + return; + + if (room._meta) + dojo.unsubscribe(room._meta); + room._meta=null; + + dojox.cometd.startBatch(); + dojox.cometd.unsubscribe("/chat/demo", room, "_chat"); + dojox.cometd.publish("/chat/demo", { user: room._username, leave: true, chat : room._username+" has left"}); + dojox.cometd.endBatch(); + + // switch the input form + dojo.byId('join').className=''; + dojo.byId('joined').className='hidden'; + dojo.byId('username').focus(); + room._username=null; + dojox.cometd.disconnect(); + }, + + chat: function(text){ + if(!text || !text.length){ return false; } + dojox.cometd.publish("/chat/demo", { user: room._username, chat: text}); + }, + + _chat: function(message){ + var chat=dojo.byId('chat'); + if(!message.data){ + alert("bad message format "+message); + return; + } + var from=message.data.user; + var special=message.data.join || message.data.leave; + var text=message.data.chat; + if(!text){ return; } + + if( !special && from == room._last ){ + from="..."; + }else{ + room._last=from; + from+=":"; + } + + if(special){ + chat.innerHTML += ""+from+" "+text+"
    "; + room._last=""; + }else{ + chat.innerHTML += ""+from+" "+text+"
    "; + } + chat.scrollTop = chat.scrollHeight - chat.clientHeight; + }, + + _init: function(){ + dojo.byId('join').className=''; + dojo.byId('joined').className='hidden'; + dojo.byId('username').focus(); + + var element=dojo.byId('username'); + element.setAttribute("autocomplete","OFF"); + dojo.connect(element, "onkeyup", function(e){ + if(e.keyCode == dojo.keys.ENTER){ + room.join(dojo.byId('username').value); + return false; + } + return true; + }); + + dojo.connect(dojo.byId('joinB'), "onclick", function(e){ + room.join(dojo.byId('username').value); + e.preventDefault(); + }); + + element=dojo.byId('phrase'); + element.setAttribute("autocomplete","OFF"); + dojo.connect(element, "onkeyup", function(e){ + if(e.keyCode == dojo.keys.ENTER){ + room.chat(dojo.byId('phrase').value); + dojo.byId('phrase').value=''; + e.preventDefault(); + } + }); + + dojo.connect(dojo.byId('sendB'), "onkeyup", function(e){ + room.chat(dojo.byId('phrase').value); + dojo.byId('phrase').value=''; + }); + dojo.connect(dojo.byId('leaveB'), "onclick", room, "leave"); + } +}; + +dojo.addOnLoad(room, "_init"); +dojo.addOnUnload(room,"leave"); diff --git a/webapps/cometd/examples/chat/index.html b/webapps/cometd/examples/chat/index.html new file mode 100644 index 000000000..3d9629967 --- /dev/null +++ b/webapps/cometd/examples/chat/index.html @@ -0,0 +1,26 @@ + + + Cometd chat + + + + + + +

    Cometd Chat

    + +
    +
    +
    +
    + Username:  +
    + +
    +
    + + diff --git a/webapps/cometd/examples/dojo/dojo/dojo.js b/webapps/cometd/examples/dojo/dojo/dojo.js new file mode 100644 index 000000000..13986ec9e --- /dev/null +++ b/webapps/cometd/examples/dojo/dojo/dojo.js @@ -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;i0){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;i0&&!(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(""+"");}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;i2){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=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>=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=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=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)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 index 000000000..674634582 --- /dev/null +++ b/webapps/cometd/examples/dojo/dojo/dojo.js.uncompressed.js @@ -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 + // ` + // | + 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('' + + '' + ); + } + + // 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 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. + // 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: + // + + 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: + // + 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: + // + + // 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 + // 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: + // + // 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: + // + // 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 + // 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: + // + // 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: + // + // 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: + // + // 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: + // + // 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: + // + // 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("

    NOTE:

    ", "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= 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=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 + // 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: + // | + // | + // |

    + // example: + // search the entire document for elements with the classes "foo" *and* "bar": + // | dojo.query(".foo.bar"); + // these elements will match: + // | + // while these will not: + // | + // |

    + // example: + // find `` elements which are descendants of paragraphs and + // which have a "highlighted" class: + // | dojo.query("p span.highlighted"); + // the innermost span in this fragment matches: + // |

    + // | ... + // | ... + // | + // |

    + // 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: + // + // |
    + // | + // | + // | + // | + // |
    + // + // 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 index 000000000..83728552d --- /dev/null +++ b/webapps/cometd/examples/dojo/dojo/dojo.xd.js @@ -0,0 +1,20 @@ +/* + Copyright (c) 2004-2007, 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. +*/ + +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;i0){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;i0&&!(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;i0){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;i0){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(""+"");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;i2){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;x3)){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;i2){_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>=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;x90)&&(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"){_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=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=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)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]);}};} 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 index 000000000..f4eeafeab --- /dev/null +++ b/webapps/cometd/examples/dojo/dojo/dojo.xd.js.uncompressed.js @@ -0,0 +1,8203 @@ +/* + Copyright (c) 2004-2007, 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. +*/ + +if(typeof dojo == "undefined"){ + +/*===== +djConfig = { + // summary: + // Application code can set the global 'djConfig' prior to loading + // the library to override certain global settings for how dojo works. + // description: The variables that can be set are as follows: + // - isDebug: false + // - libraryScriptUri: "" + // - locale: undefined + // - extraLocale: undefined + // - preventBackButtonFix: true + // 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. +} +=====*/ + +(function(){ + // make sure djConfig is defined + if(typeof this["djConfig"] == "undefined"){ + this.djConfig = {}; + } + + // firebug stubs + 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(){}; + } + } + + //TODOC: HOW TO DOC THIS? + // dojo is the root variable of (almost all) our public symbols -- make sure it is defined. + if(typeof this["dojo"] == "undefined"){ + this.dojo = {}; + } + + var d = dojo; + +/*===== +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). +} +=====*/ + dojo.global = this; + + var _config =/*===== djConfig = =====*/{ + isDebug: false, + libraryScriptUri: "", + preventBackButtonFix: true, + delayMozLoadingFix: false + }; + + for(var option in _config){ + if(typeof djConfig[option] == "undefined"){ + djConfig[option] = _config[option]; + } + } + + 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 = djConfig.locale; + + var rev = "$Rev: 11742 $".match(/\d+/); + + dojo.version = { + // summary: version number of this instance of dojo. + major: 0, minor: 0, patch: 0, flag: "dev", + revision: rev ? Number(rev[0]) : 999999, + 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", "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. + for(var i=1, l=arguments.length; i 0){ + 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())(); + } + } + + 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"); + if(arguments.length == 1){ + d._loaders.push(obj); + }else if(arguments.length > 1){ + d._loaders.push(function(){ + 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") + if(arguments.length == 1){ + d._unloaders.push(obj); + }else if(arguments.length > 1){ + d._unloaders.push(function(){ + 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" || (djConfig.useXDomain && d.isOpera)){ + setTimeout("dojo.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: String + // omitModuleCheck: Boolean? + // description: + // _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. + // + // It throws 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. + // + // This does nothing about importing symbols into the current package. + // It is presumed that the caller will take care of that. For example, + // to import all symbols: + // + // | with (dojo._loadModule("A.B")) { + // | ... + // | } + // + // And to import just the leaf symbol: + // + // | var B = dojo._loadModule("A.B"); + // | ... + // returns: the required namespace object + omitModuleCheck = this._global_omit_module_check || omitModuleCheck; + 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 (exactly) 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"); at the + // top of the file. + // 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("dojo.io.cometd"), in addition to registering that + // cometd.js is a resource for the dojo.iomodule, will ensure that + // the dojo.io javascript object exists, so that calls like + // dojo.io.foo = function(){ ... } don't fail. + // + // In the case of a build (or in the future, a rollup), where multiple + // javascript source files are combined into one bigger file (similar + // to a .lib or .jar file), that file will 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 containing Arrays*/modMap){ + // description: + // This method taks 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 the + // hostenv.name_ item. The items in the "common" array will _always_ + // be loaded, regardless of which list is chosen. Here's how it's + // normally called: + // + // | dojo.platformRequire({ + // | // an example that passes multiple args to _loadModule() + // | browser: [ + // | ["foo.bar.baz", true, true], + // | "foo.sample", + // | "foo.test, + // | ], + // | default: [ "foo.sample.*" ], + // | common: [ "really.important.module.*" ] + // | }); + + // FIXME: dojo.name_ no longer works!! + + var common = modMap.common || []; + var result = common.concat(modMap[d._name] || modMap["default"] || []); + + for(var x=0; x, + // relative to Dojo root. For example, module acme is mapped to + // ../acme. If you want to use a different module name, use + // dojo.registerModulePath. + 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+""); + + 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] == "."){ + 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] != ".."){ + + 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 += relobj.scheme + ":"; + } + if(relobj.authority){ + uri += "//" + relobj.authority; + } + uri += relobj.path; + if(relobj.query){ + uri += "?" + relobj.query; + } + if(relobj.fragment){ + uri += "#" + relobj.fragment; + } + } + + this.uri = uri.toString(); + + // 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 Url object relative to a module + // + // example: + // | dojo.moduleUrl("dojo.widget","templates/template.html"); + // example: + // | dojo.moduleUrl("acme","images/small.png") + + 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 + } +})(); + +//Cross-domain resource loader. +dojo.provide("dojo._base._loader.loader_xd"); + +dojo._xdReset = function(){ + //summary: Internal xd loader function. Resets the xd state. + + //This flag indicates where or not we have crossed into xdomain territory. Once any resource says + //it is cross domain, then the rest of the resources have to be treated as xdomain because we need + //to evaluate resources in order. If there is a xdomain resource followed by a xhr resource, we can't load + //the xhr resource until the one before it finishes loading. The text of the xhr resource will be converted + //to match the format for a xd resource and put in the xd load queue. + this._isXDomain = djConfig.useXDomain || false; + + this._xdTimer = 0; + this._xdInFlight = {}; + this._xdOrderedReqs = []; + this._xdDepMap = {}; + this._xdContents = []; + this._xdDefList = []; +} + +//Call reset immediately to set the state. +dojo._xdReset(); + +dojo._xdCreateResource = function(/*String*/contents, /*String*/resourceName, /*String*/resourcePath){ + //summary: Internal xd loader function. Creates an xd module source given an + //non-xd module contents. + + //Remove comments. Not perfect, but good enough for dependency resolution. + var depContents = contents.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg , ""); + + //Find dependencies. + var deps = []; + var depRegExp = /dojo.(require|requireIf|provide|requireAfterIf|platformRequire|requireLocalization)\(([\w\W]*?)\)/mg; + var match; + while((match = depRegExp.exec(depContents)) != null){ + if(match[1] == "requireLocalization"){ + //Need to load the local bundles asap, since they are not + //part of the list of modules watched for loading. + eval(match[0]); + }else{ + deps.push('"' + match[1] + '", ' + match[2]); + } + } + + //Create resource object and the call to _xdResourceLoaded. + var output = []; + output.push("dojo._xdResourceLoaded({\n"); + + //Add dependencies + if(deps.length > 0){ + output.push("depends: ["); + for(var i = 0; i < deps.length; i++){ + if(i > 0){ + output.push(",\n"); + } + output.push("[" + deps[i] + "]"); + } + output.push("],"); + } + + //Add the contents of the file inside a function. + //Pass in dojo as an argument to the function to help with + //allowing multiple versions of dojo in a page. + output.push("\ndefineResource: function(dojo){"); + + //Don't put in the contents in the debugAtAllCosts case + //since the contents may have syntax errors. Let those + //get pushed up when the script tags are added to the page + //in the debugAtAllCosts case. + if(!djConfig["debugAtAllCosts"] || resourceName == "dojo._base._loader.loader_debug"){ + output.push(contents); + } + //Add isLocal property so we know if we have to do something different + //in debugAtAllCosts situations. + output.push("\n}, resourceName: '" + resourceName + "', resourcePath: '" + resourcePath + "'});"); + + return output.join(""); //String +} + +dojo._xdIsXDomainPath = function(/*string*/relpath) { + //summary: Figure out whether the path is local or x-domain + //If there is a colon before the first / then, we have a URL with a protocol. + + var colonIndex = relpath.indexOf(":"); + var slashIndex = relpath.indexOf("/"); + + if(colonIndex > 0 && colonIndex < slashIndex){ + return true; + }else{ + //Is the base script URI-based URL a cross domain URL? + //If so, then the relpath will be evaluated relative to + //baseUrl, and therefore qualify as xdomain. + //Only treat it as xdomain if the page does not have a + //host (file:// url) or if the baseUrl does not match the + //current window's domain. + var url = this.baseUrl; + colonIndex = url.indexOf(":"); + slashIndex = url.indexOf("/"); + if(colonIndex > 0 && colonIndex < slashIndex && (!location.host || url.indexOf("http://" + location.host) != 0)){ + return true; + } + } + return false; +} + +dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){ + //summary: Internal xd loader function. Overrides loadPath() from loader.js. + //xd loading requires slightly different behavior from loadPath(). + + var currentIsXDomain = this._xdIsXDomainPath(relpath); + this._isXDomain |= currentIsXDomain; + + var uri = this.baseUrl + relpath; + if(currentIsXDomain){ + // check whether the relpath is an absolute URL itself. If so, we + // ignore baseUrl + var colonIndex = relpath.indexOf(":"); + var slashIndex = relpath.indexOf("/"); + if(colonIndex > 0 && colonIndex < slashIndex){ + uri = relpath; + } + } + + if(djConfig.cacheBust && dojo.isBrowser) { uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,""); } + try{ + return ((!module || this._isXDomain) ? this._loadUri(uri, cb, currentIsXDomain, module) : this._loadUriAndCheck(uri, module, cb)); //Boolean + }catch(e){ + console.debug(e); + return false; //Boolean + } +} + +dojo._loadUri = function(/*String*/uri, /*Function?*/cb, /*boolean*/currentIsXDomain, /*String?*/module){ + //summary: Internal xd loader function. Overrides loadUri() from loader.js. + // xd loading requires slightly different behavior from loadPath(). + //description: Wanted to override getText(), but it is used by + // the widget code in too many, synchronous ways right now. + if(this._loadedUrls[uri]){ + return 1; //Boolean + } + + //Add the module (resource) to the list of modules. + //Only do this work if we have a modlue name. Otherwise, + //it is a non-xd i18n bundle, which can load immediately and does not + //need to be tracked. Also, don't track dojo.i18n, since it is a prerequisite + //and will be loaded correctly if we load it right away: it has no dependencies. + if(this._isXDomain && module && module != "dojo.i18n"){ + this._xdOrderedReqs.push(module); + + //Add to waiting resources if it is an xdomain resource. + //Don't add non-xdomain i18n bundles, those get evaled immediately. + if(currentIsXDomain || uri.indexOf("/nls/") == -1){ + this._xdInFlight[module] = true; + + //Increment inFlightCount + //This will stop the modulesLoaded from firing all the way. + this._inFlightCount++; + } + + //Start timer + if(!this._xdTimer){ + this._xdTimer = setInterval("dojo._xdWatchInFlight();", 100); + } + this._xdStartTime = (new Date()).getTime(); + } + + if (currentIsXDomain){ + //Fix name to be a .xd.fileextension name. + var lastIndex = uri.lastIndexOf('.'); + if(lastIndex <= 0){ + lastIndex = uri.length - 1; + } + + var xdUri = uri.substring(0, lastIndex) + ".xd"; + if(lastIndex != uri.length - 1){ + xdUri += uri.substring(lastIndex, uri.length); + } + + //Add to script src + var element = document.createElement("script"); + element.type = "text/javascript"; + element.src = xdUri; + if(!this.headElement){ + this._headElement = document.getElementsByTagName("head")[0]; + + //Head element may not exist, particularly in html + //html 4 or tag soup cases where the page does not + //have a head tag in it. Use html element, since that will exist. + //Seems to be an issue mostly with Opera 9 and to lesser extent Safari 2 + if(!this._headElement){ + this._headElement = document.getElementsByTagName("html")[0]; + } + } + this._headElement.appendChild(element); + }else{ + var contents = this._getText(uri, null, true); + if(contents == null){ return 0; /*boolean*/} + + //If this is not xdomain, or if loading a i18n resource bundle, then send it down + //the normal eval/callback path. + if(this._isXDomain + && uri.indexOf("/nls/") == -1 + && module != "dojo.i18n"){ + var res = this._xdCreateResource(contents, module, uri); + dojo.eval(res); + }else{ + if(cb){ contents = '('+contents+')'; } + var value = dojo.eval(contents); + if(cb){ + cb(value); + } + } + } + + //These steps are done in the non-xd loader version of this function. + //Maintain these steps to fit in with the existing system. + this._loadedUrls[uri] = true; + this._loadedUrls.push(uri); + return true; //Boolean +} + +dojo._xdResourceLoaded = function(/*Object*/res){ + //summary: Internal xd loader function. Called by an xd module resource when + //it has been loaded via a script tag. + var deps = res.depends; + var requireList = null; + var requireAfterList = null; + var provideList = []; + if(deps && deps.length > 0){ + var dep = null; + var insertHint = 0; + var attachedResource = false; + for(var i = 0; i < deps.length; i++){ + dep = deps[i]; + + //Look for specific dependency indicators. + if (dep[0] == "provide"){ + provideList.push(dep[1]); + }else{ + if(!requireList){ + requireList = []; + } + if(!requireAfterList){ + requireAfterList = []; + } + + var unpackedDeps = this._xdUnpackDependency(dep); + if(unpackedDeps.requires){ + requireList = requireList.concat(unpackedDeps.requires); + } + if(unpackedDeps.requiresAfter){ + requireAfterList = requireAfterList.concat(unpackedDeps.requiresAfter); + } + } + + //Call the dependency indicator to allow for the normal dojo setup. + //Only allow for one dot reference, for the i18n._preloadLocalizations calls + //(and maybe future, one-dot things). + var depType = dep[0]; + var objPath = depType.split("."); + if(objPath.length == 2){ + dojo[objPath[0]][objPath[1]].apply(dojo[objPath[0]], dep.slice(1)); + }else{ + dojo[depType].apply(dojo, dep.slice(1)); + } + } + + + //If loading the debugAtAllCosts module, eval it right away since we need + //its functions to properly load the other modules. + if(provideList.length == 1 && provideList[0] == "dojo._base._loader.loader_debug"){ + res.defineResource(dojo); + }else{ + //Save off the resource contents for definition later. + var contentIndex = this._xdContents.push({ + content: res.defineResource, + resourceName: res["resourceName"], + resourcePath: res["resourcePath"], + isDefined: false + }) - 1; + + //Add provide/requires to dependency map. + for(var i = 0; i < provideList.length; i++){ + this._xdDepMap[provideList[i]] = { requires: requireList, requiresAfter: requireAfterList, contentIndex: contentIndex }; + } + } + + //Now update the inflight status for any provided resources in this loaded resource. + //Do this at the very end (in a *separate* for loop) to avoid shutting down the + //inflight timer check too soon. + for(var i = 0; i < provideList.length; i++){ + this._xdInFlight[provideList[i]] = false; + } + } +} + +dojo._xdLoadFlattenedBundle = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*Object*/bundleData){ + //summary: Internal xd loader function. Used when loading + //a flattened localized bundle via a script tag. + locale = locale || "root"; + var jsLoc = dojo.i18n.normalizeLocale(locale).replace('-', '_'); + var bundleResource = [moduleName, "nls", bundleName].join("."); + var bundle = dojo["provide"](bundleResource); + bundle[jsLoc] = bundleData; + + //Assign the bundle for the original locale(s) we wanted. + var mapName = [moduleName, jsLoc, bundleName].join("."); + var bundleMap = dojo._xdBundleMap[mapName]; + if(bundleMap){ + for(var param in bundleMap){ + bundle[param] = bundleData; + } + } +}; + + +dojo._xdInitExtraLocales = function(){ + // Simulate the extra locale work that dojo.requireLocalization does. + + var extra = djConfig.extraLocale; + if(extra){ + if(!extra instanceof Array){ + extra = [extra]; + } + + dojo._xdReqLoc = dojo.xdRequireLocalization; + dojo.xdRequireLocalization = function(m, b, locale, fLocales){ + dojo._xdReqLoc(m,b,locale, fLocales); + if(locale){return;} + for(var i=0; i bestLocale.length){ + bestLocale = locales[i]; + } + } + } + + var fixedBestLocale = bestLocale.replace('-', '_'); + //See if the bundle we are going to use is already loaded. + var bundleResource = dojo.getObject([moduleName, "nls", bundleName].join(".")); + if(bundleResource && bundleResource[fixedBestLocale]){ + bundle[jsLoc.replace('-', '_')] = bundleResource[fixedBestLocale]; + }else{ + //Need to remember what locale we wanted and which one we actually use. + //Then when we load the one we are actually using, use that bundle for the one + //we originally wanted. + var mapName = [moduleName, (fixedBestLocale||"root"), bundleName].join("."); + var bundleMap = dojo._xdBundleMap[mapName]; + if(!bundleMap){ + bundleMap = dojo._xdBundleMap[mapName] = {}; + } + bundleMap[jsLoc.replace('-', '_')] = true; + + //Do just a normal dojo.require so the resource tracking stuff works as usual. + dojo.require(moduleName + ".nls" + (bestLocale ? "." + bestLocale : "") + "." + bundleName); + } +} + +// Replace dojo.requireLocalization with a wrapper +dojo._xdRealRequireLocalization = dojo.requireLocalization; +dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String*/availableFlatLocales){ + // summary: loads a bundle intelligently based on whether the module is + // local or xd. Overrides the local-case implementation. + + var modulePath = this.moduleUrl(moduleName).toString(); + if (this._xdIsXDomainPath(modulePath)) { + // call cross-domain loader + return dojo.xdRequireLocalization.apply(dojo, arguments); + } else { + // call local-loader + return dojo._xdRealRequireLocalization.apply(dojo, arguments); + } +} + +//This is a bit brittle: it has to know about the dojo methods that deal with dependencies +//It would be ideal to intercept the actual methods and do something fancy at that point, +//but I have concern about knowing which provide to match to the dependency in that case, +//since scripts can load whenever they want, and trigger new calls to dojo._xdResourceLoaded(). +dojo._xdUnpackDependency = function(/*Array*/dep){ + //summary: Internal xd loader function. Determines what to do with a dependency + //that was listed in an xd version of a module contents. + + //Extract the dependency(ies). + var newDeps = null; + var newAfterDeps = null; + switch(dep[0]){ + case "requireIf": + case "requireAfterIf": + //First arg (dep[1]) is the test. Depedency is dep[2]. + if(dep[1] === true){ + newDeps = [{name: dep[2], content: null}]; + } + break; + case "platformRequire": + var modMap = dep[1]; + var common = modMap["common"]||[]; + var newDeps = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]); + //Flatten the array of arrays into a one-level deep array. + //Each result could be an array of 3 elements (the 3 arguments to dojo.require). + //We only need the first one. + if(newDeps){ + for(var i = 0; i < newDeps.length; i++){ + if(newDeps[i] instanceof Array){ + newDeps[i] = {name: newDeps[i][0], content: null}; + }else{ + newDeps[i] = {name: newDeps[i], content: null}; + } + } + } + break; + case "require": + //Just worry about dep[1] + newDeps = [{name: dep[1], content: null}]; + break; + case "i18n._preloadLocalizations": + //We can eval these immediately, since they load i18n bundles. + //Since i18n bundles have no dependencies, whenever they are loaded + //in a script tag, they are evaluated immediately, so we do not have to + //treat them has an explicit dependency for the dependency mapping. + //We can call it immediately since dojo.i18n is part of dojo.xd.js. + dojo.i18n._preloadLocalizations.apply(dojo.i18n._preloadLocalizations, dep.slice(1)); + break; + } + + //The requireIf and requireAfterIf needs to be evaluated after the current resource is evaluated. + if(dep[0] == "requireAfterIf" || dep[0] == "requireIf"){ + newAfterDeps = newDeps; + newDeps = null; + } + return {requires: newDeps, requiresAfter: newAfterDeps}; //Object +} + +dojo._xdWalkReqs = function(){ + //summary: Internal xd loader function. + //Walks the requires and evaluates module resource contents in + //the right order. + var reqChain = null; + var req; + for(var i = 0; i < this._xdOrderedReqs.length; i++){ + req = this._xdOrderedReqs[i]; + if(this._xdDepMap[req]){ + reqChain = [req]; + reqChain[req] = true; //Allow for fast lookup of the req in the array + this._xdEvalReqs(reqChain); + } + } +} + +dojo._xdEvalReqs = function(/*Array*/reqChain){ + //summary: Internal xd loader function. + //Does a depth first, breadth second search and eval of required modules. + while(reqChain.length > 0){ + var req = reqChain[reqChain.length - 1]; + var res = this._xdDepMap[req]; + if(res){ + //Trace down any requires for this resource. + //START dojo._xdTraceReqs() inlining for small Safari 2.0 call stack + var reqs = res.requires; + if(reqs && reqs.length > 0){ + var nextReq; + for(var i = 0; i < reqs.length; i++){ + nextReq = reqs[i].name; + if(nextReq && !reqChain[nextReq]){ + //New req depedency. Follow it down. + reqChain.push(nextReq); + reqChain[nextReq] = true; + this._xdEvalReqs(reqChain); + } + } + } + //END dojo._xdTraceReqs() inlining for small Safari 2.0 call stack + + //Evaluate the resource. + var contents = this._xdContents[res.contentIndex]; + if(!contents.isDefined){ + var content = contents.content; + content["resourceName"] = contents["resourceName"]; + content["resourcePath"] = contents["resourcePath"]; + this._xdDefList.push(content); + contents.isDefined = true; + } + this._xdDepMap[req] = null; + + //Trace down any requireAfters for this resource. + //START dojo._xdTraceReqs() inlining for small Safari 2.0 call stack + var reqs = res.requiresAfter; + if(reqs && reqs.length > 0){ + var nextReq; + for(var i = 0; i < reqs.length; i++){ + nextReq = reqs[i].name; + if(nextReq && !reqChain[nextReq]){ + //New req depedency. Follow it down. + reqChain.push(nextReq); + reqChain[nextReq] = true; + this._xdEvalReqs(reqChain); + } + } + } + //END dojo._xdTraceReqs() inlining for small Safari 2.0 call stack + } + + //Done with that require. Remove it and go to the next one. + reqChain.pop(); + } +} + +dojo._xdClearInterval = function(){ + //summary: Internal xd loader function. + //Clears the interval timer used to check on the + //status of in-flight xd module resource requests. + clearInterval(this._xdTimer); + this._xdTimer = 0; +} + +dojo._xdWatchInFlight = function(){ + //summary: Internal xd loader function. + //Monitors in-flight requests for xd module resources. + + var noLoads = ""; + var waitInterval = (djConfig.xdWaitSeconds || 15) * 1000; + var expired = (this._xdStartTime + waitInterval) < (new Date()).getTime(); + + //If any xdInFlight are true, then still waiting for something to load. + //Come back later. If we timed out, report the things that did not load. + for(var param in this._xdInFlight){ + if(this._xdInFlight[param] === true){ + if(expired){ + noLoads += param + " "; + }else{ + return; + } + } + } + + //All done. Clean up and notify. + this._xdClearInterval(); + + if(expired){ + throw "Could not load cross-domain resources: " + noLoads; + } + + this._xdWalkReqs(); + + var defLength = this._xdDefList.length; + for(var i= 0; i < defLength; i++){ + var content = dojo._xdDefList[i]; + if(djConfig["debugAtAllCosts"] && content["resourceName"]){ + if(!this["_xdDebugQueue"]){ + this._xdDebugQueue = []; + } + this._xdDebugQueue.push({resourceName: content.resourceName, resourcePath: content.resourcePath}); + }else{ + //Evaluate the resource to bring it into being. + //Pass dojo in so that later, to support multiple versions of dojo + //in a page, we can pass which version of dojo to use. + content(dojo); + } + } + + //Evaluate any resources that were not evaled before. + //This normally shouldn't happen with proper dojo.provide and dojo.require + //usage, but providing it just in case. Note that these may not be executed + //in the original order that the developer intended. + //Pass dojo in so that later, to support multiple versions of dojo + //in a page, we can pass which version of dojo to use. + for(var i = 0; i < this._xdContents.length; i++){ + var current = this._xdContents[i]; + if(current.content && !current.isDefined){ + current.content(dojo); + } + } + + //Clean up for the next round of xd loading. + this._xdReset(); + + if(this["_xdDebugQueue"] && this._xdDebugQueue.length > 0){ + this._xdDebugFileLoaded(); + }else{ + this._xdNotifyLoaded(); + } +} + +dojo._xdNotifyLoaded = function(){ + //Clear inflight count so we will finally do finish work. + this._inFlightCount = 0; + + //Only trigger call loaded if dj_load_init has run. + if(this._initFired && !this._loadNotifying){ + this._callLoaded(); + } +} + +/*===== +dojo.isBrowser = { + // summary: True if the client is a web-browser + // example: + // if(dojo.isBrowser){ ... } +}; + +dojo.isFF = { + // summary: True if client is using FireFox browser. False otherwise. + // example: + // if(dojo.isFF && dojo.isFF > 1){ ... } +}; + +dojo.isIE = { + // summary: The major version if client it using Internet Explorer (or false) + // example: + // if(dojo.isIE && dojo.isIE > 6){ + // // we are IE7 + // } +}; + +dojo.isKhtml = { + // summary: True if client is using Khtml browsers (konqueror, et al) +}; + +dojo.isMozilla = { + // summary: True if client is using a Mozilla-based browser. +}; + +dojo.isOpera = { + // summary: True if client is using the Opera web browser +}; + +dojo.isSafari = { + // summary: True if client is using the Safari web browser + // example: + // | if(dojo.isSafari){ ... } + // + // example: + // Detect iPhone: + // | if(dojo.isSafari && (navigator.userAgent.indexOf("iPhone") < 0)){ + // | // we are iPhone. iPod touch reports "iPod" above + // | } +}; +=====*/ + +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([\?\.]|$)/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(!djConfig["baseUrl"]){ + djConfig["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){ + djConfig[x] = cfgo[x]; + } + } + break; // "first Dojo wins" + } + } + } + d.baseUrl = djConfig["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; + 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 geckoPos = dua.indexOf("Gecko"); + d.isMozilla = d.isMoz = ((geckoPos >= 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){} + + //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:")){ + djConfig.ieForceActiveXXhr=true; + } + + var cm = document["compatMode"]; + d.isQuirks = (cm == "BackCompat")||(cm == "QuirksMode")||(d.isIE < 6); + + // TODO: is the HTML LANG attribute relevant? + d.locale = djConfig.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase(); + + d._println = console.debug; + + // 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 || !djConfig.ieForceActiveXXhr){ + try{ http = new XMLHttpRequest(); }catch(e){} + } + if(!http){ + for(var i=0; i<3; ++i){ + var progid = dojo._XMLHTTP_PROGIDS[i]; + try{ + http = new ActiveXObject(progid); + }catch(e){ + last_e = e; + } + + if(http){ + dojo._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); + */ + + 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(typeof dojo["_khtmlTimer"] != 'undefined'){ + clearInterval(dojo._khtmlTimer); + delete dojo._khtmlTimer; + } + + if(dojo._inFlightCount == 0){ + dojo._modulesLoaded(); + } + } + + // 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.isMoz && (djConfig["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(/(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. + + document.write('' + + '' + ); + + // 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') + +//Load debug code if necessary. +// dojo.requireIf((djConfig["isDebug"] || djConfig["debugAtAllCosts"]), "dojo.debug"); + +//window.widget is for Dashboard detection +//The full conditionals are spelled out to avoid issues during builds. +//Builds may be looking for require/requireIf statements and processing them. +// dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && !djConfig["useXDomain"], "dojo.browser_debug"); +// dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && djConfig["useXDomain"], "dojo.browser_debug_xd"); + +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"]){ //_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 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 +} +=====*/ + +dojo.isFunction = (function(){ + var _isFunction = function(/*anything*/ it){ + return 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 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.xyzzy == undefined; // by definition + // | thinger.xyzzy == "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 object. + // 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(); + // | } + // | ); + + // argument juggling (deprecated) + if(dojo.isFunction(props)||(arguments.length>3)){ + dojo.deprecated("dojo.declare: for class '" + className + "' pass initializer function as 'constructor' property instead of as a separate argument.", "", "1.0"); + var c = props; + props = arguments[3] || {}; + props.constructor = c; + } + // process superclass argument + // var dd=dojo.declare, mixins=null; + var dd=arguments.callee, mixins=null; + if(dojo.isArray(superclass)){ + mixins = superclass; + superclass = mixins.shift(); + } + // construct intermediate classes for mixins + if(mixins){ + for(var i=0, m; i. + // 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, /*Optional, 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(cb, cbfn); + if(arguments.length > 2){ + enclosed = dojo.partial(enclosed, arguments, 2); + } + return this.addCallbacks(enclosed, enclosed); + }, + + addCallback: function(cb, cbfn){ + // summary: + // Add a single callback to the end of the callback sequence. + var enclosed = dojo.hitch(cb, cbfn); + if(arguments.length > 2){ + enclosed = dojo.partial(enclosed, arguments, 2); + } + return this.addCallbacks(enclosed, null); + }, + + addErrback: function(cb, cbfn){ + // summary: + // Add a single callback to the end of the callback sequence. + var enclosed = dojo.hitch(cb, cbfn); + if(arguments.length > 2){ + enclosed = dojo.partial(enclosed, arguments, 2); + } + return this.addCallbacks(null, enclosed); + }, + + 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: + // evaluates the passed string-form of a JSON object + // json: + // a string literal of a JSON item, for instance: + // '{ "foo": [ "bar", 1, { "baz": "thud" } ] }' + // return: + // An object, the result of the evaluation + + // FIXME: should this accept mozilla's optional second arg? + try { + return eval("(" + json + ")"); + }catch(e){ + console.debug(e); + return json; //FIXME: peller: we should document this behavior and/or throw instead. I prefer the latter. + } +} + +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: + // Create a JSON 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. + // + // return: + // a String representing the serialized version of the passed object. + + _indentStr = _indentStr || ""; + var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : ""; + var newLine = prettyPrint ? "\n" : ""; + var objtype = typeof it; + if(objtype == "undefined"){ + return "undefined"; + } + 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; + 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 ? " " : ""; + + // 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; + } + // 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 + "}"; +} + +} + +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 i = 0, step = 1, end = array.length; + if(findLast){ + i = end - 1; + step = end = -1; + } + for(i = fromIndex || i; 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*/arr, /*Function*/callback, /*Object?*/obj){ + // summary: + // for every item in arr, call callback with that item as its + // only parameter. + // description: + // Return values are ignored. This function + // corresponds (and wraps) the JavaScript 1.6 forEach 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, obj, callback); arr = _p[0]; + for(var i=0,l=_p[0].length; i1; }); + // 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*/arr, /*Function*/callback, /*Object?*/thisObject){ + // summary: + // Determines whether or not any item in the array satisfies the + // condition implemented by callback. + // description: + // The parameter thisObject may be used to + // scope the call to callback. The function signature is derived + // from the JavaScript 1.6 Array.some() function. More + // information on this can be found here: + // 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*/arr, /*Function*/func, /*Function?*/obj){ + // summary: + // applies a function to each element of an Array and creates + // an Array with the results + // description: + // Returns a new array constituted from the return values of + // passing each element of arr into unary_func. The obj parameter + // may be passed to enable the passed function to be called in + // that scope. In environments that support JavaScript 1.6, this + // function is a passthrough to the built-in map() function + // provided by Array instances. For details on this, 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, obj, func); arr = _p[0]; + var outArr = ((arguments[3]) ? (new arguments[3]()) : []); + for(var i=0;i1; }); + // returns [2, 3, 4] + + var _p = _getParts(arr, obj, 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"); + + + + + + + + + + + + + + + +(function(){ + if(djConfig.require){ + for(var x=0; x90)&&(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._getIeDispatcher = function(){ + return function(){ + var ap=Array.prototype, h=dojo._ie_listener.handlers, c=arguments.callee, ls=c._listeners, t=h[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)){ + h[ls[i]].apply(this, arguments); + } + } + return r; + } + } + // 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: + // similar to other library's "$" function, takes a + // string representing a DOM id or a DomNode + // and returns the corresponding DomNode. If a Node is + // passed, this function is a no-op. Returns a + // single DOM node or null, working around several + // browser-specific bugs to do so. + // id: String|DOMNode + // DOM id or DOM Node + // doc: DocumentElement + // Document to work in. Defaults to the current value of + // dojo.doc. Can be used to retreive + // 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){ return; } + if(!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){ + if(dojo.isString(id)){ + return (doc || dojo.doc).getElementById(id); + }else{ + return id; // DomNode + } + } +} +/*===== +} +=====*/ + +(function(){ + /* + dojo.createElement = function(obj, parent, position){ + // TODO: need to finish this! + } + */ + + var _destroyContainer = null; + 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 = dojo.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 = dojo.byId(node); + ancestor = dojo.byId(ancestor); + while(node){ + if(node === ancestor){ + return true; // Boolean + } + node = node.parentNode; + } + }catch(e){ return -1; /* squelch */ } + 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 = dojo.byId(node); + if(dojo.isMozilla){ + node.style.MozUserSelect = selectable ? "" : "none"; + }else if(dojo.isKhtml){ + node.style.KhtmlUserSelect = selectable ? "auto" : "none"; + }else if(dojo.isIE){ + node.unselectable = selectable ? "" : "on"; + dojo.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 in relation to ref based on position + // node: + // id or reference to node to place relative to refNode + // refNode: + // id or reference of node 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 = dojo.byId(node); + refNode = dojo.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{ + refNode.appendChild(node); + return true; // boolean + } + break; + 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(dojo.isIE /*|| dojo.isOpera*/){ + var _dcm = document.compatMode; + // client code may have to adjust if compatMode varies across iframes + dojo.boxModel = (_dcm=="BackCompat")||(_dcm=="QuirksMode")||(dojo.isIE<6) ? "border-box" : "content-box"; + } + + // ============================= + // 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. + // + // This is a faux declaration to take pity on the doc tool + +/*===== + dojo.getComputedStyle = function(node){ + // summary: + // Returns a "computed style" object. + // description: + // get "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. + // + // 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(dojo.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(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(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; + } + } + + // 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 = (dojo.isIE ? function(node){ + try{ + return (node.filters.alpha.opacity / 100); // Number + }catch(e){ + return 1; // Number + } + } : function(node){ + return dojo.getComputedStyle(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 = (dojo.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) + 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="+(opacity*100)+")"; + node.style.filter = o; + } + if(node.nodeName.toLowerCase() == "tr"){ + dojo.query("> td", node).forEach(function(i){ + i.style.filter = o; + }); + } + return opacity; + } : function(node, opacity){ + return node.style.opacity = opacity; + } + ); + + var _pixelNamesCache = { + width: true, height: true, left: true, top: true + }; + var _toStyleValue = function(node, type, value){ + type = type.toLowerCase(); + if(_pixelNamesCache[type] === true){ + return dojo._toPixelValue(node, value) + }else if(_pixelNamesCache[type] === false){ + return value; + }else{ + if(dojo.isOpera && type == "cssText"){ + // FIXME: add workaround for #2855 here + } + if( + (type.indexOf("margin") >= 0) || + // (type.indexOf("border") >= 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) + ){ + _pixelNamesCache[type] = true; + return dojo._toPixelValue(node, value) + }else{ + _pixelNamesCache[type] = false; + return value; + } + } + } + + // public API + + dojo.style = function(/*DomNode|String*/ node, /*String*/style, /*String?*/value){ + // summary: + // gets or sets a style property on node. If 2 arguments are + // passed, acts as a getter. If value is passed, acts as a setter + // for the property. + // 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"). + // value: + // optional. If passed, sets value on the node for style, handling + // cross-browser concerns. + var n=dojo.byId(node), args=arguments.length, op=(style=="opacity"); + if(args==3){ + return op ? dojo._setOpacity(n, value) : n.style[style] = value; /*Number*/ + } + if(args==2 && op){ + return dojo._getOpacity(n); + } + var s = dojo.getComputedStyle(n); + 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), + 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(/*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', + px=dojo._toPixelValue, + 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=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, 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), + 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")){ + // 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 = dojo._getMarginExtents(node, s); + var l = node.offsetLeft - me.l, t = node.offsetTop - me.t; + if(dojo.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 = dojo._getBorderExtents(p, pcs); + l += be.l, t += be.t; + } + } + } + }else if(dojo.isOpera){ + // On Opera, offsetLeft includes the parent's border + 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, 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=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; + } + // On Opera, offsetLeft includes the parent's border + 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, computedStyle){ + var s=computedStyle||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 + }; + } + + // 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"; + 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(/*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 dojo.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. + var bb = dojo._usesBorderBox(node); + if(bb){ + var pb = dojo._getPadBorderExtents(node, computedStyle); + if(widthPx>=0){ widthPx += pb.w; } + if(heightPx>=0){ heightPx += pb.h; } + } + dojo._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 || dojo.getComputedStyle(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=dojo._usesBorderBox(node), + pb=bb ? _nilExtents : dojo._getPadBorderExtents(node, s), + mb=dojo._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); } + dojo._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: + // optional. 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=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); // 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: + // optional. 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 ? dojo._getContentBox(n, s) : dojo._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 = dojo.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 = 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(){ + //FIXME: could check html and body tags directly instead of computed style? need to ignore case, accept empty values + return !("_bodyLtr" in dojo) ? + dojo._bodyLtr = dojo.getComputedStyle(dojo.body()).direction == "ltr" : + dojo._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 = dojo.doc.documentElement; + if(dojo.isIE >= 7){ + return {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}; // Object + }else{ + // IE 6.0 + return {x: dojo._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. + if(dojo.isIE && !dojo._isBodyLtr()){ + var de = dojo.doc.documentElement; + return scrollLeft + de.clientWidth - de.scrollWidth; // Integer + } + return scrollLeft; // Integer + } + + dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll){ + // summary: + // Gets the absolute position of the passed element based on the + // document itself. 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 + }; + var hasScroll = false; + + // targetBoxType == "border-box" + var db = dojo.body(); + if(dojo.isIE){ + var client = node.getBoundingClientRect(); + var offset = dojo._getIeDocumentElementOffset(); + ret.x = client.left - offset.x; + ret.y = client.top - offset.y; + }else if(ownerDocument["getBoxObjectFor"]){ + // mozilla + var bo = ownerDocument.getBoxObjectFor(node); + ret.x = bo.x - _sumAncestorProperties(node, "scrollLeft"); + ret.y = bo.y - _sumAncestorProperties(node, "scrollTop"); + }else{ + if(node["offsetParent"]){ + hasScroll = true; + 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(dojo.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(dojo.isOpera || (dojo.isSafari >= 3)){ 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(!dojo.isOpera || n>0){ + ret.x += isNaN(n) ? 0 : n; + } + var m = curnode["offsetTop"]; + ret.y += isNaN(m) ? 0 : m; + 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(hasScroll || includeScroll){ + var scroll = dojo._docScroll(); + var m = hasScroll ? (!includeScroll ? -1 : 0) : 1; + ret.y += m*scroll.y; + ret.x += m*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(). 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=dojo.byId(node), s=gcs(n), mb=dojo._getMarginBox(n, s); + var abs = dojo._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" + return dojo.isIE ? "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" + } + + var _hasAttr = function(/*DomNode*/node, /*String*/name){ + var attr = node.getAttributeNode(name); + return attr ? attr.specified : false; + } + + 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 + return _hasAttr(dojo.byId(node), _fixAttrName(name)); // boolean + } + + dojo.attr = function(/*DomNode|String*/node, /*String*/name, /*String?*/value){ + // summary: + // Gets or sets an attribute on an HTML element. + // description: + // If 2 arguments are passed, acts as a getter. + // If a third argument is passed, acts as a setter. + // 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 for the attribute (optional) + // 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 + node = dojo.byId(node); + name = _fixAttrName(name); + if(arguments.length == 3){ + node.setAttribute(name, value); + return undefined; + }else{ + // should we access this attribute via a property or + // via getAttribute()? + var prop = _attrProps[name.toLowerCase()]; + if(prop){ + return node[prop]; + }else{ + return _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 + dojo.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){ + arr.constructor = dojo.NodeList; + dojo._mixin(arr, dojo.NodeList.prototype); + return arr; + } + + 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); + return this; // dojo.NodeList non-standard return to allow easier chaining + }, + + // 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); + }, + + 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 + 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; // String||dojo.NodeList + }, + + styles: function(/*===== property, value =====*/){ + // summary: + // Deprecated. Use NodeList.style instead. Will be removed in + // Dojo 1.1. 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 + d.deprecated("NodeList.styles", "use NodeList.style instead", "1.1"); + return this.style.apply(this, arguments); + }, + + addClass: function(/*String*/ className){ + // summary: + // adds the specified class to every node in the list + // + this.forEach(function(i){ d.addClass(i, className); }); + return this; + }, + + removeClass: function(/*String*/ className){ + this.forEach(function(i){ d.removeClass(i, className); }); + return this; + }, + + // FIXME: toggleClass()? 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]; + position = position||"last"; + + for(var x=0; x" child element selectors + // * "#foo" style ID selectors + // * "*" universal 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 + // * ":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 those selector types as per the CSS 3 sepc + // 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 + // * "~", the immediately preceeded-by sibling selector + // * "+", the preceeded-by sibling selector + // * 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: + // ----------------------------- + // FIXME + // + // NOTE: elementsById is not currently supported + // NOTE: ignores xpath-ish queries for now + // + // query: String + // The CSS3 expression to match against. For details on the syntax of + // CSS3 selectors, see: + // http://www.w3.org/TR/css3-selectors/#selectors + // root: String|DOMNode? + // A node (or string ID of a node) to scope the search from. Optional. + // returns: + // 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. +=====*/ + +;(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 getQueryParts = function(query){ + // summary: state machine for query tokenization + if(query.charAt(query.length-1) == ">"){ + 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= 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; + // FIXME: need to add support for ~ and + + if(tqp.oper == ">"){ + prefix = "/"; + // prefix = "/child::node()"; + tqp = qparts.shift(); + }else{ + prefix = "//"; + // prefix = "/descendant::node()" + } + + // get the tag name (if any) + + xpath += prefix + tqp.tag; + + // 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; + // var parent = d.body(); // FIXME + // FIXME: 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. + + // FIXME: need to add support for alternate roots + 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 _filterDown = function(element, queryParts, matchArr, idx){ + 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 = element[childNodesName]; + 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=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; + } + }, + /* non standard! + "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("*"); + }, + ">": 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 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 == ">"){ + 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)&& + ( + (true) // || + // (query.indexOf("[") == -1) || + // (query.indexOf("=") == -1) + ) + ){ + // 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 + 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 new 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(query, root){ + // see dojo.query for docs + + 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); + } + + // FIXME: should support more methods on the return than the stock array. + return _zip(getQueryFunc(query)(root||d.doc)); + } + + /* + // exposing these was a mistake + d.query.attrs = attrs; + 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: + // + //
    + // + // + // + // + //
    + // + // 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; + }).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: + // return 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&" + // + // returns 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(!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){ + // 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 _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(kwArgs){ + // 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: + } + =====*/ + + /*===== + dojo.__ioCallbackArgs = function(kwArgs){ + // 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. + } + =====*/ + + + + 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; + ioArgs.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{ + ioArgs.xhr.setRequestHeader(hdr, args.headers[hdr]); + } + } + } + // FIXME: is this appropriate for all content types? + ioArgs.xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType); + // FIXME: set other headers here! + try{ + ioArgs.xhr.send(ioArgs.query); + }catch(e){ + dfd.cancel(); + } + _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle); + 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.__xhrArgs = function(kwArgs){ + // 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. + } + =====*/ + + dojo.xhrGet = function(/*dojo.__xhrArgs*/ args){ + // summary: + // Sends an HTTP GET request to the server. + var dfd = _makeXhrDeferred(args); + _d._ioAddQueryToUrl(dfd.ioArgs); + return _doIt("GET", dfd); // dojo.Deferred + } + + dojo.xhrPost = function(/*dojo.__xhrArgs*/ args){ + //summary: + // Sends an HTTP POST request to the server. + return _doIt("POST", _makeXhrDeferred(args)); // 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 _doIt("PUT", _makeXhrDeferred(args)); // 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. + var dfd = _makeXhrDeferred(args); + _d._ioAddQueryToUrl(dfd.ioArgs); + return _doIt("DELETE", dfd); // 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 +*/ + +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 + } +} + +dojo.declare("dojo._Animation", null, { + // summary + // A generic animation object that fires callbacks into it's handlers + // object at various states + // + constructor: function(/*Object*/ args){ + dojo.mixin(this, args); + if(dojo.isArray(this.curve)){ + /* curve: Array + pId: a */ + this.curve = new dojo._Line(this.curve[0], this.curve[1]); + } + }, + + // duration: Integer + // The time in milliseonds the animation will take to run + duration: 1000, + +/*===== + // 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 (synhcronous) + 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: 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. + if(this[evt]){ + this[evt].apply(this, args||[]); + } + 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 d = delay||_t.delay; + var _p = dojo.hitch(_t, "_play", gotoStart); + if(d > 0){ + setTimeout(_p, d); + 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._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"); + } + } + return _t; // dojo._Animation + } +}); + +(function(){ + var d = dojo; + 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 = dojo.connect(runner, "run", this, "_cycle"); + ctr++; + } + if(!timer){ + timer = setInterval(dojo.hitch(runner, "run"), this.rate); + } + }; + + dojo._Animation.prototype._stopTimer = function(){ + dojo.disconnect(this._timer); + this._timer = null; + ctr--; + if(!ctr){ + clearInterval(timer); + timer = null; + } + }; + + 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(kwArgs){ + // duration: Integer? + // Duration of the animation in milliseconds. + // easing: Function? + // An easing function. + } + =====*/ + + 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){ + 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.animateProperty = function(/*Object*/ args){ + // summary: + // Returns an animation that will transition the properties of + // node defined in 'args' depending how they are defined in + // 'args.properties' + // + // description: + // The foundation of most dojo.fx animations, dojo.AnimateProperty + // will take an object of "properties" corresponding to style + // properties, and animate them in parallel over a set duration. + // + // args.node can be a String or a DomNode reference + // + // example: + // | 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(); + // + + 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. + 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 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) : 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){ + // console.debug("it's a color!"); + 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); + } + // console.debug("start:", prop.start); + // console.debug("end:", prop.end); + } + this.curve = new PropLine(pm); + }); + d.connect(anim, "onAnimate", anim, function(propValues){ + // try{ + for(var s in propValues){ + // console.debug(s, propValues[s], this.node.style[s]); + d.style(this.node, s, propValues[s]); + // this.node.style[s] = propValues[s]; + } + // }catch(e){ console.debug(dojo.toJson(e)); } + }); + return anim; // dojo._Animation + } +})(); + +} + +if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.i18n"] = true; +dojo.provide("dojo.i18n"); + +/*===== +dojo.i18n = { + // summary: Utility classes to enable loading of resources for internationalization (i18n) +}; +=====*/ + +dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){ + // summary: + // Returns an Object containing the localization for a given resource + // bundle in a package, matching the specified locale. + // description: + // Returns a hash containing name/value pairs in its prototypesuch + // that values can be easily overridden. Throws an exception if the + // bundle is not found. Bundle must have already been loaded by + // dojo.requireLocalization() or by a build optimization step. NOTE: + // try not to call this method as part of an object property + // definition (var foo = { bar: dojo.i18n.getLocalization() }). In + // some loading situations, the bundle may not be available in time + // for the object definition. Instead, call this method inside a + // function that is run after all modules load or the page loads (like + // in dojo.adOnLoad()), or in a widget lifecycle method. + // packageName: + // package which is associated with this resource + // bundleName: + // the base filename of the resource bundle (without the ".js" suffix) + // locale: + // the variant to load (optional). By default, the locale defined by + // the host environment: dojo.locale + + locale = dojo.i18n.normalizeLocale(locale); + + // look for nearest locale match + var elements = locale.split('-'); + var module = [packageName,"nls",bundleName].join('.'); + var bundle = dojo._loadedModules[module]; + if(bundle){ + var localization; + for(var i = elements.length; i > 0; i--){ + var loc = elements.slice(0, i).join('_'); + if(bundle[loc]){ + localization = bundle[loc]; + break; + } + } + if(!localization){ + localization = bundle.ROOT; + } + + // make a singleton prototype so that the caller won't accidentally change the values globally + if(localization){ + var clazz = function(){}; + clazz.prototype = localization; + return new clazz(); // Object + } + } + + throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale); +}; + +dojo.i18n.normalizeLocale = function(/*String?*/locale){ + // summary: + // Returns canonical form of locale, as used by Dojo. + // + // description: + // All variants are case-insensitive and are separated by '-' as specified in RFC 3066. + // If no locale is specified, the dojo.locale is returned. dojo.locale is defined by + // the user agent's locale unless overridden by djConfig. + + var result = locale ? locale.toLowerCase() : dojo.locale; + if(result == "root"){ + result = "ROOT"; + } + return result; // String +}; + +dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){ + // summary: + // See dojo.requireLocalization() + // description: + // Called by the bootstrap, but factored out so that it is only + // included in the build when needed. + + var targetLocale = dojo.i18n.normalizeLocale(locale); + var bundlePackage = [moduleName, "nls", bundleName].join("."); + // NOTE: + // When loading these resources, the packaging does not match what is + // on disk. This is an implementation detail, as this is just a + // private data structure to hold the loaded resources. e.g. + // tests/hello/nls/en-us/salutations.js is loaded as the object + // tests.hello.nls.salutations.en_us={...} The structure on disk is + // intended to be most convenient for developers and translators, but + // in memory it is more logical and efficient to store in a different + // order. Locales cannot use dashes, since the resulting path will + // not evaluate as valid JS, so we translate them to underscores. + + //Find the best-match locale to load if we have available flat locales. + var bestLocale = ""; + if(availableFlatLocales){ + var flatLocales = availableFlatLocales.split(","); + for(var i = 0; i < flatLocales.length; i++){ + //Locale must match from start of string. + if(targetLocale.indexOf(flatLocales[i]) == 0){ + if(flatLocales[i].length > bestLocale.length){ + bestLocale = flatLocales[i]; + } + } + } + if(!bestLocale){ + bestLocale = "ROOT"; + } + } + + //See if the desired locale is already loaded. + var tempLocale = availableFlatLocales ? bestLocale : targetLocale; + var bundle = dojo._loadedModules[bundlePackage]; + var localizedBundle = null; + if(bundle){ + if(djConfig.localizationComplete && bundle._built){return;} + var jsLoc = tempLocale.replace(/-/g, '_'); + var translationPackage = bundlePackage+"."+jsLoc; + localizedBundle = dojo._loadedModules[translationPackage]; + } + + if(!localizedBundle){ + bundle = dojo["provide"](bundlePackage); + var syms = dojo._getModuleSymbols(moduleName); + var modpath = syms.concat("nls").join("/"); + var parent; + + dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){ + var jsLoc = loc.replace(/-/g, '_'); + var translationPackage = bundlePackage + "." + jsLoc; + var loaded = false; + if(!dojo._loadedModules[translationPackage]){ + // Mark loaded whether it's found or not, so that further load attempts will not be made + dojo["provide"](translationPackage); + var module = [modpath]; + if(loc != "ROOT"){module.push(loc);} + module.push(bundleName); + var filespec = module.join("/") + '.js'; + loaded = dojo._loadPath(filespec, null, function(hash){ + // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath + var clazz = function(){}; + clazz.prototype = parent; + bundle[jsLoc] = new clazz(); + for(var j in hash){ bundle[jsLoc][j] = hash[j]; } + }); + }else{ + loaded = true; + } + if(loaded && bundle[jsLoc]){ + parent = bundle[jsLoc]; + }else{ + bundle[jsLoc] = parent; + } + + if(availableFlatLocales){ + //Stop the locale path searching if we know the availableFlatLocales, since + //the first call to this function will load the only bundle that is needed. + return true; + } + }); + } + + //Save the best locale bundle as the target locale bundle when we know the + //the available bundles. + if(availableFlatLocales && targetLocale != bestLocale){ + bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')]; + } +}; + +(function(){ + // If other locales are used, dojo.requireLocalization should load them as + // well, by default. + // + // Override dojo.requireLocalization to do load the default bundle, then + // iterate through the extraLocale list and load those translations as + // well, unless a particular locale was requested. + + var extra = djConfig.extraLocale; + if(extra){ + if(!extra instanceof Array){ + extra = [extra]; + } + + var req = dojo.i18n._requireLocalization; + dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){ + req(m,b,locale, availableFlatLocales); + if(locale){return;} + for(var i=0; i 0; i--){ + searchlist.push(elements.slice(0, i).join('-')); + } + searchlist.push(false); + if(down){searchlist.reverse();} + + for(var j = searchlist.length - 1; j >= 0; j--){ + var loc = searchlist[j] || "ROOT"; + var stop = searchFunc(loc); + if(stop){ break; } + } +}; + +dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){ + // summary: + // Load built, flattened resource bundles, if available for all + // locales used in the page. Only called by built layer files. + + function preload(locale){ + locale = dojo.i18n.normalizeLocale(locale); + dojo.i18n._searchLocalePath(locale, true, function(loc){ + for(var i=0; i0){_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.interval0){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");} 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 index 000000000..2bd7329d5 --- /dev/null +++ b/webapps/cometd/examples/dojo/dojox/cometd.js.uncompressed.js @@ -0,0 +1,1142 @@ +/* + Copyright (c) 2004-2007, 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. +*/ + +if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.AdapterRegistry"] = true; +dojo.provide("dojo.AdapterRegistry"); + +dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){ + // summary: + // A registry to make contextual calling/searching easier. + // description: + // Objects of this class keep list of arrays in the form [name, check, + // wrap, directReturn] that are used to determine what the contextual + // result of a set of checked arguments is. All check/wrap functions + // in this registry should be of the same arity. + // example: + // | // create a new registry + // | var reg = new dojo.AdapterRegistry(); + // | reg.register("handleString", + // | dojo.isString, + // | function(str){ + // | // do something with the string here + // | } + // | ); + // | reg.register("handleArr", + // | dojo.isArray, + // | function(arr){ + // | // do something with the array here + // | } + // | ); + // | + // | // now we can pass reg.match() *either* an array or a string and + // | // the value we pass will get handled by the right function + // | reg.match("someValue"); // will call the first function + // | reg.match(["someValue"]); // will call the second + + this.pairs = []; + this.returnWrappers = returnWrappers || false; // Boolean +} + +dojo.extend(dojo.AdapterRegistry, { + register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){ + // summary: + // register a check function to determine if the wrap function or + // object gets selected + // name: + // a way to identify this matcher. + // check: + // a function that arguments are passed to from the adapter's + // match() function. The check function should return true if the + // given arguments are appropriate for the wrap function. + // directReturn: + // If directReturn is true, the value passed in for wrap will be + // returned instead of being called. Alternately, the + // AdapterRegistry can be set globally to "return not call" using + // the returnWrappers property. Either way, this behavior allows + // the registry to act as a "search" function instead of a + // function interception library. + // override: + // If override is given and true, the check function will be given + // highest priority. Otherwise, it will be the lowest priority + // adapter. + this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]); + }, + + match: function(/* ... */){ + // summary: + // Find an adapter for the given arguments. If no suitable adapter + // is found, throws an exception. match() accepts any number of + // arguments, all of which are passed to all matching functions + // from the registered pairs. + for(var i = 0; i < this.pairs.length; i++){ + var pair = this.pairs[i]; + if(pair[1].apply(this, arguments)){ + if((pair[3])||(this.returnWrappers)){ + return pair[2]; + }else{ + return pair[2].apply(this, arguments); + } + } + } + throw new Error("No match found"); + }, + + unregister: function(name){ + // summary: Remove a named adapter from the registry + + // FIXME: this is kind of a dumb way to handle this. On a large + // registry this will be slow-ish and we can use the name as a lookup + // should we choose to trade memory for speed. + for(var i = 0; i < this.pairs.length; i++){ + var pair = this.pairs[i]; + if(pair[0] == name){ + this.pairs.splice(i, 1); + return true; + } + } + return false; + } +}); + +} + +if(!dojo._hasResource["dojo.io.script"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.io.script"] = true; +dojo.provide("dojo.io.script"); + +/*===== +dojo.io.script.__ioArgs = function(kwArgs){ + // summary: + // All the properties described in the dojo.__ioArgs type, apply to this + // type as well, EXCEPT "handleAs". It is not applicable to + // dojo.io.script.get() calls, since it is implied by the usage of + // "callbackParamName" (response will be a JSONP call returning JSON) + // or "checkString" (response is pure JavaScript defined in + // the body of the script that was attached). The following additional + // properties are allowed for dojo.io.script.get(): + // callbackParamName: String + // The URL parameter name that indicates the JSONP callback string. + // For instance, when using Yahoo JSONP calls it is normally, + // callbackParamName: "callback". For AOL JSONP calls it is normally + // callbackParamName: "c". + // checkString: String + // A string of JavaScript that when evaluated like so: + // "typeof(" + checkString + ") != 'undefined'" + // being true means that the script fetched has been loaded. + // Do not use this if doing a JSONP type of call (use callbackParamName instead). +} +=====*/ + +dojo.io.script = { + get: function(/*dojo.io.script.__ioArgs*/args){ + // summary: + // sends a get request using a dynamically created script tag. + var dfd = this._makeScriptDeferred(args); + var ioArgs = dfd.ioArgs; + dojo._ioAddQueryToUrl(ioArgs); + + this.attach(ioArgs.id, ioArgs.url); + dojo._ioWatch(dfd, this._validCheck, this._ioCheck, this._resHandle); + return dfd; + }, + + attach: function(/*String*/id, /*String*/url){ + // summary: + // creates a new + + + + + +

    Echo test

    +

    + Echo data to ONLY this client using RPC style messaging over + cometd. Requires a server side component at /service/echo which echos + responses directly to the client. +

    +
    + Echo: +
    +
    
    +    
    +
    diff --git a/webapps/cometd/examples/simplechat/cometdchat.htm b/webapps/cometd/examples/simplechat/cometdchat.htm
    new file mode 100644
    index 000000000..f6183fa30
    --- /dev/null
    +++ b/webapps/cometd/examples/simplechat/cometdchat.htm
    @@ -0,0 +1,113 @@
    +
    +
    Comet Simple Chat Application + + + + + + + +
    + +
    +
    +Screen name:  +
    Type a screen name and click 'Connect' +
    + +
    +
    +
    +
    Is my screen name
    +[ENTER] sends message
    +
    + + + diff --git a/webapps/cometd/examples/tictactoe/resources/0.gif b/webapps/cometd/examples/tictactoe/resources/0.gif new file mode 100644 index 0000000000000000000000000000000000000000..0198a166a734ccdb1649cde8abb48032a87e3464 GIT binary patch literal 92 zcmZ?wbhEHbG-5DfXkcLY|NlP&1B2pE7Dgb&paUX6G7L<{E&VG`zvW*%XUnbb&G+{F t=5KrCG3{CBvR9|Jz2iRq$>;jF-siv0egDVL!_|Id(Z>!SEp=7~YXIJ^CdmK* literal 0 HcmV?d00001 diff --git a/webapps/cometd/examples/tictactoe/resources/1.gif b/webapps/cometd/examples/tictactoe/resources/1.gif new file mode 100644 index 0000000000000000000000000000000000000000..9b5268a552aedee1df0173f24f2f9b6d2ddfd191 GIT binary patch literal 2596 zcmaKt>06Td0)~I@%f2Znq9`ivxCZ1xg$0OcWkux_mKm0nDNShBXw!7P;D%;~X{NS_ zOO9<5E|skVX{KhStybosZBEOiX_HNLJahhm^L)NP-uLxH&6f#gCGr6ueEtMbC=>>R z`S$JG*RNmEX!O+7)aTEi|N84MB9Zvz%a?!t`G-ItkVqsv9zQZNLMD^P#>TK%?CaOB zCnhE)Cnp^o9GaV(|Ni^$AAkHYFffq8VBEfaTcuL@`}@Ct|K7&N=GwJu3l}bY{`~pN zmoM$@?MFvP0RSIAe*E<5)BgSYA3uKV=jZq4%^NzM&SWxMT3X(|ee3G#YGq}G!{MBr zoui|pA3S*Q;lqdd^XI>K@nZY-?RIu{@7}%Z@9!TT9&T!C`s0s3N=iy>ZEYzO%FUZM zLqkJ@gM%#=OKWTE`1tts>(}qzy{p&jySuw@-MVFMZCzYk{QK{}@7S>;ARyqzjT@1X zkz%oU&z?Oj7Aq<$YQ~HiZf+6pm zJ(`n~bMfNEva+(=+}zWrPcK-o;N;1ZSy@?)jg1>OZj{MnY&N^RygW5E_3YWRQmIrR z5WITzipS&8XtYO<9zA>Z%+u3z(V|7mmMv4O)m>d(ot>R4R;+mV@ZtIM=SxdVLqbB< zu3d{D$e(}y$>DIGK7D%Nz=6%1H{ZE)CoC*1FE4N3zI{iI9EpjE*}He|Pe1)6kw`9F zxbXAOKPM$6RaaM=Os0s4h=zuSEnBv1+qO-qR3;}UpF4L>C=^y!R^GdJ&&$he<;s=r z?(S47b@%Sw_wV12kB>ig?3k~w@1aA7mM>q<=kpIAK0IsIEN^e`nKNf{x!kz8xQ>pF z4I4J>+`04AsZ;CLt;@{JOi4+(bm@{vBx-MOKXc|xP*6~Kc=!)L{GioppFDZ8YSpUW ze*4YQ(J?kQwxFQk#EBCg9v(F{H425Is;X*ma8NFn8;!=IqN2HT=Pq5k)ND4VrKKe# zB&4UOySTX2)z$g<_{^Czr>(7R@#4ixmMqcfbQKj9>({Rz7#KKy{P^tIvw!*JmtTMV zH8C-9_3G8NwY68STse5~;I3V}zJLD?!!QIv)6>)c`u2b3_5}?A@CgL`kNIB{06z`h z5?Ikj!{wc@O?(9y2G$HOg{h5aHX+%VnJI$`HKK8{DIORVO9}YJ>_(xa{5i;lSnE5> z4zdYH!`T5Ut&i`r+BKE#$2i%?^HJ6WWly}H+LVhb5@i;4xK&jPt{1-GNlwW87w&x% zz$}qM_bTC_=+W_ORSka3b+H3AoA6rPl2u*2k2fg6iF5bUO_}FBa#@KzZM5^X*RlZ( zfQqPnLqb4H>T9zl620wQ$kd?tt)@gTqjYZpF9&F{jJ}5&=A0%)ICpNW?MT5e?nM*b8pn%`thE1(_6(_B>`*plmf(vXJ6z+5jA zp(wU4VzvY=X99eOx_ia})kq<8TYL>Fs8uIru&x2ta7)$ zMRv0@YjY@FjiN65;pn;uyuUtbA7Mf$>h+li#E_avQJXwAdTt}at#JbdGj6LjI1DF7 zWQMFJm70+E$>R_-5%@gHF`i|(FZ&p;?jRXhwK&6MuSmk1J=TmSapFBq4ICDi;^V6- z)%YPWJA!sn>L!MG1dGOY+qY>=BziS$zQp<9 zSkGi@F%HSRj*hHRP;|?*_q)zb)i%aLE&s;vr5&n%pr)VHfAj&?Nr;)_aXDOH>Es~E z%)%#`%+^-q6%{ZS56gT4FXxGH4wu;y_-uqxOmu6bGOs5o^x>OfQy-ch*E%*hmC^Mf z@O6KxD^#)Zihb`H?lciaPoZAS3SgrUef7kD9$uhZ3>wlXaNe!H+R5uht!9)r)^Erz0+5^u;0XdF)&QMTbChJhosnpO;yp;T;q+5iM%`jAzV zBa}6LLvchsN~svJvMDt?-*?Ni89bGHge0L%;t*yBurn&J6NH>|ghmVHqQZ;&Y3;-H8FDCcVs>%?;5Ysn(_3PJWEknmHiB zZ6AfvWZ!-LUC+^LL~vN4$PeZ!F=|UL(YsGT%z&Ita-P6`Jx$Zlzo+i&f=Ag(3{d~eM!2;PWBU+jy>m49!~sAH9m5p(CDL0! zq+K$#dGU~zJkQdO7T5&HS~XUy^pGK43J1y~oHn&-Jq^?xI=7}FrV;H>kLXfWH6ata zIJ+u0oz2u~wty@pR`lo3y8W8K(_eQ;L9$OQJ3vvH#H^sDIa^}^R9lCGQ_NN?2iSxI z0XiU8qk~!5HtC}B4Zw(8)*E2Q0S(qq4%u6_<6JYr{?UG5KOm+orJB(pe1LDX;KsXP z3d1P9+2#!@yue1c(#MZf-Jx^ZP}nC%+=8G;7tEN+=Ap7+vAT?Zuk!YWB>zfi`r0cg zi7lo{6S3}`bxk@xAgc`IFpGumDninkCV=KDA#x^?ap>zHmRy55ZiI0g#ksVi*g}y83{0JdV??lDvV<5-h-3JZ?G>6lYx z#5GSA&PiEhLTAdgG=b&D+%XMG%6-8m#i3{Ex(ET+hFd@?AaF$xQHgNsOaR*k4p0mr hz~0t-QD>U(4WXV_dch^Jv#IJ)@2gOIPlyfX{1>vs6t(~W literal 0 HcmV?d00001 diff --git a/webapps/cometd/examples/tictactoe/resources/10.gif b/webapps/cometd/examples/tictactoe/resources/10.gif new file mode 100644 index 0000000000000000000000000000000000000000..6cc7a6cfe70b29bb0188292867000878c79955b6 GIT binary patch literal 2586 zcmaKr`9sT%1Asrr?zXk7w(f0Rt5#N$B5WNxO(lfNI(#F=A_-xutwYzMh{DKAiHt%B z+bX4lm0qGSm8&-t<$k}t-}fK*K0iD^J- z2*l5yKT#;uuV24@{P^+p>sKTaiAJNpfB*jN+czU4BMOBwIXO8tHa0UeGc`4Z!{M-4 z?DX{X$B!Rhy?R9;5Wal*^6As3&!0b&NTd%RKD>PS($LV*z`y{5!4QeW+qZAW#Kasu zdi2qwM-vkhqobpEJpRs|JFTs)RaI3~Dm5-HE-x={;lhP3E-v@)-+%Y+owc>KMx$Y| zSg&8dHa0eX|Ni~cr%%ny%(z_c+qZ8O3dNs){+X4P_3+`tnwpx##KeSzgunj!ORv|D zjErzN92*;(=g*(__xF#Fk6Tz+sMYGnk00;fzyIdVn?plG<>lpMGWo=b6LPuy?%lf! z7A&Z!s2Cm|UcGvCaBwh>$78eEZf@+yLPQxw~j`m&7C`U&6+jy=FQ8`&p&kNP(eX~ zL?X$^$k?!9Lr_pqZEdZOkI$JiXNrr9#bWWng9ifx1Fv4aDwRr=N~NWx<<_lR3kwVP z?b}yUQj(sYE)WP-ty;BY$&!2b?p?WZW!bW2D^{$CkB>im_;6=uCxgKV2??>XvI+J~clGUD*69;9NBv}v@q*q1QH3N6NJNd6P?98nZHVjd21)U7x z)0sI%W#^1m9CP;+O{YNSm&O92QDwGd$Y?i%`bD6X7zrMylXwpSjJ<1YGi9_h57m}r z!jLwGF0Upc0HE@^T2RTciJ;A<`XPttIK-A;6`HfhRela#*Gi&9+VY5^^f)yCQYO() z?F|ueUdt{Akm|C)Z14+56ivv>?}toR#a-1UCm6F{Q_&6b0SRc;ZnFL)xUk5Gl-OVE{pPG|(YaT-L#*4W9)ti^?ZqY)G^k zfGL@?%6!ru+6suFAO-=*;A*JXglr?>`+;ZtVVkWz;eN*6GA6JH@@B&HTN7yz!JSDa za<-|q1)FM%ArbTFe91ETb^c7+h1&PQpq}m9hoj`r?2FS zhai_}3BhttD5y~-eaLUamt!b7Fk;#sKqi~__gJ;$x3-&Bd-DYq!!aiLP7xC->6!xU zz$g=%&omL^ozJ=&g3(omO_>*esi7xiigMbghSxY$VXPUStb5(Ju5bFmT`Fv!d^ zE1V1)S3K+UH3Se6F>EJQi#i>TM~ScL2^LBXnH$9lWC+X0zrSalcMY9urvQ+Is}Hnn z&1MbkD7wCjB$5>BV7#LSK{<_Z!0;fet||PEA`LQTE+5qb7P37PFeeqQc8|;CpEndO zL5Vet1~MTyG#+c|*%NFk$)+mUxEQ7k*WYSEZ_*l8sUTya8tvI~3VD~~OA((vKJA)7 zuze?I2$e93^ofOR21uP`nF~jgr7+NNNK#xk7R1sk0US?J608GBhu;CmEa#WvyqVaT zS~<_Oui$MH3Zw_-Fiz|9OsAIvZY@%W#Ih!LrXMSKla|&46t8>DN0<#T2IH9r0wPB- zA>)An4wwb~D_`L@)Cy1&b`m1L(b#t|_vYl4I*Yw()QRRcR;Q{xsknY&zOL0ZP$f5@ z{jr##)|#Icpdz}$j4tc^>|}aao%#u|3TpFe2+L+<*6jb1Sq+^R6)e_)G(OJ<^iUy( zZd^=`jT8{N^ah(fwJ;}{PWUd72mIC^w$6=ZH9?L@P#{3&6_R`M~;s(9_R3aB5)gy$iy1Q74=e z!;rVUgn(lhL?0uU7!RH{vJjB~8n2Lx8KH*qd>-jMfPI#rK_n-8VKZp@;TuT_yEbcW zCi%r^Wu50rg@BB6g)Q)xGZ|X7lO-!ZazIqLqnnR^F@;!`7%!+kT&~_fqM<&HYW@^b@5#H@>a1i@!Qn>;m+ zN}vZ74ZvXlnwmk95-?5;?FIluQA!CZ5||>kg}H##AcZM>27f+)=1E{0vkTmq3KEsK XjSPfcvBbv8j?g!5RX;c&2rT>uYP1;jF-siv0egDVL!_|Id(Z>!SEp=7~YXIJ^CdmK* literal 0 HcmV?d00001 diff --git a/webapps/cometd/examples/tictactoe/resources/tictactoe.css b/webapps/cometd/examples/tictactoe/resources/tictactoe.css new file mode 100644 index 000000000..008999d4c --- /dev/null +++ b/webapps/cometd/examples/tictactoe/resources/tictactoe.css @@ -0,0 +1,74 @@ + +body { + background-color : #ffffcc; + text-align: center; +} +a:link { + color : black; +} +a:visited { + color : black; +} +a:hover { + color : yellow; +} +p.instructions { + background-color : #ccffcc; + padding-left : 20%; + padding-right : 20%; +} +table { + border: none; + margin: 0 auto; +} +td#cell0 { + padding: 10px ; + border-bottom: thin solid black; + border-right: thin solid black; +} +td#cell1 { + padding: 10px ; + border-bottom: thin solid black; + border-right: thin solid black; + border-left: thin solid black; +} +td#cell2 { + padding: 10px ; + border-left: thin solid black; + border-bottom: thin solid black; +} +td#cell3 { + padding: 10px ; + border-top: thin solid black; + border-right: thin solid black; + border-bottom: thin solid black; +} +td#cell4 { + padding: 10px ; + border-top: thin solid black; + border-right: thin solid black; + border-left: thin solid black; + border-bottom: thin solid black; +} +td#cell5 { + padding: 10px ; + border-top: thin solid black; + border-left: thin solid black; + border-bottom: thin solid black; +} +td#cell6 { + padding: 10px ; + border-top: thin solid black; + border-right: thin solid black; +} +td#cell7 { + padding: 10px ; + border-top: thin solid black; + border-right: thin solid black; + border-left: thin solid black; +} +td#cell8 { + padding: 10px ; + border-top: thin solid black; + border-left: thin solid black; +} diff --git a/webapps/cometd/examples/tictactoe/ttt1.htm b/webapps/cometd/examples/tictactoe/ttt1.htm new file mode 100644 index 000000000..b6b560af8 --- /dev/null +++ b/webapps/cometd/examples/tictactoe/ttt1.htm @@ -0,0 +1,252 @@ + + + + Tic Tac Toe - Cometd Example + + + + + + + + + + + +

    Tic Tac Toe

    + + + + + + + + + + + + + + + + +
    +

    New game, click to start.


    +

    + + diff --git a/webapps/cometd/examples/timesync/index.html b/webapps/cometd/examples/timesync/index.html new file mode 100644 index 000000000..eaf506d87 --- /dev/null +++ b/webapps/cometd/examples/timesync/index.html @@ -0,0 +1,41 @@ + + + + Comet echo RPC + + + + + + + +

    TimeSync test

    +

    + This test uses the timesync extension to calculate a timeoffset with the server. + 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. + The accuracy should improve as each connect response is received. +

    +
    +
    + + diff --git a/webapps/cometd/index.html b/webapps/cometd/index.html new file mode 100644 index 000000000..29e461686 --- /dev/null +++ b/webapps/cometd/index.html @@ -0,0 +1,7 @@ + +

    Cometd demo

    + +

    +Try the Simple Chat Demo.
    +Try the Tic-Tac-Toe Demo.
    +

    diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 20a39d555..8f1321c2c 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -33,7 +33,7 @@
    - + 671650
    45279
    -- 2.11.0