You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2012/01/13 14:47:16 UTC

[1/2] git commit: WICKET-4326 Make AjaxRequestTarget an interface and move the impl to AjaxRequestHandler

Updated Branches:
  refs/heads/master 2989237bf -> c2b44cca9


WICKET-4326
Make AjaxRequestTarget an interface and move the impl to AjaxRequestHandler


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/c2b44cca
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/c2b44cca
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/c2b44cca

Branch: refs/heads/master
Commit: c2b44cca9b37d58bc8e23eea6e62e1dcfb330719
Parents: 2989237
Author: martin-g <mg...@apache.org>
Authored: Sat Jan 7 11:25:52 2012 +0200
Committer: martin-g <mg...@apache.org>
Committed: Fri Jan 13 15:45:22 2012 +0200

----------------------------------------------------------------------
 .../src/main/java/org/apache/wicket/Behaviors.java |    3 +-
 .../wicket/ajax/AbstractAjaxTimerBehavior.java     |    8 +-
 .../org/apache/wicket/ajax/AjaxEventBehavior.java  |    8 +-
 .../org/apache/wicket/ajax/AjaxRequestHandler.java | 1129 +++++++++++++
 .../org/apache/wicket/ajax/AjaxRequestTarget.java  | 1258 +--------------
 .../ajax/AjaxRequestTargetListenerCollection.java  |    2 +-
 .../wicket/ajax/AjaxSelfUpdatingTimerBehavior.java |    2 +-
 .../AjaxFormChoiceComponentUpdatingBehavior.java   |    6 +-
 .../form/AjaxFormComponentUpdatingBehavior.java    |    5 +-
 .../wicket/ajax/markup/html/AjaxFallbackLink.java  |    4 -
 .../wicket/ajax/markup/html/form/AjaxButton.java   |    8 -
 .../wicket/ajax/markup/html/form/AjaxCheckBox.java |    2 +-
 .../ajax/markup/html/form/AjaxFallbackButton.java  |    2 +-
 .../wicket/markup/head/ResourceAggregator.java     |    8 +-
 .../head/filter/FilteringHeaderResponse.java       |    2 +-
 .../org/apache/wicket/markup/html/image/Image.java |    4 +-
 .../wicket/markup/html/image/NonCachingImage.java  |    2 +-
 .../wicket/markup/html/tree/AbstractTree.java      |   10 +-
 .../apache/wicket/markup/html/tree/BaseTree.java   |    4 +-
 .../protocol/http/AjaxEnclosureListener.java       |    4 +-
 .../wicket/protocol/http/WebApplication.java       |   19 +-
 .../apache/wicket/request/cycle/RequestCycle.java  |   27 +
 .../wicket/util/tester/BaseWicketTester.java       |   17 +-
 .../apache/wicket/util/tester/WicketTester.java    |    5 +-
 .../java/org/apache/wicket/MockPanelWithLink.java  |    2 +-
 ...ResponseAtInterceptPageExceptionInAjaxTest.java |    7 -
 .../apache/wicket/ajax/AjaxRequestHandlerTest.java |  361 +++++
 .../apache/wicket/ajax/AjaxRequestTargetTest.java  |  361 -----
 .../wicket/markup/html/HeaderContributorTest.java  |    4 +-
 .../markup/html/TransparentWithAjaxUpdatePage.java |    2 +-
 .../internal/TraditionalEnclosureAjaxPage.java     |    1 -
 .../AbstractTransformerBehaviorTest.java           |    4 +-
 .../wicket/request/handler/PageIdPoliticTest.java  |    1 -
 .../wicket/response/filter/ResponseFilterTest.java |    1 -
 .../wicket/util/tester/WicketTesterTest.java       |    1 -
 .../wicket/extensions/yui/calendar/DatePicker.java |    7 +-
 .../wicket/examples/ajax/builtin/EffectsPage.java  |    2 +-
 .../wicket/examples/ajax/builtin/GuestBook.java    |    8 +-
 .../wicket/examples/ajax/builtin/LinksPage.java    |    4 +-
 .../wicket/examples/ajax/builtin/TodoList.java     |    2 +-
 .../examples/ajax/builtin/tree/BaseTreePage.java   |    1 -
 .../examples/resourcedecoration/HomePage.java      |    2 +-
 .../apache/wicket/examples/source/SourcesPage.java |   34 +-
 .../org/apache/wicket/examples/StartExamples.java  |    2 +
 .../ajax/markup/html/AjaxEditableLabel.java        |   14 +-
 .../ajax/markup/html/AjaxIndicatorAppender.java    |    2 +-
 .../ajax/markup/html/AjaxLazyLoadPanel.java        |    4 +-
 .../autocomplete/AbstractAutoCompleteBehavior.java |    2 +-
 .../ajax/markup/html/modal/ModalWindow.java        |   16 +-
 .../data/sort/AjaxFallbackOrderByLink.java         |    2 +-
 .../repeater/data/table/AjaxNavigationToolbar.java |    2 +-
 .../captcha/kittens/KittenCaptchaPanel.java        |   29 +-
 .../markup/html/tree/DefaultAbstractTree.java      |    2 +-
 .../wicket/extensions/rating/RatingPanel.java      |    2 +-
 .../apache/wicket/proxy/util/InterfaceObject.java  |    1 -
 55 files changed, 1720 insertions(+), 1702 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/Behaviors.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/Behaviors.java b/wicket-core/src/main/java/org/apache/wicket/Behaviors.java
index eb7e25d..2936d8a 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Behaviors.java
+++ b/wicket-core/src/main/java/org/apache/wicket/Behaviors.java
@@ -20,7 +20,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.behavior.InvalidBehaviorIdException;
 import org.apache.wicket.model.IDetachable;
@@ -129,7 +128,7 @@ final class Behaviors implements IDetachable
 	 * THIS IS WICKET INTERNAL ONLY. DO NOT USE IT.
 	 * 
 	 * Traverses all behaviors and calls detachModel() on them. This is needed to cleanup behavior
-	 * after render. This method is necessary for {@link AjaxRequestTarget} to be able to cleanup
+	 * after render. This method is necessary for {@link org.apache.wicket.ajax.AjaxRequestTarget} to be able to cleanup
 	 * component's behaviors after header contribution has been done (which is separated from
 	 * component render).
 	 */

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java
index eb765df..e6fd084 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java
@@ -52,11 +52,7 @@ public abstract class AbstractAjaxTimerBehavior extends AbstractDefaultAjaxBehav
 	 */
 	public AbstractAjaxTimerBehavior(final Duration updateInterval)
 	{
-		if (updateInterval == null || updateInterval.getMilliseconds() <= 0)
-		{
-			throw new IllegalArgumentException("Invalid update interval");
-		}
-		this.updateInterval = updateInterval;
+		setUpdateInterval(updateInterval);
 	}
 
 	/**
@@ -132,7 +128,7 @@ public abstract class AbstractAjaxTimerBehavior extends AbstractDefaultAjaxBehav
 	
 	/**
 	 * 
-	 * @see org.apache.wicket.ajax.AbstractDefaultAjaxBehavior#respond(org.apache.wicket.ajax.AjaxRequestTarget)
+	 * @see org.apache.wicket.ajax.AbstractDefaultAjaxBehavior#respond(AjaxRequestTarget)
 	 */
 	@Override
 	protected final void respond(final AjaxRequestTarget target)

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxEventBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxEventBehavior.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxEventBehavior.java
index c385a90..a011cb8 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxEventBehavior.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxEventBehavior.java
@@ -17,14 +17,10 @@
 package org.apache.wicket.ajax;
 
 import org.apache.wicket.Component;
-import org.apache.wicket.IClusterable;
 import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
-import org.apache.wicket.ajax.attributes.ThrottlingSettings;
-import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
 import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.time.Duration;
 
 /**
  * An ajax behavior that is attached to a certain client-side (usually javascript) event, such as
@@ -99,7 +95,7 @@ public abstract class AjaxEventBehavior extends AbstractDefaultAjaxBehavior
 
 			js.append(");");
 
-			AjaxRequestTarget target = AjaxRequestTarget.get();
+			AjaxRequestTarget target = getComponent().getRequestCycle().find(AjaxRequestTarget.class);
 			if (target == null)
 			{
 				response.render(OnDomReadyHeaderItem.forScript(js.toString()));
@@ -130,7 +126,7 @@ public abstract class AjaxEventBehavior extends AbstractDefaultAjaxBehavior
 
 	/**
 	 * 
-	 * @see org.apache.wicket.ajax.AbstractDefaultAjaxBehavior#respond(org.apache.wicket.ajax.AjaxRequestTarget)
+	 * @see org.apache.wicket.ajax.AbstractDefaultAjaxBehavior#respond(AjaxRequestTarget)
 	 */
 	@Override
 	protected final void respond(final AjaxRequestTarget target)

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
new file mode 100644
index 0000000..cb95e09
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
@@ -0,0 +1,1129 @@
+/*
+ * 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.ajax;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.Page;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.head.internal.HeaderResponse;
+import org.apache.wicket.markup.html.internal.HtmlHeaderContainer;
+import org.apache.wicket.markup.parser.filter.HtmlHeaderSectionHandler;
+import org.apache.wicket.markup.repeater.AbstractRepeater;
+import org.apache.wicket.request.IRequestCycle;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.Response;
+import org.apache.wicket.request.component.IRequestablePage;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.handler.PageProvider;
+import org.apache.wicket.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.request.handler.logger.PageLogData;
+import org.apache.wicket.request.http.WebRequest;
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.markup.head.HeaderItem;
+import org.apache.wicket.markup.head.OnLoadHeaderItem;
+import org.apache.wicket.response.StringResponse;
+import org.apache.wicket.response.filter.IResponseFilter;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.lang.Generics;
+import org.apache.wicket.util.string.AppendingStringBuffer;
+import org.apache.wicket.util.string.Strings;
+import org.apache.wicket.util.visit.IVisit;
+import org.apache.wicket.util.visit.IVisitor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A request target that produces ajax response envelopes used on the client side to update
+ * component markup as well as evaluate arbitrary javascript.
+ * <p>
+ * A component whose markup needs to be updated should be added to this target via
+ * AjaxRequestTarget#add(Component) method. Its body will be rendered and added to the
+ * envelope when the target is processed, and refreshed on the client side when the ajax response is
+ * received.
+ * <p>
+ * It is important that the component whose markup needs to be updated contains an id attribute in
+ * the generated markup that is equal to the value retrieved from Component#getMarkupId(). This can
+ * be accomplished by either setting the id attribute in the html template, or using an attribute
+ * modifier that will add the attribute with value Component#getMarkupId() to the tag ( such as
+ * MarkupIdSetter )
+ * <p>
+ * Any javascript that needs to be evaluated on the client side can be added using
+ * AjaxRequestTarget#append/prependJavaScript(String). For example, this feature can be useful when
+ * it is desirable to link component update with some javascript effects.
+ * <p>
+ * The target provides a listener interface {@link AjaxRequestTarget.IListener} that can be used to add code that
+ * responds to various target events by adding listeners via
+ * {@link #addListener(AjaxRequestTarget.IListener)}
+ *
+ * @since 1.2
+ *
+ * @author Igor Vaynberg (ivaynberg)
+ * @author Eelco Hillenius
+ */
+public class AjaxRequestHandler implements AjaxRequestTarget
+{
+
+	/**
+	 * Response that uses an encoder to encode its contents
+	 *
+	 * @author Igor Vaynberg (ivaynberg)
+	 */
+	private static final class AjaxResponse extends Response
+	{
+		private final AppendingStringBuffer buffer = new AppendingStringBuffer(256);
+
+		private boolean escaped = false;
+
+		private final Response originalResponse;
+
+		/**
+		 * Construct.
+		 *
+		 * @param originalResponse
+		 */
+		private AjaxResponse(Response originalResponse)
+		{
+			this.originalResponse = originalResponse;
+		}
+
+		/**
+		 * @see org.apache.wicket.request.Response#encodeURL(CharSequence)
+		 */
+		@Override
+		public String encodeURL(CharSequence url)
+		{
+			return originalResponse.encodeURL(url);
+		}
+
+		/**
+		 * @return contents of the response
+		 */
+		public CharSequence getContents()
+		{
+			return buffer;
+		}
+
+		/**
+		 * @return true if any escaping has been performed, false otherwise
+		 */
+		public boolean isContentsEncoded()
+		{
+			return escaped;
+		}
+
+		/**
+		 * @see org.apache.wicket.request.Response#write(CharSequence)
+		 */
+		@Override
+		public void write(CharSequence cs)
+		{
+			String string = cs.toString();
+			if (needsEncoding(string))
+			{
+				string = encode(string);
+				escaped = true;
+				buffer.append(string);
+			}
+			else
+			{
+				buffer.append(cs);
+			}
+		}
+
+		/**
+		 * Resets the response to a clean state so it can be reused to save on garbage.
+		 */
+		@Override
+		public void reset()
+		{
+			buffer.clear();
+			escaped = false;
+		}
+
+		@Override
+		public void write(byte[] array)
+		{
+			throw new UnsupportedOperationException("Cannot write binary data.");
+		}
+
+		@Override
+		public void write(byte[] array, int offset, int length)
+		{
+			throw new UnsupportedOperationException("Cannot write binary data.");
+		}
+
+		@Override
+		public Object getContainerResponse()
+		{
+			return originalResponse.getContainerResponse();
+		}
+	}
+
+	private static final Logger log = LoggerFactory.getLogger(AjaxRequestHandler.class);
+
+	private final List<CharSequence> appendJavaScripts = Generics.newArrayList();
+
+	private final List<CharSequence> domReadyJavaScripts = Generics.newArrayList();
+
+	/**
+	 * Create a response for component body and javascript that will escape output to make it safe
+	 * to use inside a CDATA block
+	 */
+	private final AjaxResponse encodingBodyResponse;
+
+	/**
+	 * Response for header contribution that will escape output to make it safe to use inside a
+	 * CDATA block
+	 */
+	private final AjaxResponse encodingHeaderResponse;
+
+	/** the component instances that will be rendered */
+	private final Map<String, Component> markupIdToComponent = new LinkedHashMap<String, Component>();
+
+	/** */
+	private final List<CharSequence> prependJavaScripts = Generics.newArrayList();
+
+	/** a list of listeners */
+	private List<AjaxRequestTarget.IListener> listeners = null;
+
+	/** */
+	private final Set<ITargetRespondListener> respondListeners = new HashSet<ITargetRespondListener>();
+
+	/** The associated Page */
+	private final Page page;
+
+	/** see https://issues.apache.org/jira/browse/WICKET-3564 */
+	private transient boolean componentsFrozen;
+	private transient boolean listenersFrozen;
+	private transient boolean respondersFrozen;
+
+	private PageLogData logData;
+
+	/**
+	 * Constructor
+	 *
+	 * @param page
+	 */
+	public AjaxRequestHandler(Page page)
+	{
+		this.page = Args.notNull(page, "page");
+		Response response = page.getResponse();
+		encodingBodyResponse = new AjaxResponse(response);
+		encodingHeaderResponse = new AjaxResponse(response);
+	}
+
+	/**
+	 * @see org.apache.wicket.request.handler.IPageRequestHandler#getPage()
+	 */
+	@Override
+	public Page getPage()
+	{
+		return page;
+	}
+
+	private void assertNotFrozen(boolean frozen, Class<?> clazz)
+	{
+		if (frozen)
+		{
+			throw new IllegalStateException(clazz.getSimpleName() + "s can no " +
+				" longer be added");
+		}
+	}
+
+	private void assertListenersNotFrozen()
+	{
+		assertNotFrozen(listenersFrozen, AjaxRequestTarget.IListener.class);
+	}
+
+	private void assertComponentsNotFrozen()
+	{
+		assertNotFrozen(componentsFrozen, Component.class);
+	}
+
+	private void assertRespondersNotFrozen()
+	{
+		assertNotFrozen(respondersFrozen, ITargetRespondListener.class);
+	}
+
+	@Override
+	public void addListener(AjaxRequestTarget.IListener listener) throws IllegalStateException
+	{
+		Args.notNull(listener, "listener");
+		assertListenersNotFrozen();
+
+		if (listeners == null)
+		{
+			listeners = new LinkedList<AjaxRequestTarget.IListener>();
+		}
+
+		if (!listeners.contains(listener))
+		{
+			listeners.add(listener);
+		}
+	}
+
+	@Override
+	public final void addChildren(MarkupContainer parent, Class<?> childCriteria)
+	{
+		Args.notNull(parent, "parent");
+		Args.notNull(childCriteria, "childCriteria");
+
+		parent.visitChildren(childCriteria, new IVisitor<Component, Void>()
+		{
+			@Override
+			public void component(final Component component, final IVisit<Void> visit)
+			{
+				add(component);
+				visit.dontGoDeeper();
+			}
+		});
+	}
+
+	@Override
+	public void add(Component... components)
+	{
+		for (final Component component : components)
+		{
+			Args.notNull(component, "component");
+
+			if (component.getOutputMarkupId() == false)
+			{
+				throw new IllegalArgumentException(
+					"cannot update component that does not have setOutputMarkupId property set to true. Component: " +
+						component.toString());
+			}
+			add(component, component.getMarkupId());
+		}
+	}
+
+	@Override
+	public final void add(final Component component, final String markupId)
+		throws IllegalArgumentException, IllegalStateException
+	{
+		Args.notEmpty(markupId, "markupId");
+		Args.notNull(component, "component");
+
+		if (component instanceof Page)
+		{
+			if (component != page)
+			{
+				throw new IllegalArgumentException("component cannot be a page");
+			}
+		}
+		else if (component instanceof AbstractRepeater)
+		{
+			throw new IllegalArgumentException(
+				"Component " +
+					component.getClass().getName() +
+					" has been added to the target. This component is a repeater and cannot be repainted via ajax directly. " +
+					"Instead add its parent or another markup container higher in the hierarchy.");
+		}
+
+		assertComponentsNotFrozen();
+
+		component.setMarkupId(markupId);
+		markupIdToComponent.put(markupId, component);
+	}
+
+	@Override
+	public final Collection<? extends Component> getComponents()
+	{
+		return Collections.unmodifiableCollection(markupIdToComponent.values());
+	}
+
+	@Override
+	public final void focusComponent(Component component)
+	{
+		if (component != null && component.getOutputMarkupId() == false)
+		{
+			throw new IllegalArgumentException(
+				"cannot update component that does not have setOutputMarkupId property set to true. Component: " +
+					component.toString());
+		}
+		final String id = component != null ? ("'" + component.getMarkupId() + "'") : "null";
+		appendJavaScript("Wicket.Focus.setFocusOnId(" + id + ");");
+	}
+
+	@Override
+	public final void appendJavaScript(CharSequence javascript)
+	{
+		Args.notNull(javascript, "javascript");
+
+		appendJavaScripts.add(javascript);
+	}
+
+	/**
+	 * @see org.apache.wicket.request.handler.IPageRequestHandler#detach(org.apache.wicket.request.IRequestCycle)
+	 */
+	@Override
+	public void detach(final IRequestCycle requestCycle)
+	{
+		if (logData == null)
+			logData = new PageLogData(page);
+
+		// detach the page if it was updated
+		if (markupIdToComponent.size() > 0)
+		{
+			final Component component = markupIdToComponent.values().iterator().next();
+			final Page page = component.findParent(Page.class);
+			if (page != null)
+			{
+				page.detach();
+			}
+		}
+	}
+
+	/**
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	@Override
+	public boolean equals(final Object obj)
+	{
+		if (obj instanceof AjaxRequestHandler)
+		{
+			AjaxRequestHandler that = (AjaxRequestHandler)obj;
+			return markupIdToComponent.equals(that.markupIdToComponent) &&
+				prependJavaScripts.equals(that.prependJavaScripts) &&
+				appendJavaScripts.equals(that.appendJavaScripts);
+		}
+		return false;
+	}
+
+	/**
+	 * @see java.lang.Object#hashCode()
+	 */
+	@Override
+	public int hashCode()
+	{
+		int result = "AjaxRequestHandler".hashCode();
+		result += markupIdToComponent.hashCode() * 17;
+		result += prependJavaScripts.hashCode() * 17;
+		result += appendJavaScripts.hashCode() * 17;
+		return result;
+	}
+
+	@Override
+	public final void prependJavaScript(CharSequence javascript)
+	{
+		Args.notNull(javascript, "javascript");
+
+		prependJavaScripts.add(javascript);
+	}
+
+	@Override
+	public void registerRespondListener(ITargetRespondListener listener)
+	{
+		assertRespondersNotFrozen();
+		respondListeners.add(listener);
+	}
+
+	/**
+	 * @see org.apache.wicket.request.handler.IPageRequestHandler#respond(org.apache.wicket.request.IRequestCycle)
+	 */
+	@Override
+	public final void respond(final IRequestCycle requestCycle)
+	{
+		final RequestCycle rc = (RequestCycle)requestCycle;
+		final WebResponse response = (WebResponse)requestCycle.getResponse();
+
+		if (markupIdToComponent.values().contains(page))
+		{
+			// the page itself has been added to the request target, we simply issue a redirect
+			// back to the page
+			IRequestHandler handler = new RenderPageRequestHandler(new PageProvider(page));
+			final String url = rc.urlFor(handler).toString();
+			response.sendRedirect(url);
+			return;
+		}
+
+		respondersFrozen = true;
+
+		for (ITargetRespondListener listener : respondListeners)
+		{
+			listener.onTargetRespond(this);
+		}
+
+		final Application app = Application.get();
+
+		page.send(app, Broadcast.BREADTH, this);
+
+		// Determine encoding
+		final String encoding = app.getRequestCycleSettings().getResponseRequestEncoding();
+
+		// Set content type based on markup type for page
+		response.setContentType("text/xml; charset=" + encoding);
+
+		// Make sure it is not cached by a client
+		response.disableCaching();
+
+		try
+		{
+			final StringResponse bodyResponse = new StringResponse();
+			constructResponseBody(bodyResponse, encoding);
+			CharSequence filteredResponse = invokeResponseFilters(bodyResponse);
+			response.write(filteredResponse);
+		}
+		finally
+		{
+			// restore the original response
+			RequestCycle.get().setResponse(response);
+		}
+	}
+
+	/**
+	 * Collects the response body (without the headers) so that it can be pre-processed before
+	 * written down to the original response.
+	 *
+	 * @param bodyResponse
+	 *            the buffering response
+	 * @param encoding
+	 *            the encoding that should be used to encode the body
+	 */
+	private void constructResponseBody(final Response bodyResponse, final String encoding)
+	{
+		bodyResponse.write("<?xml version=\"1.0\" encoding=\"");
+		bodyResponse.write(encoding);
+		bodyResponse.write("\"?>");
+		bodyResponse.write("<ajax-response>");
+
+		// invoke onbeforerespond event on listeners
+		fireOnBeforeRespondListeners();
+
+		// process added components
+		respondComponents(bodyResponse);
+
+		fireOnAfterRespondListeners(bodyResponse);
+
+		// queue up prepend javascripts. unlike other steps these are executed out of order so that
+		// components can contribute them from inside their onbeforerender methods.
+		Iterator<CharSequence> it = prependJavaScripts.iterator();
+		while (it.hasNext())
+		{
+			CharSequence js = it.next();
+			respondPriorityInvocation(bodyResponse, js);
+		}
+
+
+		// execute the dom ready javascripts as first javascripts
+		// after component replacement
+		it = domReadyJavaScripts.iterator();
+		while (it.hasNext())
+		{
+			CharSequence js = it.next();
+			respondInvocation(bodyResponse, js);
+		}
+		it = appendJavaScripts.iterator();
+		while (it.hasNext())
+		{
+			CharSequence js = it.next();
+			respondInvocation(bodyResponse, js);
+		}
+
+		bodyResponse.write("</ajax-response>");
+	}
+
+	/**
+	 * Runs the configured {@link IResponseFilter}s over the constructed Ajax response
+	 *
+	 * @param contentResponse
+	 *            the Ajax {@link Response} body
+	 * @return filtered response
+	 */
+	private AppendingStringBuffer invokeResponseFilters(final StringResponse contentResponse)
+	{
+		AppendingStringBuffer responseBuffer = new AppendingStringBuffer(
+			contentResponse.getBuffer());
+
+		List<IResponseFilter> responseFilters = Application.get()
+			.getRequestCycleSettings()
+			.getResponseFilters();
+
+		if (responseFilters != null)
+		{
+			for (IResponseFilter filter : responseFilters)
+			{
+				responseBuffer = filter.filter(responseBuffer);
+			}
+		}
+		return responseBuffer;
+	}
+
+	/**
+	 * Freezes the {@link #listeners} before firing the event and un-freezes them afterwards to
+	 * allow components to add more {@link AjaxRequestTarget.IListener}s for the second event.
+	 */
+	private void fireOnBeforeRespondListeners()
+	{
+		listenersFrozen = true;
+
+		if (listeners != null)
+		{
+			final Map<String, Component> components = Collections.unmodifiableMap(markupIdToComponent);
+
+			for (AjaxRequestTarget.IListener listener : listeners)
+			{
+				listener.onBeforeRespond(components, this);
+			}
+		}
+
+		listenersFrozen = false;
+	}
+
+	/**
+	 * Freezes the {@link #listeners}, and does not un-freeze them as the events will have been
+	 * fired by now.
+	 *
+	 * @param response
+	 */
+	private void fireOnAfterRespondListeners(final Response response)
+	{
+		listenersFrozen = true;
+
+		// invoke onafterresponse event on listeners
+		if (listeners != null)
+		{
+			final Map<String, Component> components = Collections.unmodifiableMap(markupIdToComponent);
+
+			// create response that will be used by listeners to append
+			// javascript
+			final IJavaScriptResponse jsresponse = new IJavaScriptResponse()
+			{
+				@Override
+				public void addJavaScript(String script)
+				{
+					respondInvocation(response, script);
+				}
+			};
+
+			for (AjaxRequestTarget.IListener listener : listeners)
+			{
+				listener.onAfterRespond(components, jsresponse);
+			}
+		}
+	}
+
+	/**
+	 * Processes components added to the target. This involves attaching components, rendering
+	 * markup into a client side xml envelope, and detaching them
+	 *
+	 * @param response
+	 */
+	private void respondComponents(Response response)
+	{
+		componentsFrozen = true;
+		// TODO: We might need to call prepareRender on all components upfront
+
+		// process component markup
+		for (Map.Entry<String, Component> stringComponentEntry : markupIdToComponent.entrySet())
+		{
+			final Component component = stringComponentEntry.getValue();
+			// final String markupId = stringComponentEntry.getKey();
+
+			if (!containsAncestorFor(component))
+			{
+				respondComponent(response, component.getAjaxRegionMarkupId(), component);
+			}
+		}
+
+		if (header != null)
+		{
+			// some header responses buffer all calls to render*** until close is called.
+			// when they are closed, they do something (i.e. aggregate all JS resource urls to a
+			// single url), and then "flush" (by writing to the real response) before closing.
+			// to support this, we need to allow header contributions to be written in the close
+			// tag, which we do here:
+			headerRendering = true;
+			// save old response, set new
+			Response oldResponse = RequestCycle.get().setResponse(encodingHeaderResponse);
+			encodingHeaderResponse.reset();
+
+			// now, close the response (which may render things)
+			header.getHeaderResponse().close();
+
+			// revert to old response
+			RequestCycle.get().setResponse(oldResponse);
+
+			// write the XML tags and we're done
+			writeHeaderContribution(response);
+			headerRendering = false;
+		}
+	}
+
+	private void writeHeaderContribution(Response response)
+	{
+		if (encodingHeaderResponse.getContents().length() != 0)
+		{
+			response.write("<header-contribution");
+
+			if (encodingHeaderResponse.isContentsEncoded())
+			{
+				response.write(" encoding=\"");
+				response.write(getEncodingName());
+				response.write("\" ");
+			}
+
+			// we need to write response as CDATA and parse it on client,
+			// because konqueror crashes when there is a <script> element
+			response.write("><![CDATA[<head xmlns:wicket=\"http://wicket.apache.org\">");
+			response.write(encodingHeaderResponse.getContents());
+			response.write("</head>]]>");
+			response.write("</header-contribution>");
+		}
+	}
+
+	/**
+	 * Checks if the target contains an ancestor for the given component
+	 *
+	 * @param component
+	 * @return <code>true</code> if target contains an ancestor for the given component
+	 */
+	private boolean containsAncestorFor(Component component)
+	{
+		Component cursor = component.getParent();
+		while (cursor != null)
+		{
+			if (markupIdToComponent.containsValue(cursor))
+			{
+				return true;
+			}
+			cursor = cursor.getParent();
+		}
+		return false;
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	@Override
+	public String toString()
+	{
+		return "[AjaxRequestHandler@" + hashCode() + " markupIdToComponent [" + markupIdToComponent +
+			"], prependJavaScript [" + prependJavaScripts + "], appendJavaScript [" +
+			appendJavaScripts + "]";
+	}
+
+	/**
+	 * Encodes a string so it is safe to use inside CDATA blocks
+	 *
+	 * @param str
+	 * @return encoded string
+	 */
+	private static String encode(CharSequence str)
+	{
+		if (str == null)
+		{
+			return null;
+		}
+
+		return Strings.replaceAll(str, "]", "]^").toString();
+	}
+
+	/**
+	 * @return name of encoding used to possibly encode the contents of the CDATA blocks
+	 */
+	protected String getEncodingName()
+	{
+		return "wicket1";
+	}
+
+	/**
+	 *
+	 * @param str
+	 * @return true if string needs to be encoded, false otherwise
+	 */
+	private static boolean needsEncoding(CharSequence str)
+	{
+		/*
+		 * TODO Post 1.2: Ajax: we can improve this by keeping a buffer of at least 3 characters and
+		 * checking that buffer so that we can narrow down escaping occurring only for ']]>'
+		 * sequence, or at least for ]] if ] is the last char in this buffer.
+		 *
+		 * but this improvement will only work if we write first and encode later instead of working
+		 * on fragments sent to write
+		 */
+		return Strings.indexOf(str, ']') >= 0;
+	}
+
+	/**
+	 *
+	 * @param response
+	 * @param markupId
+	 *            id of client-side dom element
+	 * @param component
+	 *            component to render
+	 */
+	private void respondComponent(final Response response, final String markupId,
+		final Component component)
+	{
+		if (component.getRenderBodyOnly() == true)
+		{
+			throw new IllegalStateException(
+				"Ajax render cannot be called on component that has setRenderBodyOnly enabled. Component: " +
+					component.toString());
+		}
+
+		component.setOutputMarkupId(true);
+
+		// substitute our encoding response for the real one so we can capture
+		// component's markup in a manner safe for transport inside CDATA block
+		encodingBodyResponse.reset();
+		RequestCycle.get().setResponse(encodingBodyResponse);
+
+		// Initialize temporary variables
+		final Page page = component.findParent(Page.class);
+		if (page == null)
+		{
+			// dont throw an exception but just ignore this component, somehow
+			// it got removed from the page.
+			log.debug("component: " + component + " with markupid: " + markupId +
+				" not rendered because it was already removed from page");
+			return;
+		}
+
+		page.startComponentRender(component);
+
+		try
+		{
+			component.prepareForRender();
+
+			// render any associated headers of the component
+			respondHeaderContribution(response, component);
+		}
+		catch (RuntimeException e)
+		{
+			try
+			{
+				component.afterRender();
+			}
+			catch (RuntimeException e2)
+			{
+				// ignore this one could be a result off.
+			}
+			// Restore original response
+			RequestCycle.get().setResponse(response);
+			encodingBodyResponse.reset();
+			throw e;
+		}
+
+		try
+		{
+			component.render();
+		}
+		catch (RuntimeException e)
+		{
+			RequestCycle.get().setResponse(response);
+			encodingBodyResponse.reset();
+			throw e;
+		}
+
+		page.endComponentRender(component);
+
+		// Restore original response
+		RequestCycle.get().setResponse(response);
+
+		response.write("<component id=\"");
+		response.write(markupId);
+		response.write("\" ");
+		if (encodingBodyResponse.isContentsEncoded())
+		{
+			response.write(" encoding=\"");
+			response.write(getEncodingName());
+			response.write("\" ");
+		}
+		response.write("><![CDATA[");
+		response.write(encodingBodyResponse.getContents());
+		response.write("]]></component>");
+
+		encodingBodyResponse.reset();
+	}
+
+	/**
+	 * Header response for an ajax request.
+	 *
+	 * @author Matej Knopp
+	 */
+	private class AjaxHeaderResponse extends HeaderResponse
+	{
+		@Override
+		public void render(HeaderItem item)
+		{
+			if (item instanceof OnLoadHeaderItem)
+			{
+				if (!wasItemRendered(item))
+				{
+					appendJavaScripts.add(((OnLoadHeaderItem)item).getJavaScript());
+					markItemRendered(item);
+				}
+			}
+			else if (item instanceof OnDomReadyHeaderItem)
+			{
+				if (!wasItemRendered(item))
+				{
+					domReadyJavaScripts.add(((OnDomReadyHeaderItem)item).getJavaScript());
+					markItemRendered(item);
+				}
+			}
+			else if (headerRendering)
+				super.render(item);
+			else
+				log.debug("Only methods that can be called on IHeaderResponse outside renderHead() are renderOnLoadJavaScript and renderOnDomReadyJavaScript");
+		}
+
+		/**
+		 * Construct.
+		 */
+		public AjaxHeaderResponse()
+		{
+		}
+
+		/**
+		 *
+		 * @see org.apache.wicket.markup.head.internal.HeaderResponse#getRealResponse()
+		 */
+		@Override
+		protected Response getRealResponse()
+		{
+			return RequestCycle.get().getResponse();
+		}
+	}
+
+	// whether a header contribution is being rendered
+	private boolean headerRendering = false;
+	private HtmlHeaderContainer header = null;
+
+	private IHeaderResponse headerResponse;
+
+	@Override
+	public IHeaderResponse getHeaderResponse()
+	{
+		if (headerResponse == null)
+		{
+			// we don't need to decorate the header response here because this is called from
+			// within AjaxHtmlHeaderContainer, which decorates the response
+			headerResponse = new AjaxHeaderResponse();
+		}
+		return headerResponse;
+	}
+
+	/**
+	 * Header container component for ajax header contributions
+	 *
+	 * @author Matej Knopp
+	 */
+	private static class AjaxHtmlHeaderContainer extends HtmlHeaderContainer
+	{
+		private static final long serialVersionUID = 1L;
+
+		/**
+		 * Construct.
+		 *
+		 * @param id
+		 * @param target
+		 */
+		public AjaxHtmlHeaderContainer(String id, AjaxRequestHandler target)
+		{
+			super(id);
+			this.target = target;
+		}
+
+		/**
+		 *
+		 * @see org.apache.wicket.markup.html.internal.HtmlHeaderContainer#newHeaderResponse()
+		 */
+		@Override
+		protected IHeaderResponse newHeaderResponse()
+		{
+			return target.getHeaderResponse();
+		}
+
+		private final transient AjaxRequestHandler target;
+	}
+
+	/**
+	 *
+	 * @param response
+	 * @param component
+	 */
+	private void respondHeaderContribution(final Response response, final Component component)
+	{
+		headerRendering = true;
+
+		// create the htmlheadercontainer if needed
+		if (header == null)
+		{
+			header = new AjaxHtmlHeaderContainer(HtmlHeaderSectionHandler.HEADER_ID, this);
+			final Page page = component.getPage();
+			page.addOrReplace(header);
+		}
+
+		// save old response, set new
+		Response oldResponse = RequestCycle.get().setResponse(encodingHeaderResponse);
+
+		encodingHeaderResponse.reset();
+
+		// render the head of component and all it's children
+
+		component.renderHead(header);
+
+		if (component instanceof MarkupContainer)
+		{
+			((MarkupContainer)component).visitChildren(new IVisitor<Component, Void>()
+			{
+				@Override
+				public void component(final Component component, final IVisit<Void> visit)
+				{
+					if (component.isVisibleInHierarchy())
+					{
+						component.renderHead(header);
+					}
+					else
+					{
+						visit.dontGoDeeper();
+					}
+				}
+			});
+		}
+
+		// revert to old response
+		RequestCycle.get().setResponse(oldResponse);
+
+		writeHeaderContribution(response);
+
+		headerRendering = false;
+	}
+
+	private void respondInvocation(final Response response, final CharSequence js)
+	{
+		respondJavascriptInvocation("evaluate", response, js);
+	}
+
+	private void respondPriorityInvocation(final Response response, final CharSequence js)
+	{
+		respondJavascriptInvocation("priority-evaluate", response, js);
+	}
+
+
+	/**
+	 * @param invocation
+	 *            type of invocation tag, usually {@literal evaluate} or
+	 *            {@literal priority-evaluate}
+	 * @param response
+	 * @param js
+	 */
+	private void respondJavascriptInvocation(final String invocation, final Response response,
+		final CharSequence js)
+	{
+		boolean encoded = false;
+		CharSequence javascript = js;
+
+		// encode the response if needed
+		if (needsEncoding(js))
+		{
+			encoded = true;
+			javascript = encode(js);
+		}
+
+		response.write("<");
+		response.write(invocation);
+		if (encoded)
+		{
+			response.write(" encoding=\"");
+			response.write(getEncodingName());
+			response.write("\"");
+		}
+		response.write(">");
+
+		response.write("<![CDATA[");
+		response.write(javascript);
+		response.write("]]>");
+
+		response.write("</");
+		response.write(invocation);
+		response.write(">");
+
+		encodingBodyResponse.reset();
+	}
+
+	@Override
+	public String getLastFocusedElementId()
+	{
+		WebRequest request = (WebRequest)RequestCycle.get().getRequest();
+		String id = request.getHeader("Wicket-FocusedElementId");
+		return Strings.isEmpty(id) ? null : id;
+	}
+
+	/**
+	 * @see org.apache.wicket.request.handler.IPageRequestHandler#getPageClass()
+	 */
+	@Override
+	public Class<? extends IRequestablePage> getPageClass()
+	{
+		return page.getPageClass();
+	}
+
+	/**
+	 * @see org.apache.wicket.request.handler.IPageRequestHandler#getPageId()
+	 */
+	@Override
+	public Integer getPageId()
+	{
+		return page.getPageId();
+	}
+
+	/**
+	 * @see org.apache.wicket.request.handler.IPageRequestHandler#getPageParameters()
+	 */
+	@Override
+	public PageParameters getPageParameters()
+	{
+		return page.getPageParameters();
+	}
+
+	@Override
+	public final boolean isPageInstanceCreated()
+	{
+		return true;
+	}
+
+	@Override
+	public final Integer getRenderCount()
+	{
+		return page.getRenderCount();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public PageLogData getLogData()
+	{
+		return logData;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java
index 8904948..0ad5b51 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTarget.java
@@ -17,1307 +17,199 @@
 package org.apache.wicket.ajax;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
-import org.apache.wicket.Application;
 import org.apache.wicket.Component;
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.Page;
-import org.apache.wicket.event.Broadcast;
 import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
-import org.apache.wicket.markup.head.internal.HeaderResponse;
-import org.apache.wicket.markup.html.internal.HtmlHeaderContainer;
-import org.apache.wicket.markup.parser.filter.HtmlHeaderSectionHandler;
-import org.apache.wicket.markup.repeater.AbstractRepeater;
 import org.apache.wicket.request.ILoggableRequestHandler;
-import org.apache.wicket.request.IRequestCycle;
-import org.apache.wicket.request.IRequestHandler;
-import org.apache.wicket.request.Response;
-import org.apache.wicket.request.component.IRequestablePage;
-import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.handler.IPageRequestHandler;
-import org.apache.wicket.request.handler.PageProvider;
-import org.apache.wicket.request.handler.RenderPageRequestHandler;
-import org.apache.wicket.request.handler.logger.PageLogData;
-import org.apache.wicket.request.http.WebRequest;
-import org.apache.wicket.request.http.WebResponse;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.markup.head.HeaderItem;
-import org.apache.wicket.markup.head.OnLoadHeaderItem;
-import org.apache.wicket.response.StringResponse;
-import org.apache.wicket.response.filter.IResponseFilter;
-import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.lang.Generics;
-import org.apache.wicket.util.string.AppendingStringBuffer;
-import org.apache.wicket.util.string.Strings;
-import org.apache.wicket.util.visit.IVisit;
-import org.apache.wicket.util.visit.IVisitor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
- * A request target that produces ajax response envelopes used on the client side to update
- * component markup as well as evaluate arbitrary javascript.
- * <p>
- * A component whose markup needs to be updated should be added to this target via
- * AjaxRequestTarget#addComponent(Component) method. Its body will be rendered and added to the
- * envelope when the target is processed, and refreshed on the client side when the ajax response is
- * received.
- * <p>
- * It is important that the component whose markup needs to be updated contains an id attribute in
- * the generated markup that is equal to the value retrieved from Component#getMarkupId(). This can
- * be accomplished by either setting the id attribute in the html template, or using an attribute
- * modifier that will add the attribute with value Component#getMarkupId() to the tag ( such as
- * MarkupIdSetter )
- * <p>
- * Any javascript that needs to be evaluated on the client side can be added using
- * AjaxRequestTarget#append/prependJavaScript(String). For example, this feature can be useful when
- * it is desirable to link component update with some javascript effects.
- * <p>
- * The target provides a listener interface {@link IListener} that can be used to add code that
- * responds to various target events by adding listeners via
- * {@link #addListener(org.apache.wicket.ajax.AjaxRequestTarget.IListener)}
- * 
- * @since 1.2
- * 
- * @author Igor Vaynberg (ivaynberg)
- * @author Eelco Hillenius
+ *
+ * @since 6.0
  */
-public class AjaxRequestTarget implements IPageRequestHandler, ILoggableRequestHandler
+public interface AjaxRequestTarget extends IPageRequestHandler, ILoggableRequestHandler
 {
 	/**
 	 * An {@link AjaxRequestTarget} listener that can be used to respond to various target-related
 	 * events
-	 * 
+	 *
 	 */
-	public static interface IListener
+	interface IListener
 	{
 		/**
 		 * Triggered before ajax request target begins its response cycle
-		 * 
+		 *
 		 * @param map
 		 *            read-only map:markupId->component of components already added to the target
 		 * @param target
 		 *            the target itself. Could be used to add components or to append/prepend
 		 *            javascript
-		 * 
+		 *
 		 */
-		public void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target);
+		void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target);
 
 		/**
 		 * Triggered after ajax request target is done with its response cycle. At this point only
 		 * additional javascript can be output to the response using the provided
-		 * {@link IJavaScriptResponse} object
-		 * 
+		 * {@link AjaxRequestTarget.IJavaScriptResponse} object
+		 *
 		 * NOTE: During this stage of processing any calls to target that manipulate the response
 		 * (adding components, javascript) will have no effect
-		 * 
+		 *
 		 * @param map
 		 *            read-only map:markupId->component of components already added to the target
 		 * @param response
 		 *            response object that can be used to output javascript
 		 */
-		public void onAfterRespond(Map<String, Component> map, IJavaScriptResponse response);
+		void onAfterRespond(Map<String, Component> map, AjaxRequestTarget.IJavaScriptResponse response);
 	}
 
 	/**
 	 * An ajax javascript response that allows users to add javascript to be executed on the client
 	 * side
-	 * 
+	 *
 	 * @author ivaynberg
 	 */
-	public static interface IJavaScriptResponse
+	interface IJavaScriptResponse
 	{
 		/**
 		 * Adds more javascript to the ajax response that will be executed on the client side
-		 * 
+		 *
 		 * @param script
 		 *            javascript
 		 */
-		public void addJavaScript(String script);
+		void addJavaScript(String script);
 	}
 
 	/**
-	 * Response that uses an encoder to encode its contents
-	 * 
-	 * @author Igor Vaynberg (ivaynberg)
+	 * Components can implement this interface to get a notification when AjaxRequestTarget begins
+	 * to respond. This can be used to postpone adding components to AjaxRequestTarget until the
+	 * response begins.
+	 *
+	 * @author Matej Knopp
 	 */
-	private final class AjaxResponse extends Response
+	interface ITargetRespondListener
 	{
-		private final AppendingStringBuffer buffer = new AppendingStringBuffer(256);
-
-		private boolean escaped = false;
-
-		private final Response originalResponse;
-
-		/**
-		 * Construct.
-		 * 
-		 * @param originalResponse
-		 */
-		public AjaxResponse(Response originalResponse)
-		{
-			this.originalResponse = originalResponse;
-		}
-
-		/**
-		 * @see org.apache.wicket.request.Response#encodeURL(CharSequence)
-		 */
-		@Override
-		public String encodeURL(CharSequence url)
-		{
-			return originalResponse.encodeURL(url);
-		}
-
-		/**
-		 * @return contents of the response
-		 */
-		public CharSequence getContents()
-		{
-			return buffer;
-		}
-
-		/**
-		 * @return true if any escaping has been performed, false otherwise
-		 */
-		public boolean isContentsEncoded()
-		{
-			return escaped;
-		}
-
-		/**
-		 * @see org.apache.wicket.request.Response#write(CharSequence)
-		 */
-		@Override
-		public void write(CharSequence cs)
-		{
-			String string = cs.toString();
-			if (needsEncoding(string))
-			{
-				string = encode(string);
-				escaped = true;
-				buffer.append(string);
-			}
-			else
-			{
-				buffer.append(cs);
-			}
-		}
-
 		/**
-		 * Resets the response to a clean state so it can be reused to save on garbage.
+		 * Invoked when AjaxRequestTarget is about the respond.
+		 *
+		 * @param target
 		 */
-		@Override
-		public void reset()
-		{
-			buffer.clear();
-			escaped = false;
-		}
-
-		@Override
-		public void write(byte[] array)
-		{
-			throw new UnsupportedOperationException("Cannot write binary data.");
-		}
-
-		@Override
-		public void write(byte[] array, int offset, int length)
-		{
-			throw new UnsupportedOperationException("Cannot write binary data.");
-		}
-
-		@Override
-		public Object getContainerResponse()
-		{
-			return originalResponse.getContainerResponse();
-		}
+		void onTargetRespond(AjaxRequestTarget target);
 	}
 
-	private static final Logger log = LoggerFactory.getLogger(AjaxRequestTarget.class);
-
-	private final List<CharSequence> appendJavaScripts = Generics.newArrayList();
-
-	private final List<CharSequence> domReadyJavaScripts = Generics.newArrayList();
-
-	/**
-	 * Create a response for component body and javascript that will escape output to make it safe
-	 * to use inside a CDATA block
-	 */
-	private final AjaxResponse encodingBodyResponse;
-
-	/**
-	 * Response for header contribution that will escape output to make it safe to use inside a
-	 * CDATA block
-	 */
-	private final AjaxResponse encodingHeaderResponse;
-
-	/** the component instances that will be rendered */
-	private final Map<String, Component> markupIdToComponent = new LinkedHashMap<String, Component>();
-
-	/** */
-	private final List<CharSequence> prependJavaScripts = Generics.newArrayList();
-
-	/** a list of listeners */
-	private List<IListener> listeners = null;
-
-	/** */
-	private final Set<ITargetRespondListener> respondListeners = new HashSet<ITargetRespondListener>();
-
-	/** The associated Page */
-	private final Page page;
-
-	/** see https://issues.apache.org/jira/browse/WICKET-3564 */
-	private transient boolean componentsFrozen;
-	private transient boolean listenersFrozen;
-	private transient boolean respondersFrozen;
-
-	private PageLogData logData;
-
-	/**
-	 * Constructor
-	 * 
-	 * @param page
-	 */
-	public AjaxRequestTarget(Page page)
-	{
-		Args.notNull(page, "page");
-		this.page = page;
-		Response response = RequestCycle.get().getResponse();
-		encodingBodyResponse = new AjaxResponse(response);
-		encodingHeaderResponse = new AjaxResponse(response);
-	}
 
 	/**
-	 * @see org.apache.wicket.request.handler.IPageRequestHandler#getPage()
+	 * Adds a component to the list of components to be rendered
+	 *
+	 * @param markupId
+	 *            id of client-side dom element that will be updated
+	 * @param component
+	 *            component to be rendered
+	 * @throws IllegalArgumentException
+	 *             if the component is a {@link org.apache.wicket.Page} or an {@link org.apache.wicket.markup.repeater.AbstractRepeater}
+	 * @throws IllegalStateException
+	 *             if the components are currently being rendered, or have already been rendered
 	 */
-	@Override
-	public Page getPage()
-	{
-		return page;
-	}
-
-	private void assertNotFrozen(boolean frozen, Class<?> clazz)
-	{
-		if (frozen)
-		{
-			throw new IllegalStateException(clazz.getSimpleName() + "s can no " +
-				" longer be added");
-		}
-	}
-
-	private void assertListenersNotFrozen()
-	{
-		assertNotFrozen(listenersFrozen, IListener.class);
-	}
-
-	private void assertComponentsNotFrozen()
-	{
-		assertNotFrozen(componentsFrozen, Component.class);
-	}
-
-	private void assertRespondersNotFrozen()
-	{
-		assertNotFrozen(respondersFrozen, ITargetRespondListener.class);
-	}
+	void add(final Component component, final String markupId);
 
 	/**
-	 * Adds a listener to this target
-	 * 
-	 * @param listener
-	 * @throws IllegalStateException
-	 *             if {@link IListener}'s events are currently being fired or have both been fired
-	 *             already
+	 * Adds components to the list of components to be rendered.
+	 *
+	 * @param components
+	 *            components to be rendered
 	 */
-	public void addListener(IListener listener) throws IllegalStateException
-	{
-		Args.notNull(listener, "listener");
-		assertListenersNotFrozen();
-
-		if (listeners == null)
-		{
-			listeners = new LinkedList<IListener>();
-		}
+	void add(Component... components);
 
-		if (!listeners.contains(listener))
-		{
-			listeners.add(listener);
-		}
-	}
 
 	/**
 	 * Visits all children of the specified parent container and adds them to the target if they are
 	 * of same type as <code>childCriteria</code>
-	 * 
+	 *
 	 * @param parent
 	 *            Must not be null.
 	 * @param childCriteria
 	 *            Must not be null. If you want to traverse all components use ` Component.class as
 	 *            the value for this argument.
 	 */
-	public final void addChildren(MarkupContainer parent, Class<?> childCriteria)
-	{
-		Args.notNull(parent, "parent");
-		Args.notNull(childCriteria, "childCriteria");
+	void addChildren(MarkupContainer parent, Class<?> childCriteria);
 
-		parent.visitChildren(childCriteria, new IVisitor<Component, Void>()
-		{
-			@Override
-			public void component(final Component component, final IVisit<Void> visit)
-			{
-				add(component);
-				visit.dontGoDeeper();
-			}
-		});
-	}
 
 	/**
-	 * Adds components to the list of components to be rendered
-	 * 
-	 * @param components
-	 *            components to be rendered
-	 * @deprecated use {@link #add(Component...)} instead
-	 */
-	// should stay deprecated in 1.5
-	@Deprecated
-	public void addComponent(Component... components)
-	{
-		add(components);
-	}
-
-	/**
-	 * Adds components to the list of components to be rendered.
-	 * 
-	 * @param components
-	 *            components to be rendered
-	 */
-	public void add(Component... components)
-	{
-		for (final Component component : components)
-		{
-			Args.notNull(component, "component");
-
-			if (component.getOutputMarkupId() == false)
-			{
-				throw new IllegalArgumentException(
-					"cannot update component that does not have setOutputMarkupId property set to true. Component: " +
-						component.toString());
-			}
-			add(component, component.getMarkupId());
-		}
-	}
-
-	/**
-	 * Adds a component to the list of components to be rendered
-	 * 
-	 * @param markupId
-	 *            id of client-side dom element that will be updated
-	 * 
-	 * @param component
-	 *            component to be rendered
-	 * @deprecated use {@link #add(Component...)} instead
-	 */
-	// should stay deprecated in 1.5
-	@Deprecated
-	public final void addComponent(Component component, String markupId)
-	{
-		add(component, markupId);
-	}
-
-	/**
-	 * Adds a component to the list of components to be rendered
-	 * 
-	 * @param markupId
-	 *            id of client-side dom element that will be updated
-	 * @param component
-	 *            component to be rendered
-	 * @throws IllegalArgumentException
-	 *             if the component is a {@link Page} or an {@link AbstractRepeater}
+	 * Adds a listener to this target
+	 *
+	 * @param listener
 	 * @throws IllegalStateException
-	 *             if the components are currently being rendered, or have already been rendered
-	 */
-	public final void add(final Component component, final String markupId)
-		throws IllegalArgumentException, IllegalStateException
-	{
-		Args.notEmpty(markupId, "markupId");
-		Args.notNull(component, "component");
-
-		if (component instanceof Page)
-		{
-			if (component != page)
-			{
-				throw new IllegalArgumentException("component cannot be a page");
-			}
-		}
-		else if (component instanceof AbstractRepeater)
-		{
-			throw new IllegalArgumentException(
-				"Component " +
-					component.getClass().getName() +
-					" has been added to the target. This component is a repeater and cannot be repainted via ajax directly. " +
-					"Instead add its parent or another markup container higher in the hierarchy.");
-		}
-
-		assertComponentsNotFrozen();
-
-		component.setMarkupId(markupId);
-		markupIdToComponent.put(markupId, component);
-	}
-
-	/**
-	 * Returns an unmodifiable collection of all components added to this target
-	 * 
-	 * @return unmodifiable collection of all components added to this target
-	 */
-	public final Collection<? extends Component> getComponents()
-	{
-		return Collections.unmodifiableCollection(markupIdToComponent.values());
-	}
-
-	/**
-	 * Sets the focus in the browser to the given component. The markup id must be set. If the
-	 * component is null the focus will not be set to any component.
-	 * 
-	 * @param component
-	 *            The component to get the focus or null.
+	 *             if {@link AjaxRequestTarget.IListener}'s events are currently being fired or have both been fired
+	 *             already
 	 */
-	public final void focusComponent(Component component)
-	{
-		if (component != null && component.getOutputMarkupId() == false)
-		{
-			throw new IllegalArgumentException(
-				"cannot update component that does not have setOutputMarkupId property set to true. Component: " +
-					component.toString());
-		}
-		final String id = component != null ? ("'" + component.getMarkupId() + "'") : "null";
-		appendJavaScript("Wicket.Focus.setFocusOnId(" + id + ");");
-	}
+	void addListener(AjaxRequestTarget.IListener listener);
 
 	/**
 	 * Adds javascript that will be evaluated on the client side after components are replaced
-	 * 
+	 *
 	 * @param javascript
 	 */
-	public final void appendJavaScript(CharSequence javascript)
-	{
-		Args.notNull(javascript, "javascript");
-
-		appendJavaScripts.add(javascript);
-	}
-
-	/**
-	 * @see org.apache.wicket.request.handler.IPageRequestHandler#detach(org.apache.wicket.request.IRequestCycle)
-	 */
-	@Override
-	public void detach(final IRequestCycle requestCycle)
-	{
-		if (logData == null)
-			logData = new PageLogData(page);
-
-		// detach the page if it was updated
-		if (markupIdToComponent.size() > 0)
-		{
-			final Component component = markupIdToComponent.values().iterator().next();
-			final Page page = component.findParent(Page.class);
-			if (page != null)
-			{
-				page.detach();
-			}
-		}
-	}
-
-	/**
-	 * @see java.lang.Object#equals(java.lang.Object)
-	 */
-	@Override
-	public boolean equals(final Object obj)
-	{
-		if (obj instanceof AjaxRequestTarget)
-		{
-			AjaxRequestTarget that = (AjaxRequestTarget)obj;
-			return markupIdToComponent.equals(that.markupIdToComponent) &&
-				prependJavaScripts.equals(that.prependJavaScripts) &&
-				appendJavaScripts.equals(that.appendJavaScripts);
-		}
-		return false;
-	}
-
-	/**
-	 * @see java.lang.Object#hashCode()
-	 */
-	@Override
-	public int hashCode()
-	{
-		int result = "AjaxRequestTarget".hashCode();
-		result += markupIdToComponent.hashCode() * 17;
-		result += prependJavaScripts.hashCode() * 17;
-		result += appendJavaScripts.hashCode() * 17;
-		return result;
-	}
+	void appendJavaScript(CharSequence javascript);
 
 	/**
 	 * Adds javascript that will be evaluated on the client side before components are replaced
-	 * 
+	 *
 	 * @param javascript
 	 */
-	public final void prependJavaScript(CharSequence javascript)
-	{
-		Args.notNull(javascript, "javascript");
-
-		prependJavaScripts.add(javascript);
-	}
-
-	/**
-	 * Components can implement this interface to get a notification when AjaxRequestTarget begins
-	 * to respond. This can be used to postpone adding components to AjaxRequestTarget until the
-	 * response begins.
-	 * 
-	 * @author Matej Knopp
-	 */
-	public static interface ITargetRespondListener
-	{
-		/**
-		 * Invoked when AjaxRequestTarget is about the respond.
-		 * 
-		 * @param target
-		 */
-		public void onTargetRespond(AjaxRequestTarget target);
-	}
+	void prependJavaScript(CharSequence javascript);
 
 	/**
 	 * Register the given respond listener. The listener's
-	 * {@link ITargetRespondListener#onTargetRespond(AjaxRequestTarget)} method will be invoked when
+	 * {@link org.apache.wicket.ajax.AjaxRequestTarget.ITargetRespondListener#onTargetRespond} method will be invoked when
 	 * the {@link AjaxRequestTarget} starts to respond.
-	 * 
+	 *
 	 * @param listener
 	 */
-	public void registerRespondListener(ITargetRespondListener listener)
-	{
-		assertRespondersNotFrozen();
-		respondListeners.add(listener);
-	}
-
-	/**
-	 * @see org.apache.wicket.request.handler.IPageRequestHandler#respond(org.apache.wicket.request.IRequestCycle)
-	 */
-	@Override
-	public final void respond(final IRequestCycle requestCycle)
-	{
-		final RequestCycle rc = (RequestCycle)requestCycle;
-		final WebResponse response = (WebResponse)requestCycle.getResponse();
-
-		if (markupIdToComponent.values().contains(page))
-		{
-			// the page itself has been added to the request target, we simply issue a redirect
-			// back to the page
-			IRequestHandler handler = new RenderPageRequestHandler(new PageProvider(page));
-			final String url = rc.urlFor(handler).toString();
-			response.sendRedirect(url);
-			return;
-		}
-
-		respondersFrozen = true;
-
-		for (ITargetRespondListener listener : respondListeners)
-		{
-			listener.onTargetRespond(this);
-		}
-
-		final Application app = Application.get();
-
-		page.send(app, Broadcast.BREADTH, this);
-
-		// Determine encoding
-		final String encoding = app.getRequestCycleSettings().getResponseRequestEncoding();
-
-		// Set content type based on markup type for page
-		response.setContentType("text/xml; charset=" + encoding);
-
-		// Make sure it is not cached by a client
-		response.disableCaching();
-
-		try
-		{
-			final StringResponse bodyResponse = new StringResponse();
-			constructResponseBody(bodyResponse, encoding);
-			CharSequence filteredResponse = invokeResponseFilters(bodyResponse);
-			response.write(filteredResponse);
-		}
-		finally
-		{
-			// restore the original response
-			RequestCycle.get().setResponse(response);
-		}
-	}
-
-	/**
-	 * Collects the response body (without the headers) so that it can be pre-processed before
-	 * written down to the original response.
-	 * 
-	 * @param bodyResponse
-	 *            the buffering response
-	 * @param encoding
-	 *            the encoding that should be used to encode the body
-	 */
-	private void constructResponseBody(final Response bodyResponse, final String encoding)
-	{
-		bodyResponse.write("<?xml version=\"1.0\" encoding=\"");
-		bodyResponse.write(encoding);
-		bodyResponse.write("\"?>");
-		bodyResponse.write("<ajax-response>");
-
-		// invoke onbeforerespond event on listeners
-		fireOnBeforeRespondListeners();
-
-		// process added components
-		respondComponents(bodyResponse);
-
-		fireOnAfterRespondListeners(bodyResponse);
-
-		// queue up prepend javascripts. unlike other steps these are executed out of order so that
-		// components can contribute them from inside their onbeforerender methods.
-		Iterator<CharSequence> it = prependJavaScripts.iterator();
-		while (it.hasNext())
-		{
-			CharSequence js = it.next();
-			respondPriorityInvocation(bodyResponse, js);
-		}
-
-
-		// execute the dom ready javascripts as first javascripts
-		// after component replacement
-		it = domReadyJavaScripts.iterator();
-		while (it.hasNext())
-		{
-			CharSequence js = it.next();
-			respondInvocation(bodyResponse, js);
-		}
-		it = appendJavaScripts.iterator();
-		while (it.hasNext())
-		{
-			CharSequence js = it.next();
-			respondInvocation(bodyResponse, js);
-		}
-
-		bodyResponse.write("</ajax-response>");
-	}
-
-	/**
-	 * Runs the configured {@link IResponseFilter}s over the constructed Ajax response
-	 * 
-	 * @param contentResponse
-	 *            the Ajax {@link Response} body
-	 * @return filtered response
-	 */
-	private AppendingStringBuffer invokeResponseFilters(final StringResponse contentResponse)
-	{
-		AppendingStringBuffer responseBuffer = new AppendingStringBuffer(
-			contentResponse.getBuffer());
-
-		List<IResponseFilter> responseFilters = Application.get()
-			.getRequestCycleSettings()
-			.getResponseFilters();
-
-		if (responseFilters != null)
-		{
-			for (IResponseFilter filter : responseFilters)
-			{
-				responseBuffer = filter.filter(responseBuffer);
-			}
-		}
-		return responseBuffer;
-	}
-
-	/**
-	 * Freezes the {@link #listeners} before firing the event and un-freezes them afterwards to
-	 * allow components to add more {@link IListener}s for the second event.
-	 */
-	private void fireOnBeforeRespondListeners()
-	{
-		listenersFrozen = true;
-
-		if (listeners != null)
-		{
-			final Map<String, Component> components = Collections.unmodifiableMap(markupIdToComponent);
-
-			for (IListener listener : listeners)
-			{
-				listener.onBeforeRespond(components, this);
-			}
-		}
-
-		listenersFrozen = false;
-	}
-
-	/**
-	 * Freezes the {@link #listeners}, and does not un-freeze them as the events will have been
-	 * fired by now.
-	 * 
-	 * @param response
-	 */
-	private void fireOnAfterRespondListeners(final Response response)
-	{
-		listenersFrozen = true;
-
-		// invoke onafterresponse event on listeners
-		if (listeners != null)
-		{
-			final Map<String, Component> components = Collections.unmodifiableMap(markupIdToComponent);
-
-			// create response that will be used by listeners to append
-			// javascript
-			final IJavaScriptResponse jsresponse = new IJavaScriptResponse()
-			{
-				@Override
-				public void addJavaScript(String script)
-				{
-					respondInvocation(response, script);
-				}
-			};
-
-			for (IListener listener : listeners)
-			{
-				listener.onAfterRespond(components, jsresponse);
-			}
-		}
-	}
-
-	/**
-	 * Processes components added to the target. This involves attaching components, rendering
-	 * markup into a client side xml envelope, and detaching them
-	 * 
-	 * @param response
-	 */
-	private void respondComponents(Response response)
-	{
-		componentsFrozen = true;
-		// TODO: We might need to call prepareRender on all components upfront
-
-		// process component markup
-		for (Map.Entry<String, Component> stringComponentEntry : markupIdToComponent.entrySet())
-		{
-			final Component component = stringComponentEntry.getValue();
-			// final String markupId = stringComponentEntry.getKey();
-
-			if (!containsAncestorFor(component))
-			{
-				respondComponent(response, component.getAjaxRegionMarkupId(), component);
-			}
-		}
-
-		if (header != null)
-		{
-			// some header responses buffer all calls to render*** until close is called.
-			// when they are closed, they do something (i.e. aggregate all JS resource urls to a
-			// single url), and then "flush" (by writing to the real response) before closing.
-			// to support this, we need to allow header contributions to be written in the close
-			// tag, which we do here:
-			headerRendering = true;
-			// save old response, set new
-			Response oldResponse = RequestCycle.get().setResponse(encodingHeaderResponse);
-			encodingHeaderResponse.reset();
-
-			// now, close the response (which may render things)
-			header.getHeaderResponse().close();
-
-			// revert to old response
-			RequestCycle.get().setResponse(oldResponse);
-
-			// write the XML tags and we're done
-			writeHeaderContribution(response);
-			headerRendering = false;
-		}
-	}
-
-	private void writeHeaderContribution(Response response)
-	{
-		if (encodingHeaderResponse.getContents().length() != 0)
-		{
-			response.write("<header-contribution");
-
-			if (encodingHeaderResponse.isContentsEncoded())
-			{
-				response.write(" encoding=\"");
-				response.write(getEncodingName());
-				response.write("\" ");
-			}
-
-			// we need to write response as CDATA and parse it on client,
-			// because konqueror crashes when there is a <script> element
-			response.write("><![CDATA[<head xmlns:wicket=\"http://wicket.apache.org\">");
-			response.write(encodingHeaderResponse.getContents());
-			response.write("</head>]]>");
-			response.write("</header-contribution>");
-		}
-	}
-
-	/**
-	 * Checks if the target contains an ancestor for the given component
-	 * 
-	 * @param component
-	 * @return <code>true</code> if target contains an ancestor for the given component
-	 */
-	private boolean containsAncestorFor(Component component)
-	{
-		Component cursor = component.getParent();
-		while (cursor != null)
-		{
-			if (markupIdToComponent.containsValue(cursor))
-			{
-				return true;
-			}
-			cursor = cursor.getParent();
-		}
-		return false;
-	}
-
-	/**
-	 * @see java.lang.Object#toString()
-	 */
-	@Override
-	public String toString()
-	{
-		return "[AjaxRequestTarget@" + hashCode() + " markupIdToComponent [" + markupIdToComponent +
-			"], prependJavaScript [" + prependJavaScripts + "], appendJavaScript [" +
-			appendJavaScripts + "]";
-	}
-
-	/**
-	 * Encodes a string so it is safe to use inside CDATA blocks
-	 * 
-	 * @param str
-	 * @return encoded string
-	 */
-	protected String encode(CharSequence str)
-	{
-		if (str == null)
-		{
-			return null;
-		}
-
-		return Strings.replaceAll(str, "]", "]^").toString();
-	}
+	void registerRespondListener(ITargetRespondListener listener);
 
 	/**
-	 * @return name of encoding used to possibly encode the contents of the CDATA blocks
-	 */
-	protected String getEncodingName()
-	{
-		return "wicket1";
-	}
-
-	/**
-	 * 
-	 * @param str
-	 * @return true if string needs to be encoded, false otherwise
+	 * Returns an unmodifiable collection of all components added to this target
+	 *
+	 * @return unmodifiable collection of all components added to this target
 	 */
-	protected boolean needsEncoding(CharSequence str)
-	{
-		/*
-		 * TODO Post 1.2: Ajax: we can improve this by keeping a buffer of at least 3 characters and
-		 * checking that buffer so that we can narrow down escaping occurring only for ']]>'
-		 * sequence, or at least for ]] if ] is the last char in this buffer.
-		 * 
-		 * but this improvement will only work if we write first and encode later instead of working
-		 * on fragments sent to write
-		 */
-		return Strings.indexOf(str, ']') >= 0;
-	}
+	Collection<? extends Component> getComponents();
 
 	/**
-	 * 
-	 * @param response
-	 * @param markupId
-	 *            id of client-side dom element
+	 * Sets the focus in the browser to the given component. The markup id must be set. If the
+	 * component is null the focus will not be set to any component.
+	 *
 	 * @param component
-	 *            component to render
-	 */
-	private void respondComponent(final Response response, final String markupId,
-		final Component component)
-	{
-		if (component.getRenderBodyOnly() == true)
-		{
-			throw new IllegalStateException(
-				"Ajax render cannot be called on component that has setRenderBodyOnly enabled. Component: " +
-					component.toString());
-		}
-
-		component.setOutputMarkupId(true);
-
-		// substitute our encoding response for the real one so we can capture
-		// component's markup in a manner safe for transport inside CDATA block
-		encodingBodyResponse.reset();
-		RequestCycle.get().setResponse(encodingBodyResponse);
-
-		// Initialize temporary variables
-		final Page page = component.findParent(Page.class);
-		if (page == null)
-		{
-			// dont throw an exception but just ignore this component, somehow
-			// it got removed from the page.
-			log.debug("component: " + component + " with markupid: " + markupId +
-				" not rendered because it was already removed from page");
-			return;
-		}
-
-		page.startComponentRender(component);
-
-		try
-		{
-			component.prepareForRender();
-
-			// render any associated headers of the component
-			respondHeaderContribution(response, component);
-		}
-		catch (RuntimeException e)
-		{
-			try
-			{
-				component.afterRender();
-			}
-			catch (RuntimeException e2)
-			{
-				// ignore this one could be a result off.
-			}
-			// Restore original response
-			RequestCycle.get().setResponse(response);
-			encodingBodyResponse.reset();
-			throw e;
-		}
-
-		try
-		{
-			component.render();
-		}
-		catch (RuntimeException e)
-		{
-			RequestCycle.get().setResponse(response);
-			encodingBodyResponse.reset();
-			throw e;
-		}
-
-		page.endComponentRender(component);
-
-		// Restore original response
-		RequestCycle.get().setResponse(response);
-
-		response.write("<component id=\"");
-		response.write(markupId);
-		response.write("\" ");
-		if (encodingBodyResponse.isContentsEncoded())
-		{
-			response.write(" encoding=\"");
-			response.write(getEncodingName());
-			response.write("\" ");
-		}
-		response.write("><![CDATA[");
-		response.write(encodingBodyResponse.getContents());
-		response.write("]]></component>");
-
-		encodingBodyResponse.reset();
-	}
-
-	/**
-	 * Header response for an ajax request.
-	 * 
-	 * @author Matej Knopp
+	 *            The component to get the focus or null.
 	 */
-	private class AjaxHeaderResponse extends HeaderResponse
-	{
-		@Override
-		public void render(HeaderItem item)
-		{
-			if (item instanceof OnLoadHeaderItem)
-			{
-				if (!wasItemRendered(item))
-				{
-					appendJavaScripts.add(((OnLoadHeaderItem)item).getJavaScript());
-					markItemRendered(item);
-				}
-			}
-			else if (item instanceof OnDomReadyHeaderItem)
-			{
-				if (!wasItemRendered(item))
-				{
-					domReadyJavaScripts.add(((OnDomReadyHeaderItem)item).getJavaScript());
-					markItemRendered(item);
-				}
-			}
-			else if (headerRendering)
-				super.render(item);
-			else
-				log.debug("Only methods that can be called on IHeaderResponse outside renderHead() are renderOnLoadJavaScript and renderOnDomReadyJavaScript");
-		}
-
-		/**
-		 * Construct.
-		 */
-		public AjaxHeaderResponse()
-		{
-		}
-
-		/**
-		 * 
-		 * @see org.apache.wicket.markup.head.internal.HeaderResponse#getRealResponse()
-		 */
-		@Override
-		protected Response getRealResponse()
-		{
-			return RequestCycle.get().getResponse();
-		}
-	}
-
-	// whether a header contribution is being rendered
-	private boolean headerRendering = false;
-	private HtmlHeaderContainer header = null;
-
-	private IHeaderResponse headerResponse;
+	void focusComponent(Component component);
 
 	/**
 	 * Returns the header response associated with current AjaxRequestTarget.
-	 * 
+	 *
 	 * Beware that only renderOnDomReadyJavaScript and renderOnLoadJavaScript can be called outside
 	 * the renderHeader(IHeaderResponse response) method. Calls to other render** methods will
 	 * result in the call failing with a debug-level log statement to help you see why it failed.
-	 * 
+	 *
 	 * @return header response
 	 */
-	public IHeaderResponse getHeaderResponse()
-	{
-		if (headerResponse == null)
-		{
-			// we don't need to decorate the header response here because this is called from
-			// within AjaxHtmlHeaderContainer, which decorates the response
-			headerResponse = new AjaxHeaderResponse();
-		}
-		return headerResponse;
-	}
-
-	/**
-	 * Header container component for ajax header contributions
-	 * 
-	 * @author Matej Knopp
-	 */
-	private static class AjaxHtmlHeaderContainer extends HtmlHeaderContainer
-	{
-		private static final long serialVersionUID = 1L;
-
-		/**
-		 * Construct.
-		 * 
-		 * @param id
-		 * @param target
-		 */
-		public AjaxHtmlHeaderContainer(String id, AjaxRequestTarget target)
-		{
-			super(id);
-			this.target = target;
-		}
-
-		/**
-		 * 
-		 * @see org.apache.wicket.markup.html.internal.HtmlHeaderContainer#newHeaderResponse()
-		 */
-		@Override
-		protected IHeaderResponse newHeaderResponse()
-		{
-			return target.getHeaderResponse();
-		}
-
-		private final transient AjaxRequestTarget target;
-	}
-
-	/**
-	 * 
-	 * @param response
-	 * @param component
-	 */
-	private void respondHeaderContribution(final Response response, final Component component)
-	{
-		headerRendering = true;
-
-		// create the htmlheadercontainer if needed
-		if (header == null)
-		{
-			header = new AjaxHtmlHeaderContainer(HtmlHeaderSectionHandler.HEADER_ID, this);
-			final Page page = component.getPage();
-			page.addOrReplace(header);
-		}
-
-		// save old response, set new
-		Response oldResponse = RequestCycle.get().setResponse(encodingHeaderResponse);
-
-		encodingHeaderResponse.reset();
-
-		// render the head of component and all it's children
-
-		component.renderHead(header);
-
-		if (component instanceof MarkupContainer)
-		{
-			((MarkupContainer)component).visitChildren(new IVisitor<Component, Void>()
-			{
-				@Override
-				public void component(final Component component, final IVisit<Void> visit)
-				{
-					if (component.isVisibleInHierarchy())
-					{
-						component.renderHead(header);
-					}
-					else
-					{
-						visit.dontGoDeeper();
-					}
-				}
-			});
-		}
-
-		// revert to old response
-		RequestCycle.get().setResponse(oldResponse);
-
-		writeHeaderContribution(response);
-
-		headerRendering = false;
-	}
-
-	private void respondInvocation(final Response response, final CharSequence js)
-	{
-		respondJavascriptInvocation("evaluate", response, js);
-	}
-
-	private void respondPriorityInvocation(final Response response, final CharSequence js)
-	{
-		respondJavascriptInvocation("priority-evaluate", response, js);
-	}
-
-
-	/**
-	 * @param invocation
-	 *            type of invocation tag, usually {@literal evaluate} or
-	 *            {@literal priority-evaluate}
-	 * @param response
-	 * @param js
-	 */
-	private void respondJavascriptInvocation(final String invocation, final Response response,
-		final CharSequence js)
-	{
-		boolean encoded = false;
-		CharSequence javascript = js;
-
-		// encode the response if needed
-		if (needsEncoding(js))
-		{
-			encoded = true;
-			javascript = encode(js);
-		}
-
-		response.write("<");
-		response.write(invocation);
-		if (encoded)
-		{
-			response.write(" encoding=\"");
-			response.write(getEncodingName());
-			response.write("\"");
-		}
-		response.write(">");
-
-		response.write("<![CDATA[");
-		response.write(javascript);
-		response.write("]]>");
-
-		response.write("</");
-		response.write(invocation);
-		response.write(">");
-
-		encodingBodyResponse.reset();
-	}
-
-	/**
-	 * 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.getActiveRequestHandler() instanceof AjaxRequestTarget)
-			{
-				return (AjaxRequestTarget)requestCycle.getActiveRequestHandler();
-			}
-			else if (requestCycle.getRequestHandlerScheduledAfterCurrent() instanceof AjaxRequestTarget)
-			{
-				return (AjaxRequestTarget)requestCycle.getRequestHandlerScheduledAfterCurrent();
-			}
-		}
-		return null;
-	}
+	IHeaderResponse getHeaderResponse();
 
 	/**
 	 * Returns the HTML id of the last focused element.
-	 * 
+	 *
 	 * @return markup id of last focused element, <code>null</code> if none
 	 */
-	public String getLastFocusedElementId()
-	{
-		WebRequest request = (WebRequest)RequestCycle.get().getRequest();
-		String id = request.getHeader("Wicket-FocusedElementId");
-		return Strings.isEmpty(id) ? null : id;
-	}
-
-	/**
-	 * @see org.apache.wicket.request.handler.IPageRequestHandler#getPageClass()
-	 */
-	@Override
-	public Class<? extends IRequestablePage> getPageClass()
-	{
-		return page.getPageClass();
-	}
+	String getLastFocusedElementId();
 
-	/**
-	 * @see org.apache.wicket.request.handler.IPageRequestHandler#getPageId()
-	 */
-	@Override
-	public Integer getPageId()
-	{
-		return page.getPageId();
-	}
 
 	/**
-	 * @see org.apache.wicket.request.handler.IPageRequestHandler#getPageParameters()
+	 * Returns the page. Be aware that the page can be instantiated if this wasn't the case already.
+	 *
+	 * @return page instance
 	 */
-	@Override
-	public PageParameters getPageParameters()
-	{
-		return page.getPageParameters();
-	}
-
-	@Override
-	public final boolean isPageInstanceCreated()
-	{
-		return true;
-	}
-
-	@Override
-	public final Integer getRenderCount()
-	{
-		return page.getRenderCount();
-	}
-
-	/** {@inheritDoc} */
-	@Override
-	public PageLogData getLogData()
-	{
-		return logData;
-	}
+	Page getPage();
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTargetListenerCollection.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTargetListenerCollection.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTargetListenerCollection.java
index c7f1cc5..a3153b8 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTargetListenerCollection.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestTargetListenerCollection.java
@@ -20,7 +20,7 @@ import org.apache.wicket.util.listener.ListenerCollection;
 
 /**
  * A collection of {@link AjaxRequestTarget.IListener}s
- * 
+ *
  * @since 1.5
  */
 public class AjaxRequestTargetListenerCollection extends

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxSelfUpdatingTimerBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxSelfUpdatingTimerBehavior.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxSelfUpdatingTimerBehavior.java
index 9d00331..0a7552e 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxSelfUpdatingTimerBehavior.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxSelfUpdatingTimerBehavior.java
@@ -42,7 +42,7 @@ public class AjaxSelfUpdatingTimerBehavior extends AbstractAjaxTimerBehavior
 	}
 
 	/**
-	 * @see org.apache.wicket.ajax.AbstractAjaxTimerBehavior#onTimer(org.apache.wicket.ajax.AjaxRequestTarget)
+	 * @see org.apache.wicket.ajax.AbstractAjaxTimerBehavior#onTimer(AjaxRequestTarget)
 	 */
 	@Override
 	protected final void onTimer(final AjaxRequestTarget target)

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormChoiceComponentUpdatingBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormChoiceComponentUpdatingBehavior.java b/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormChoiceComponentUpdatingBehavior.java
index f6e4070..1cb9d2a 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormChoiceComponentUpdatingBehavior.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormChoiceComponentUpdatingBehavior.java
@@ -21,13 +21,13 @@ import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnLoadHeaderItem;
 import org.apache.wicket.markup.html.form.CheckBoxMultipleChoice;
 import org.apache.wicket.markup.html.form.CheckGroup;
 import org.apache.wicket.markup.html.form.FormComponent;
 import org.apache.wicket.markup.html.form.RadioChoice;
 import org.apache.wicket.markup.html.form.RadioGroup;
-import org.apache.wicket.markup.head.JavaScriptHeaderItem;
-import org.apache.wicket.markup.head.OnLoadHeaderItem;
 import org.apache.wicket.util.string.AppendingStringBuffer;
 
 /**
@@ -88,7 +88,7 @@ public abstract class AjaxFormChoiceComponentUpdatingBehavior extends AbstractDe
 
 	/**
 	 * Called to handle any error resulting from updating form component. Errors thrown from
-	 * {@link #onUpdate(AjaxRequestTarget)} will not be caught here.
+	 * {@link #onUpdate(org.apache.wicket.ajax.AjaxRequestTarget)} will not be caught here.
 	 * 
 	 * The RuntimeException will be null if it was just a validation or conversion error of the
 	 * FormComponent

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormComponentUpdatingBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormComponentUpdatingBehavior.java b/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormComponentUpdatingBehavior.java
index 1c91e10..93795a0 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormComponentUpdatingBehavior.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/form/AjaxFormComponentUpdatingBehavior.java
@@ -19,12 +19,11 @@ package org.apache.wicket.ajax.form;
 import org.apache.wicket.Application;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.ajax.AjaxEventBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
 import org.apache.wicket.ajax.attributes.AjaxRequestAttributes.Method;
-import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.form.FormComponent;
 import org.apache.wicket.markup.html.form.validation.IFormValidator;
-import org.apache.wicket.util.string.AppendingStringBuffer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -191,7 +190,7 @@ public abstract class AjaxFormComponentUpdatingBehavior extends AjaxEventBehavio
 
 	/**
 	 * Called to handle any error resulting from updating form component. Errors thrown from
-	 * {@link #onUpdate(AjaxRequestTarget)} will not be caught here.
+	 * {@link #onUpdate(org.apache.wicket.ajax.AjaxRequestTarget)} will not be caught here.
 	 * 
 	 * The RuntimeException will be null if it was just a validation or conversion error of the
 	 * FormComponent

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java b/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java
index 8057c24..fe78ecb 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/AjaxFallbackLink.java
@@ -80,10 +80,6 @@ public abstract class AjaxFallbackLink<T> extends Link<T> implements IAjaxLink
 		{
 			private static final long serialVersionUID = 1L;
 
-			/**
-			 * 
-			 * @see org.apache.wicket.ajax.AjaxEventBehavior#onEvent(org.apache.wicket.ajax.AjaxRequestTarget)
-			 */
 			@Override
 			protected void onEvent(AjaxRequestTarget target)
 			{

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxButton.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxButton.java b/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxButton.java
index 0ade076..3df547d 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxButton.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxButton.java
@@ -91,20 +91,12 @@ public abstract class AjaxButton extends Button
 		{
 			private static final long serialVersionUID = 1L;
 
-			/**
-			 * 
-			 * @see org.apache.wicket.ajax.form.AjaxFormSubmitBehavior#onSubmit(org.apache.wicket.ajax.AjaxRequestTarget)
-			 */
 			@Override
 			protected void onSubmit(AjaxRequestTarget target)
 			{
 				AjaxButton.this.onSubmit(target, AjaxButton.this.getForm());
 			}
 
-			/**
-			 * 
-			 * @see org.apache.wicket.ajax.form.AjaxFormSubmitBehavior#onError(org.apache.wicket.ajax.AjaxRequestTarget)
-			 */
 			@Override
 			protected void onError(AjaxRequestTarget target)
 			{

http://git-wip-us.apache.org/repos/asf/wicket/blob/c2b44cca/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxCheckBox.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxCheckBox.java b/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxCheckBox.java
index f621347..bf9a6ca 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxCheckBox.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/markup/html/form/AjaxCheckBox.java
@@ -16,8 +16,8 @@
  */
 package org.apache.wicket.ajax.markup.html.form;
 
-import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
 import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
 import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
 import org.apache.wicket.markup.html.form.CheckBox;
 import org.apache.wicket.model.IModel;