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/08/24 14:49:43 UTC
svn commit: r688502 [2/2] - in
/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng:
./ js/ json/ request/
Modified: wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/request/AjaxRequestTarget.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/request/AjaxRequestTarget.java?rev=688502&r1=688501&r2=688502&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/request/AjaxRequestTarget.java (original)
+++ wicket/sandbox/knopp/experimental/wicket/src/main/java/org/apache/wicket/ajaxng/request/AjaxRequestTarget.java Sun Aug 24 05:49:42 2008
@@ -16,22 +16,137 @@
*/
package org.apache.wicket.ajaxng.request;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.IRequestTarget;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.Page;
import org.apache.wicket.RequestCycle;
+import org.apache.wicket.ResourceReference;
+import org.apache.wicket.Response;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.ajaxng.AjaxBehavior;
+import org.apache.wicket.ajaxng.json.JSONArray;
+import org.apache.wicket.ajaxng.json.JSONObject;
+import org.apache.wicket.behavior.IBehavior;
+import org.apache.wicket.markup.html.IHeaderResponse;
+import org.apache.wicket.markup.html.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.protocol.http.WebResponse;
+import org.apache.wicket.response.StringResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class AjaxRequestTarget implements IRequestTarget
{
private AjaxRequestTarget()
{
+ this.component = null;
+ this.page = null;
+ this.behaviorIndex = -1;
+ }
+
+ /**
+ * An {@link AjaxRequestTarget} listener that can be used to respond to various target-related
+ * events
+ *
+ */
+ public static interface IListener
+ {
+ /**
+ * Triggered before ajax request target begins its response cycle
+ *
+ * @param components
+ * read-only list of component entries already added to the target
+ * @param target
+ * the target itself. Could be used to add components or to append/prepend
+ * javascript
+ *
+ */
+ public void onBeforeRespond(List<ComponentEntry> components, 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
+ *
+ * NOTE: During this stage of processing any calls to target that manipulate the response
+ * (adding components, javascript) will have no effect
+ *
+ * @param components
+ * read-only list of component entries added to the target
+ * @param response
+ * response object that can be used to output javascript
+ */
+ public void onAfterRespond(List<ComponentEntry> components, IJavascriptResponse response);
}
-
- private Component component;
- private int behaviorIndex;
-
+
+ /**
+ * An ajax javascript response that allows users to add javascript to be executed on the client
+ * side
+ *
+ * @author ivaynberg
+ */
+ public static 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);
+ }
+
+ private final Page page;
+ private final Component component;
+ private final int behaviorIndex;
+
+ private final List<ComponentEntry> entries = new ArrayList<ComponentEntry>();
+
+ private final List<JavascriptEntry> prependJavascripts = new ArrayList<JavascriptEntry>();
+ private final List<JavascriptEntry> appendJavascripts = new ArrayList<JavascriptEntry>();
+
+ private final List<JavascriptEntry> domReadyJavascripts = new ArrayList<JavascriptEntry>();
+
+ private final List<IListener> listeners = new ArrayList<IListener>();
+
+ private static final Logger log = LoggerFactory.getLogger(AjaxRequestTarget.class);
+
+ private String redirect = null;
+
+ // whether a header contribution is being rendered
+ private boolean headerRendering = false;
+ private HtmlHeaderContainer header = null;
+
+ private IHeaderResponse headerResponse;
+
+ /**
+ * Construct.
+ *
+ * @param component
+ * @param behaviorIndex
+ */
public AjaxRequestTarget(Component component, int behaviorIndex)
{
+ if (component == null)
+ {
+ throw new IllegalArgumentException("Argument 'component' may not be null.");
+ }
+ page = component.getPage();
+ if (page == null)
+ {
+ throw new IllegalArgumentException("Component must belong to a page.");
+ }
this.component = component;
this.behaviorIndex = behaviorIndex;
}
@@ -40,8 +155,854 @@
{
}
+ /**
+ * Returns component that has behavior which initiated this Ajax request.
+ *
+ * @return component
+ */
+ public Component getComponent()
+ {
+ return component;
+ }
+
+ /**
+ * Entry for a single component. Allows to specify custom javascript handlers executed before
+ * and after replacing the component. Also the actual component replacement can be overriden.
+ *
+ * @author Matej Knopp
+ */
+ public static class ComponentEntry
+ {
+ private final Component component;
+ private String beforeReplaceJavascript;
+ private String afterReplaceJavascript;
+ private String replaceJavascript;
+
+ /**
+ * Construct.
+ *
+ * @param component
+ */
+ public ComponentEntry(Component component)
+ {
+ this.component = component;
+ }
+
+ /**
+ * Construct.
+ *
+ * @param entry
+ * entry to copy
+ */
+ public ComponentEntry(ComponentEntry entry)
+ {
+ this.component = entry.component;
+ this.beforeReplaceJavascript = entry.beforeReplaceJavascript;
+ this.afterReplaceJavascript = entry.afterReplaceJavascript;
+ this.replaceJavascript = entry.replaceJavascript;
+ }
+
+ /**
+ * Returns component that will be updated.
+ *
+ * @return component
+ */
+ public Component getComponent()
+ {
+ return component;
+ }
+
+ /**
+ * Sets the javascript executed right before replacing the component.
+ * <p>
+ * The javascript can use following variables:
+ * <dl>
+ * <dt>requestQueueItem</dt>
+ * <dd>RequestQueueItem instance for current request</dd>
+ * <dt>componentId</dt>
+ * <dd>MarkupId of component that is about to be replaced
+ * <dt>sourceComponentId</dt>
+ * <dd>MarkupId of component that has initiated current ajax request or <code>null</code>
+ * if the component is not available.
+ * <dt>done</dt>
+ * <dd>Method that javascript needs to execute after it has finished. Note that it is
+ * mandatory to call this method otherwise the processing pipeline will stop</dd>
+ * </dl>
+ *
+ * @param beforeReplaceJavascript
+ * the javascript
+ */
+ public void setBeforeReplaceJavascript(String beforeReplaceJavascript)
+ {
+ this.beforeReplaceJavascript = beforeReplaceJavascript;
+ }
+
+ /**
+ * Returns the javascript executed before replacing the component.
+ *
+ * @see #setBeforeReplaceJavascript(String)
+ * @return javascript
+ */
+ public String getBeforeReplaceJavascript()
+ {
+ return beforeReplaceJavascript;
+ }
+
+ /**
+ * Sets the javascript executed right after replacing the component.
+ * <p>
+ * The javascript can use following variables:
+ * <dl>
+ * <dt>requestQueueItem</dt>
+ * <dd>RequestQueueItem instance for current request</dd>
+ * <dt>componentId</dt>
+ * <dd>MarkupId of component that has been replaced
+ * <dt>done</dt>
+ * <dd>Method that javascript needs to execute after it has finished. Note that it is
+ * mandatory to call this method otherwise the processing pipeline will stop</dd>
+ * </dl>
+ *
+ * @param afterReplaceJavascript
+ * the javascript
+ */
+ public void setAfterReplaceJavascript(String afterReplaceJavascript)
+ {
+ this.afterReplaceJavascript = afterReplaceJavascript;
+ }
+
+ /**
+ * Returns the javascript executed after replacing the component.
+ *
+ * @see #setAfterReplaceJavascript(String)
+ * @return javascript
+ */
+ public String getAfterReplaceJavascript()
+ {
+ return afterReplaceJavascript;
+ }
+
+ /**
+ * Sets the javascript executed to replace the component.
+ * <p>
+ * The javascript can use following variables:
+ * <dl>
+ * <dt>requestQueueItem</dt>
+ * <dd>RequestQueueItem instance for current request</dd>
+ * <dt>componentId</dt>
+ * <dd>MarkupId of component that has been replaced
+ * <dt>markup</dt>
+ * <dd>The new markup that should replace current markup</dd>
+ * <dt>done</dt>
+ * <dd>Method that javascript needs to execute after the component has been replaced. Note
+ * that it is mandatory to call this method otherwise the processing pipeline will stop</dd>
+ * </dl>
+ *
+ * An example javascript:
+ *
+ * <pre>
+ * var element = W.$(componentId);
+ * W.replaceOuterHtml(element, markup);
+ * done();
+ * </pre>
+ *
+ * @param replaceJavascript
+ * the javascript
+ */
+ public void setReplaceJavascript(String replaceJavascript)
+ {
+ this.replaceJavascript = replaceJavascript;
+ }
+
+ /**
+ * Returns the javascript executed to replace component.
+ *
+ * @see #setReplaceJavascript(String)
+ * @return javsacript executed to replace component
+ */
+ public String getReplaceJavascript()
+ {
+ return replaceJavascript;
+ }
+ }
+
+ private static class UnmodifiableComponentEntry extends ComponentEntry
+ {
+
+ public UnmodifiableComponentEntry(ComponentEntry entry)
+ {
+ super(entry);
+ }
+
+ @Override
+ public void setAfterReplaceJavascript(String javascript)
+ {
+ throw new UnsupportedOperationException("ComponentEntry is can not be modified.");
+ }
+
+ @Override
+ public void setBeforeReplaceJavascript(String javascript)
+ {
+ throw new UnsupportedOperationException("ComponentEntry is can not be modified.");
+ }
+
+ @Override
+ public void setReplaceJavascript(String javascript)
+ {
+ throw new UnsupportedOperationException("ComponentEntry is can not be modified.");
+ }
+ }
+
+ private boolean isParent(Component parent, Component component)
+ {
+ Component p = component.getParent();
+ while (p != null && p != parent)
+ {
+ p = p.getParent();
+ }
+ return p == null;
+ }
+
+ private void checkComponent(Component component)
+ {
+ if (component == null)
+ {
+ throw new IllegalArgumentException("Component may not be null.");
+ }
+ else if (component instanceof Page)
+ {
+ throw new IllegalArgumentException("Component cannot be a page");
+ }
+ else if (!component.getOutputMarkupId())
+ {
+ throw new IllegalStateException("Component " + component.getClass().getName() +
+ " must have setOuputMarkupId set in order to be updated via ajax.");
+ }
+ else if (component.getRenderBodyOnly())
+ {
+ throw new IllegalStateException("Component " + component.getClass().getName() +
+ " must not have setRenderBodyOnly set in order to be updated via ajax.");
+ }
+ 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.");
+ }
+ }
+
+ public boolean addComponent(ComponentEntry entry)
+ {
+ if (entry == null)
+ {
+ throw new IllegalArgumentException("Argument 'entry' may not be null.");
+ }
+
+ final Component component = entry.getComponent();
+ checkComponent(component);
+
+ for (ComponentEntry e : entries)
+ {
+ if (e.getComponent() == component)
+ {
+ return false;
+ }
+ // check if component's parent is already in queue
+ else if (isParent(e.getComponent(), component))
+ {
+ return false;
+ }
+ // check if new component is parent of existing component
+ else if (isParent(component, e.getComponent()))
+ {
+ entries.remove(e);
+ break;
+ }
+ }
+ entries.add(entry);
+ return true;
+ }
+
+ public boolean addComponent(Component component)
+ {
+ if (component == null)
+ {
+ throw new IllegalArgumentException("Argument 'component' may not be null.");
+ }
+ return addComponent(new ComponentEntry(component));
+ }
+
+ private static class JavascriptEntry
+ {
+ private final String javascript;
+ private final boolean async;
+
+ public JavascriptEntry(String javascript, boolean async)
+ {
+ this.javascript = javascript;
+ this.async = async;
+ }
+
+ public String getJavascript()
+ {
+ return javascript;
+ }
+
+ public boolean isAsync()
+ {
+ return async;
+ }
+ };
+
+ /**
+ * Adds javascript that will be evaluated on the client side before components are replaced
+ * <p>
+ * The javascript can use following variables:
+ * <dl>
+ * <dt>requestQueueItem</dt>
+ * <dd>RequestQueueItem instance for current request</dd>
+ * <dt>done</dt>
+ * <dd>Must be called for asynchronous javascript
+ * </dl>
+ *
+ * @param javascript
+ * javascript to be evaluated
+ * @param async
+ * indicates if the javascript should be evaluated asynchrously. If
+ * <code>async</code> is <code>true</code>, the javascript must invoke the
+ * <code>done</code> function that it gets passed for the processing queue to
+ * continue.
+ */
+ public void prependJavascript(String javascript, boolean async)
+ {
+ if (javascript == null)
+ {
+ throw new IllegalArgumentException("Argument 'javascript' may not be null.");
+ }
+ prependJavascripts.add(new JavascriptEntry(javascript, async));
+ }
+
+ /**
+ * Adds javascript that will be evaluated on the client side before components are replaced. The
+ * javascript will be executed synchronously which means that the processing queue will be held
+ * until the javascript finishes.
+ * <p>
+ * The javascript can use following variables:
+ * <dl>
+ * <dt>requestQueueItem</dt>
+ * <dd>RequestQueueItem instance for current request</dd>
+ * </dl>
+ *
+ * @param javascript
+ * javascript to be evaluated
+ */
+ public void prependJavascript(String javascript)
+ {
+ prependJavascript(javascript, false);
+ }
+
+ /**
+ * Adds javascript that will be evaluated on the client side after components are replaced
+ * <p>
+ * The javascript can use following variables:
+ * <dl>
+ * <dt>requestQueueItem</dt>
+ * <dd>RequestQueueItem instance for current request</dd>
+ * <dt>done</dt>
+ * <dd>Must be called for asynchronous javascript
+ * </dl>
+ *
+ * @param javascript
+ * javascript to be evaluated
+ * @param async
+ * indicates if the javascript should be evaluated asynchrously. If
+ * <code>async</code> is <code>true</code>, the javascript must invoke the
+ * <code>done</code> function that it gets passed for the processing queue to
+ * continue.
+ */
+ public void appendJavascript(String javascript, boolean async)
+ {
+ if (javascript == null)
+ {
+ throw new IllegalArgumentException("Argument 'javascript' may not be null.");
+ }
+ appendJavascripts.add(new JavascriptEntry(javascript, async));
+ }
+
+ /**
+ * Adds javascript that will be evaluated on the client side after components are replaced. The
+ * javascript will be executed synchronously which means that the processing queue will be held
+ * until the javascript finishes.
+ * <p>
+ * The javascript can use following variables:
+ * <dl>
+ * <dt>requestQueueItem</dt>
+ * <dd>RequestQueueItem instance for current request</dd>
+ * </dl>
+ *
+ * @param javascript
+ * javascript to be evaluated
+ */
+ public void appendJavascript(String javascript)
+ {
+ appendJavascript(javascript, false);
+ }
+
+
+ /**
+ * Adds a listener to this target
+ *
+ * @param listener
+ */
+ public void addListener(IListener listener)
+ {
+ if (listener == null)
+ {
+ throw new IllegalArgumentException("Argument `listener` cannot be null");
+ }
+ listeners.add(listener);
+ }
+
+ private List<ComponentEntry> entriesCopy()
+ {
+ List<ComponentEntry> list = new ArrayList<ComponentEntry>(entries.size());
+ for (ComponentEntry e : entries)
+ {
+ list.add(new UnmodifiableComponentEntry(e));
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ private void fireOnBeforeRespondListeners(List<ComponentEntry> entries)
+ {
+ if (!listeners.isEmpty())
+ {
+ for (IListener l : listeners)
+ {
+ l.onBeforeRespond(entries, this);
+ }
+ }
+ }
+
+ private void fireOnAfterRespondListeners(List<ComponentEntry> entries)
+ {
+ // invoke onafterresponse event on listeners
+ if (!(listeners.isEmpty()))
+ {
+ // create response that will be used by listeners to append
+ // javascript
+ final IJavascriptResponse jsresponse = new IJavascriptResponse()
+ {
+
+ public void addJavascript(String script)
+ {
+ appendJavascript(script, false);
+ }
+ };
+
+ for (IListener listener : listeners)
+ {
+ listener.onAfterRespond(entries, jsresponse);
+ }
+ }
+ }
+
+ /**
+ * Header response for an ajax request.
+ *
+ * @author Matej Knopp
+ */
+ private class AjaxHeaderResponse extends HeaderResponse
+ {
+
+ private static final long serialVersionUID = 1L;
+
+ private void checkHeaderRendering()
+ {
+ if (headerRendering == false)
+ {
+ throw new WicketRuntimeException(
+ "Only methods that can be called on IHeaderResponse outside renderHead() are renderOnLoadJavascript and renderOnDomReadyJavascript");
+ }
+ }
+
+ @Override
+ public void renderCSSReference(ResourceReference reference, String media)
+ {
+ checkHeaderRendering();
+ super.renderCSSReference(reference, media);
+ }
+
+ @Override
+ public void renderCSSReference(String url)
+ {
+ checkHeaderRendering();
+ super.renderCSSReference(url);
+ }
+
+ @Override
+ public void renderCSSReference(String url, String media)
+ {
+ checkHeaderRendering();
+ super.renderCSSReference(url, media);
+ }
+
+ @Override
+ public void renderJavascript(CharSequence javascript, String id)
+ {
+ checkHeaderRendering();
+ super.renderJavascript(javascript, id);
+ }
+
+ @Override
+ public void renderCSSReference(ResourceReference reference)
+ {
+ checkHeaderRendering();
+ super.renderCSSReference(reference);
+ }
+
+ @Override
+ public void renderJavascriptReference(ResourceReference reference)
+ {
+ checkHeaderRendering();
+ super.renderJavascriptReference(reference);
+ }
+
+ @Override
+ public void renderJavascriptReference(ResourceReference reference, String id)
+ {
+ checkHeaderRendering();
+ super.renderJavascriptReference(reference, id);
+ }
+
+ @Override
+ public void renderJavascriptReference(String url)
+ {
+ checkHeaderRendering();
+ super.renderJavascriptReference(url);
+ }
+
+ @Override
+ public void renderJavascriptReference(String url, String id)
+ {
+ checkHeaderRendering();
+ super.renderJavascriptReference(url, id);
+ }
+
+ @Override
+ public void renderString(CharSequence string)
+ {
+ checkHeaderRendering();
+ super.renderString(string);
+ }
+
+ /**
+ * Construct.
+ */
+ public AjaxHeaderResponse()
+ {
+
+ }
+
+ /**
+ *
+ * @see org.apache.wicket.markup.html.internal.HeaderResponse#renderOnDomReadyJavascript(java.lang.String)
+ */
+ @Override
+ public void renderOnDomReadyJavascript(String javascript)
+ {
+ List<String> token = Arrays.asList(new String[] { "javascript-event", "window",
+ "domready", javascript });
+ if (wasRendered(token) == false)
+ {
+ domReadyJavascripts.add(new JavascriptEntry(javascript, false));
+ markRendered(token);
+ }
+ }
+
+ /**
+ *
+ * @see org.apache.wicket.markup.html.internal.HeaderResponse#renderOnLoadJavascript(java.lang.String)
+ */
+ @Override
+ public void renderOnLoadJavascript(String javascript)
+ {
+ List<String> token = Arrays.asList(new String[] { "javascript-event", "window", "load",
+ javascript });
+ if (wasRendered(token) == false)
+ {
+ // execute the javascript after all other scripts are executed
+ appendJavascript(javascript, false);
+ markRendered(token);
+ }
+ }
+
+ /**
+ *
+ * @see org.apache.wicket.markup.html.internal.HeaderResponse#getRealResponse()
+ */
+ @Override
+ protected Response getRealResponse()
+ {
+ return RequestCycle.get().getResponse();
+ }
+ };
+
+ /**
+ * 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 an exception being thrown.
+ *
+ * @return header response
+ */
+ public IHeaderResponse getHeaderResponse()
+ {
+ if (headerResponse == null)
+ {
+ 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)
+ {
+ // render the head of component and all it's children
+
+ component.renderHead(header);
+
+ if (component instanceof MarkupContainer)
+ {
+ ((MarkupContainer)component).visitChildren(new Component.IVisitor<Component>()
+ {
+ public Object component(Component component)
+ {
+ if (component.isVisible())
+ {
+ component.renderHead(header);
+ return CONTINUE_TRAVERSAL;
+ }
+ else
+ {
+ return CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER;
+ }
+ }
+ });
+ }
+ }
+
+ private String respondHeaderContribution()
+ {
+ 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
+ StringResponse stringResponse = new StringResponse();
+ Response oldResponse = RequestCycle.get().setResponse(stringResponse);
+
+ for (ComponentEntry e : entries)
+ {
+ respondHeaderContribution(stringResponse, component);
+ }
+
+ // revert to old response
+ RequestCycle.get().setResponse(oldResponse);
+
+ headerRendering = false;
+ return stringResponse.toString();
+ }
+
+ public void setRedirect(String redirect)
+ {
+ this.redirect = redirect;
+ }
+
+ private void prepareRender()
+ {
+ for (Iterator<ComponentEntry> i = entries.iterator(); i.hasNext();)
+ {
+ ComponentEntry entry = i.next();
+ Component component = entry.getComponent();
+
+ final Page 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: " + component.getMarkupId() +
+ " not rendered because it was already removed from page");
+ i.remove();
+ continue;
+ }
+
+ checkComponent(component);
+
+ component.prepareForRender();
+ }
+ }
+
+ private String renderComponent(Component component)
+ {
+ StringResponse stringResponse = new StringResponse();
+ Response originalResponse = RequestCycle.get().setResponse(stringResponse);
+
+ page.startComponentRender(component);
+ component.renderComponent();
+ page.endComponentRender(component);
+
+ RequestCycle.get().setResponse(originalResponse);
+ return stringResponse.toString();
+ }
+
+ private JSONObject renderComponentEntry(ComponentEntry componentEntry)
+ {
+ JSONObject object = new JSONObject();
+
+ Component component = componentEntry.getComponent();
+ object.put("componentId", component.getId());
+ object.put("beforeReplaceJavascript", componentEntry.getBeforeReplaceJavascript());
+ object.put("afterReplaceJavascript", componentEntry.getAfterReplaceJavascript());
+ object.put("replaceJavascript", componentEntry.getReplaceJavascript());
+ object.put("markup", renderComponent(component));
+
+ return object;
+ }
+
+ private JSONObject renderJavascriptEntry(JavascriptEntry javascriptEntry)
+ {
+ JSONObject object = new JSONObject();
+
+ object.put("async", javascriptEntry.isAsync());
+ object.put("javascript", javascriptEntry.getJavascript());
+
+ return object;
+ }
+
public void respond(RequestCycle requestCycle)
{
+ IBehavior behavior = component.getBehaviors().get(behaviorIndex);
+ if (behavior instanceof AjaxBehavior == false)
+ {
+ throw new WicketRuntimeException("Behavior must be instance of AjaxBehavior.");
+ }
+ ((AjaxBehavior)behavior).respond(this);
+
+ List<ComponentEntry> entriesCopy = entriesCopy();
+ fireOnBeforeRespondListeners(entriesCopy);
+
+ JSONObject response = new JSONObject();
+
+ if (redirect != null)
+ {
+ response.put("redirect", redirect);
+ }
+ else
+ {
+ prepareRender();
+
+ response.put("header", respondHeaderContribution());
+
+ JSONArray components = new JSONArray();
+ response.put("components", components);
+ for (ComponentEntry entry : entries)
+ {
+ components.put(renderComponentEntry(entry));
+ }
+
+ fireOnAfterRespondListeners(entries);
+
+ JSONArray prependJavascripts = new JSONArray();
+ response.put("prependJavascript", prependJavascripts);
+
+ for (JavascriptEntry e : this.prependJavascripts)
+ {
+ prependJavascripts.put(renderJavascriptEntry(e));
+ }
+
+ JSONArray appendJavascripts = new JSONArray();
+ response.put("appendJavascript", appendJavascripts);
+
+ for (JavascriptEntry e : this.domReadyJavascripts)
+ {
+ appendJavascripts.put(renderJavascriptEntry(e));
+ }
+
+ for (JavascriptEntry e : this.appendJavascripts)
+ {
+ appendJavascripts.put(renderJavascriptEntry(e));
+ }
+ }
+
+ WebResponse webResponse = (WebResponse) requestCycle.getResponse();
+ prepareResponse(webResponse);
+
+ webResponse.write("if (false) (");
+ webResponse.write(response.toString());
+ webResponse.write(")");
+ }
+
+ private void prepareResponse(WebResponse response)
+ {
+ final Application app = Application.get();
+
+ // Determine encoding
+ final String encoding = app.getRequestCycleSettings().getResponseRequestEncoding();
+
+ // Set content type based on markup type for page
+ response.setCharacterEncoding(encoding);
+ response.setContentType("text/xml; charset=" + encoding);
+
+ // Make sure it is not cached by a client
+ response.setHeader("Expires", "Mon, 26 Jul 1997 05:00:00 GMT");
+ response.setHeader("Cache-Control", "no-cache, must-revalidate");
+ response.setHeader("Pragma", "no-cache");
}
public static final AjaxRequestTarget DUMMY = new AjaxRequestTarget();