You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by kn...@apache.org on 2008/09/01 22:14:18 UTC

svn commit: r691044 - in /wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng: ./ js/ json/

Author: knopp
Date: Mon Sep  1 13:14:17 2008
New Revision: 691044

URL: http://svn.apache.org/viewvc?rev=691044&view=rev
Log:
woot. timer with resume

Added:
    wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxTimerBehavior.java   (with props)
    wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/IAjaxIndicatorAware.java   (with props)
Modified:
    wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxBehavior.java
    wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestAttributes.java
    wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestTarget.java
    wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/js/wicket-ajax-ng.js
    wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/json/JSONObject.java

Modified: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxBehavior.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxBehavior.java?rev=691044&r1=691043&r2=691044&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxBehavior.java (original)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxBehavior.java Mon Sep  1 13:14:17 2008
@@ -79,7 +79,7 @@
 
 	private final static ResourceReference YUI_COMBO = new JavascriptResourceReference(
 		AjaxBehavior.class, "js/yui-combo.js");
-	
+
 	/**
 	 * Wicket javascript namespace.
 	 */
@@ -87,27 +87,26 @@
 
 	public void renderHead(Component component, IHeaderResponse response)
 	{
-				
+
 		/*
-		response.renderJavascriptReference(YUI_BASE);
-		response.renderJavascriptReference(YUI_OOP);
-		response.renderJavascriptReference(YUI_EVENT);
-		response.renderJavascriptReference(YUI_DOM);
-		response.renderJavascriptReference(YUI_NODE);
-		response.renderJavascriptReference(YUI_IO);
-		response.renderJavascriptReference(YUI_GET);
-		*/
+		 * response.renderJavascriptReference(YUI_BASE);
+		 * response.renderJavascriptReference(YUI_OOP);
+		 * response.renderJavascriptReference(YUI_EVENT);
+		 * response.renderJavascriptReference(YUI_DOM);
+		 * response.renderJavascriptReference(YUI_NODE); response.renderJavascriptReference(YUI_IO);
+		 * response.renderJavascriptReference(YUI_GET);
+		 */
 		response.renderJavascriptReference(YUI_COMBO);
-		response.renderJavascriptReference(AJAX_NG);		
+		response.renderJavascriptReference(AJAX_NG);
 
 		CharSequence prefix = RequestCycle.get().urlFor(AjaxRequestTarget.DUMMY);
 
 		StringBuilder config = new StringBuilder();
-		
+
 		config.append("(function() {\n");
-		
+
 		config.append("var gs = " + WICKET_NS + ".ajax.globalSettings;\n");
-		
+
 		config.append("gs.urlPrefix='");
 		config.append(prefix);
 		config.append("';\n");
@@ -115,7 +114,7 @@
 		config.append("gs.defaultPageId='");
 		config.append(getPageId(component.getPage()));
 		config.append("';\n");
-		
+
 		config.append("gs.urlParamComponentId='");
 		config.append(AjaxUrlCodingStrategy.PARAM_COMPONENT_ID);
 		config.append("';\n");
@@ -139,15 +138,15 @@
 		config.append("gs.urlParamBehaviorIndex='");
 		config.append(AjaxUrlCodingStrategy.PARAM_BEHAVIOR_INDEX);
 		config.append("';\n");
-		
+
 		config.append("gs.urlParamUrlDepth='");
 		config.append(AjaxUrlCodingStrategy.PARAM_URL_DEPTH);
 		config.append("';\n");
-		
+
 		config.append("gs.urlDepthValue=");
 		config.append(getUrlDepth());
 		config.append(";\n");
-		
+
 		config.append("})();");
 
 		response.renderJavascript(config, WICKET_NS + "-Config");
@@ -172,7 +171,7 @@
 			return -1;
 		}
 	}
-	
+
 	public void afterRender(Component component)
 	{
 	}
@@ -181,10 +180,20 @@
 	{
 	}
 
+	protected boolean allowBindToMultipleComponents()
+	{
+		return true;
+	}
+
 	public void bind(Component component)
 	{
 		if (boundComponents.contains(component) == false)
 		{
+			if (!allowBindToMultipleComponents() && !boundComponents.isEmpty())
+			{
+				throw new IllegalStateException("Behavior '" + getClass().getName() +
+					" can only be bound to one component.");
+			}
 			boundComponents.add(component);
 			component.setOutputMarkupId(true);
 		}
@@ -222,6 +231,13 @@
 
 		renderAttributes(component, getAttributes(), o);
 
+		if (allowAjaxIndicator())
+		{
+			o.put("i", findIndicatorId(component));	
+		}		
+
+		postprocessConfiguration(o, component);
+
 		return o.toString();
 	}
 
@@ -280,7 +296,7 @@
 			return null;
 		}
 	}
-	
+
 	private void renderAttributes(Component component, AjaxRequestAttributes attributes,
 		JSONObject o)
 	{
@@ -316,6 +332,11 @@
 		renderFunctionList(o, "ua", attributes.getUrlArgumentMethods());
 	}
 
+	protected void postprocessConfiguration(JSONObject object, Component component)
+	{
+
+	}
+
 	public void detach(Component component)
 	{
 	}
@@ -361,7 +382,18 @@
 		}
 		return script;
 	}
-	
+
+	/**
+	 * Creates {@link AjaxRequestAttributes} instance. If behavior needs to change the behaviors,
+	 * this is the method to override and wrap the attributes.
+	 * 
+	 * @return {@link AjaxRequestAttributes} instance.
+	 */
+	public AjaxRequestAttributes initAttributes()
+	{
+		return new AjaxRequestAttributes();
+	}
+
 	/**
 	 * Returns attributes for Ajax Request.
 	 * 
@@ -369,6 +401,48 @@
 	 */
 	public AjaxRequestAttributes getAttributes()
 	{
-		return new AjaxRequestAttributes();
+		return initAttributes();
+	}
+
+	protected String getAjaxIndicatorMarkupId()
+	{
+		return null;
+	}
+
+	protected boolean allowAjaxIndicator()
+	{
+		return true;
+	}
+	
+	private String findIndicatorId(Component component)
+	{
+		String id = getAjaxIndicatorMarkupId();
+		if (id != null)
+		{
+			return id;
+		}
+		if (component instanceof IAjaxIndicatorAware)
+		{
+			id = ((IAjaxIndicatorAware)component).getAjaxIndicatorMarkupId();
+			if (id != null)
+			{
+				return id;
+			}
+		}
+
+		Component parent = component.getParent();
+		while (parent != null)
+		{
+			if (parent instanceof IAjaxIndicatorAware)
+			{
+				id = ((IAjaxIndicatorAware)parent).getAjaxIndicatorMarkupId();
+				if (id != null)
+				{
+					return id;
+				}
+			}
+			parent = parent.getParent();
+		}
+		return null;
 	}
 }

Modified: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestAttributes.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestAttributes.java?rev=691044&r1=691043&r2=691044&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestAttributes.java (original)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestAttributes.java Mon Sep  1 13:14:17 2008
@@ -567,5 +567,5 @@
 		{
 			return false;
 		}
-	}
+	}	
 }

Modified: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestTarget.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestTarget.java?rev=691044&r1=691043&r2=691044&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestTarget.java (original)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxRequestTarget.java Mon Sep  1 13:14:17 2008
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 package org.apache.wicket.ajaxng;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -181,6 +180,10 @@
 		{
 			entries.iterator().next().getComponent().getPage().detach();
 		}
+		else
+		{
+			page.detach();
+		}
 	}
 
 	/**
@@ -1084,6 +1087,26 @@
 	}
 
 	/**
+	 * Static method that returns current {@link AjaxRequestTarget} or <code>null</code> of no
+	 * {@link AjaxRequestTarget} is available.
+	 * 
+	 * @return {@link AjaxRequestTarget} instance if current request is an Ajax request,
+	 *         <code>null</code> otherwise.
+	 */
+	public static AjaxRequestTarget get()
+	{
+		final RequestCycle requestCycle = RequestCycle.get();
+		if (requestCycle != null)
+		{
+			if (requestCycle.getRequestTarget() instanceof AjaxRequestTarget)
+			{
+				return (AjaxRequestTarget)requestCycle.getRequestTarget();
+			}
+		}
+		return null;
+	}
+
+	/**
 	 * Dummy AJAX request target instance used by {@link AjaxBehavior} to generate AJAX URL prefix.
 	 */
 	public static final AjaxRequestTarget DUMMY = new AjaxRequestTarget();

Added: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxTimerBehavior.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxTimerBehavior.java?rev=691044&view=auto
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxTimerBehavior.java (added)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxTimerBehavior.java Mon Sep  1 13:14:17 2008
@@ -0,0 +1,261 @@
+/*
+ * 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.wicket.ajaxng;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.markup.html.IHeaderResponse;
+import org.apache.wicket.util.time.Duration;
+
+/**
+ * A behavior that generates an AJAX update callback at a regular interval.
+ * 
+ * @author Matej Knopp
+ */
+public abstract class AjaxTimerBehavior extends AjaxBehavior
+{
+	private static final long serialVersionUID = 1L;
+
+	/** The update interval */
+	private Duration updateInterval;
+
+	/** Indicates whether the behavior is active or stopped */
+	private boolean stopped = false;
+
+	/** Flag to prevent multiple script rendering in one request */
+	private boolean headRendered = false;
+
+	/**
+	 * Construct.
+	 * 
+	 * @param updateInterval
+	 *            Duration between AJAX callbacks
+	 */
+	public AjaxTimerBehavior(final Duration updateInterval)
+	{
+		if (updateInterval == null || updateInterval.getMilliseconds() <= 0)
+		{
+			throw new IllegalArgumentException("Invalid update interval");
+		}
+		this.updateInterval = updateInterval;
+	}
+
+	/**
+	 * Sets the update interval duration. This method should only be called within the
+	 * {@link #onTimer(AjaxRequestTarget)} method.
+	 * 
+	 * @param updateInterval
+	 * @return <code>this</code>
+	 */
+	protected final AjaxTimerBehavior setUpdateInterval(Duration updateInterval)
+	{
+		if (updateInterval == null || updateInterval.getMilliseconds() <= 0)
+		{
+			throw new IllegalArgumentException("Invalid update interval");
+		}
+		this.updateInterval = updateInterval;
+		return this;
+	}
+
+	/**
+	 * Returns the update interval
+	 * 
+	 * @return The update interval
+	 */
+	public final Duration getUpdateInterval()
+	{
+		return updateInterval;
+	}
+
+	private Component getComponent()
+	{
+		if (getBoundComponents().isEmpty())
+		{
+			return null;
+		}
+		else
+		{
+			return getBoundComponents().iterator().next();
+		}
+	}
+
+	/**
+	 * Returns unique Id for this behavior. The id will be used as RequestQueueItem token and also
+	 * to cancel pending timeout on client when {@link #stop()} is called.
+	 * 
+	 * @return unique id for this behavior
+	 */
+	protected String getId()
+	{
+		Component component = getComponent();
+		int index = component.getBehaviors().indexOf(this);
+		StringBuilder b = new StringBuilder();
+		b.append(component instanceof Page ? "_page_" : component.getMarkupId());
+		b.append("___");
+		b.append(index);
+		return b.toString();
+	}
+
+	@Override
+	protected boolean allowBindToMultipleComponents()
+	{
+		return false;
+	}
+
+	private String renderSetTimeoutJavascript()
+	{
+		String attrs = renderAttributes(getComponent());
+
+		StringBuilder b = new StringBuilder();
+		b.append("W.timerManager.setTimeout(");
+		b.append(updateInterval.getMilliseconds());
+		b.append(",");
+		b.append(attrs);
+		b.append(");");
+
+		return b.toString();
+	}
+
+	private String renderRemoveTimeoutJavascript()
+	{
+		String attrs = renderAttributes(getComponent());
+
+		StringBuilder b = new StringBuilder();
+		b.append("W.timerManager.removeTimeout(");
+		b.append(attrs);
+		b.append(");");
+
+		return b.toString();
+	}
+
+	private void renderAjax(AjaxRequestTarget target)
+	{
+		if (!headRendered)
+		{
+			if (stopped)
+			{
+				target.appendJavascript(renderRemoveTimeoutJavascript());
+			}
+			else
+			{
+				target.appendJavascript(renderSetTimeoutJavascript());
+			}
+			headRendered = true;
+		}
+	}
+
+	/**
+	 * Returns whether this behavior has been stopped.
+	 * 
+	 * @see #stop()
+	 * @see #resume()
+	 * 
+	 * @return <code>true<code> if the behavior has been stopped, <code>false</code> otherwise.
+	 */
+	public boolean isStopped()
+	{
+		return stopped;
+	}
+
+	/**
+	 * Stops this behavior. The {@link #onTimer(AjaxRequestTarget)} method will not be called
+	 * until the timer is enabled again.
+	 * 
+	 * @see #isStopped()
+	 * @see #resume()
+	 */
+	public void stop()
+	{
+		if (stopped == false)
+		{
+			stopped = true;
+			AjaxRequestTarget target = AjaxRequestTarget.get();
+			if (target != null)
+			{
+				renderAjax(target);
+			}
+		}
+	}
+
+	/**
+	 * Resumes stopped behavior.
+	 * 
+	 * @see #isStopped()
+	 * @see #stop()
+	 */
+	public void resume()
+	{
+		if (stopped == true)
+		{
+			stopped = false;
+			AjaxRequestTarget target = AjaxRequestTarget.get();
+			if (target != null)
+			{
+				renderAjax(target);
+			}
+		}
+	}
+
+	@Override
+	public void renderHead(Component component, IHeaderResponse response)
+	{
+		super.renderHead(component, response);
+
+		// only set the timeout on regular (non-ajax) request when the behavior is enabled 
+		AjaxRequestTarget target = AjaxRequestTarget.get();
+		if (target == null && stopped == false)
+		{
+			response.renderOnDomReadyJavascript(renderSetTimeoutJavascript());
+		}
+	}
+
+	@Override
+	public void detach(Component component)
+	{
+		super.detach(component);
+
+		headRendered = false;
+	}
+
+	abstract protected void onTimer(AjaxRequestTarget target);
+
+	public final void respond(AjaxRequestTarget target)
+	{
+		onTimer(target);
+		renderAjax(target);
+	}
+
+	@Override
+	public AjaxRequestAttributes initAttributes()
+	{
+		return new AjaxRequestAttributes(super.initAttributes())
+		{
+			@Override
+			public Boolean isRemovePrevious()
+			{
+				// Do not queue the requests on client
+				return true;
+			}
+
+			@Override
+			public String getToken()
+			{				
+				return getId();
+			}
+		};
+	}
+}

Propchange: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/AjaxTimerBehavior.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/IAjaxIndicatorAware.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/IAjaxIndicatorAware.java?rev=691044&view=auto
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/IAjaxIndicatorAware.java (added)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/IAjaxIndicatorAware.java Mon Sep  1 13:14:17 2008
@@ -0,0 +1,41 @@
+/*
+ * 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.wicket.ajaxng;
+
+
+/**
+ * This interface makes it trivial to use busy indicators for ajax requests. This interface can be
+ * implemented by a component that has an ajax behavior attached to it, or any parent of the
+ * component, or by the ajax behavior itself. If this is the case javascript will be added
+ * automatically that will show a markup element pointed to by the
+ * {@link #getAjaxIndicatorMarkupId()} markup id attribute when the ajax request begins, and hide it
+ * when the ajax requests succeeds or fails.
+ * <p>
+ * If both a component and a behavior implement this interface, the component will take precedence.
+ * 
+ * @since 1.2
+ * 
+ * @author Igor Vaynberg (ivaynberg)
+ * 
+ */
+public interface IAjaxIndicatorAware
+{
+	/**
+	 * @return the value of the markup id attribute of the indicating element
+	 */
+	String getAjaxIndicatorMarkupId();
+}

Propchange: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/IAjaxIndicatorAware.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/js/wicket-ajax-ng.js
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/js/wicket-ajax-ng.js?rev=691044&r1=691043&r2=691044&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/js/wicket-ajax-ng.js (original)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/js/wicket-ajax-ng.js Mon Sep  1 13:14:17 2008
@@ -1465,6 +1465,11 @@
      *                                        after the RequestQueueItem instance is created. The methods 
      *                                        will get the request queue instance passed as first argument.
      *                                        
+     *   i, indicatorId          - String     Optional. Id of the indicator element. The element is assumed 
+     *                                        to have style="display:none" set. The style is removed on 
+     *                                        beginning of Ajax request and restored when request is 
+     *                                        processed.
+     *                                        
 	 */
 	var RequestQueueItem = function(attributes)
 	{
@@ -1515,7 +1520,8 @@
 			errorHandlers:      m(a.errorHandlers      || a.e,   gs.errorHandlers),
 			urlArguments:         a.urlArguments       || a.u    || null,
 			urlArgumentMethods: m(a.urlArgumentMethods || a.ua,  gs.urlArgumentMethods),
-			requestQueueItem:   m(a.requestQueueItem   || a.rqi, gs.requestQueueItem)
+			requestQueueItem:   m(a.requestQueueItem   || a.rqi, gs.requestQueueItem),
+			indicatorId:          a.indicatorId        || a.i    || null
 		}
 		
 		log.trace("RequestQueue", "Creating New Item", this.attributes);				
@@ -2237,6 +2243,32 @@
 		W.focusManager.restoreFocus();
 	}
 	
+	var showIndicator = function(requestQueueItem)
+	{
+		var a = requestQueueItem.attributes;
+		if (a.indicatorId != null)
+		{
+			var e = W.$(a.indicatorId);
+			if (e != null)
+			{
+				e.style.display="";
+			}
+		}
+	}
+	
+	var hideIndicator = function(requestQueueItem)
+	{
+		var a = requestQueueItem.attributes;
+		if (a.indicatorId != null)
+		{
+			var e = W.$(a.indicatorId);
+			if (e != null)
+			{
+				e.style.display="none";
+			}
+		}
+	}
+	
 	var globalSettings = 
 	{
 		defaultRequestTimeout: 60000,
@@ -2244,10 +2276,10 @@
 		defaultPageId: -1,
 		defaultToken: null,
 		defaultRemovePrevious: false,
-		beforeHandlers: [],
+		beforeHandlers: [showIndicator],
 		preconditions: [],
-		successHandlers: [],
-		errorHandlers: [],
+		successHandlers: [hideIndicator],
+		errorHandlers: [hideIndicator],
 		urlPostProcessors: [],
 		urlArgumentMethods: [ defaultArgumentMethod ],
 		requestQueueItem: [],
@@ -2573,6 +2605,44 @@
 		W.attachEventHandler(event, attributes, f);
 	}
 	
+	var TimerManager = function()
+	{
+		this.timers = {};
+	};
+	
+	TimerManager.prototype = 
+	{
+		setTimeout: function(duration, attributes)
+		{
+			var token = attributes.t;
+			if (token != null)
+			{
+				if (this.timers[token] == null)
+				{
+					var f = bind(function() 
+					{
+						this.timers[token] = null;
+						var item = new RequestQueueItem(attributes);
+						W.ajax.requestQueue.add(item);
+					}, this);
+					this.timers[token] = window.setTimeout(f, duration);
+				}
+			}
+		},
+		removeTimeout: function(attributes)
+		{
+			var token = attributes.t;
+			var t = this.timers[token];
+			if (t != null)
+			{
+				window.clearTimeout(t);
+				this.timers[token] = null;
+			}
+		},
+	}
+	
+	W.timerManager = new TimerManager();
+	
 	window.W = W;			
 	
 });

Modified: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/json/JSONObject.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/json/JSONObject.java?rev=691044&r1=691043&r2=691044&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/json/JSONObject.java (original)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/json/JSONObject.java Mon Sep  1 13:14:17 2008
@@ -17,19 +17,17 @@
 package org.apache.wicket.ajaxng.json;
 
 /*
- Copyright (c) 2002 JSON.org
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ * Copyright (c) 2002 JSON.org
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
  */
 
 import java.io.IOException;
@@ -68,7 +66,11 @@
  * produces the string <code>{"JSON": "Hello, World"}</code>.
  * <p>
  * The texts produced by the <code>toString</code> methods strictly conform to the JSON sysntax
- * rules. 
+ * rules.
+ * <p>
+ * Note that this class has been slightly modified not to quote parameter names unless necessary.
+ * i.e. it will produce <code>{ JSON: "Hello, World"}</code> rather than
+ * <code>{"JSON": "Hello, World"}<code>
  * 
  * @author JSON.org
  * @version 2
@@ -1390,13 +1392,13 @@
 				String key = k.toString();
 				if (needsQuote(key))
 				{
-					writer.write(quote(k.toString()));	
+					writer.write(quote(k.toString()));
 				}
 				else
 				{
 					writer.write(k.toString());
 				}
-				
+
 				writer.write(':');
 				Object v = this.myHashMap.get(k);
 				if (v instanceof JSONObject)