All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wicket.ajax.AjaxRequestTarget Maven / Gradle / Ivy

Go to download

Pax Wicket Service is an OSGi extension of the Wicket framework, allowing for dynamic loading and unloading of Wicket components and pageSources.

There is a newer version: 5.0.0
Show newest version
/*
 * 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.Arrays;
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.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.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.request.resource.ResourceReference;
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.
 * 

* 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. *

* 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 ) *

* 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. *

* 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 */ public class AjaxRequestTarget implements IPageRequestHandler, ILoggableRequestHandler { /** * 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 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 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 * * 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 map, 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 { /** * Adds more javascript to the ajax response that will be executed on the client side * * @param script * javascript */ public void addJavaScript(String script); } /** * Response that uses an encoder to encode its contents * * @author Igor Vaynberg (ivaynberg) */ private final class AjaxResponse extends Response { 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. */ @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(AjaxRequestTarget.class); private final List appendJavaScripts = Generics.newArrayList(); private final List 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 markupIdToComponent = new LinkedHashMap(); /** */ private final List prependJavaScripts = Generics.newArrayList(); /** a list of listeners */ private List listeners = null; /** */ private final Set respondListeners = new HashSet(); /** 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() */ 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); } /** * 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 */ public void addListener(IListener listener) throws IllegalStateException { Args.notNull(listener, "listener"); assertListenersNotFrozen(); if (listeners == null) { listeners = new LinkedList(); } 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 childCriteria * * @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"); parent.visitChildren(childCriteria, new IVisitor() { public void component(final Component component, final IVisit 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 && !(component instanceof Page)) { 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} * @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 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. */ 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 + ");"); } /** * 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) */ 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; } /** * 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); } /** * Register the given respond listener. The listener's * {@link ITargetRespondListener#onTargetRespond(AjaxRequestTarget)} 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) */ 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(""); bodyResponse.write(""); // 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 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(""); } /** * 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 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 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 components = Collections.unmodifiableMap(markupIdToComponent); // create response that will be used by listeners to append // javascript final IJavaScriptResponse jsresponse = new IJavaScriptResponse() { 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 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(" element response.write(">"); response.write(encodingHeaderResponse.getContents()); response.write("]]>"); response.write(""); } } /** * Checks if the target contains an ancestor for the given component * * @param component * @return true 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(); } /** * @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 */ 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; } /** * * @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(""); encodingBodyResponse.reset(); } /** * Header response for an ajax request. * * @author Matej Knopp */ private class AjaxHeaderResponse extends HeaderResponse { private boolean checkHeaderRendering() { if (headerRendering == false) { log.debug("Only methods that can be called on IHeaderResponse outside renderHead() are renderOnLoadJavaScript and renderOnDomReadyJavaScript"); } return headerRendering; } @Override public void renderCSSReference(ResourceReference reference, String media) { if (checkHeaderRendering()) { super.renderCSSReference(reference, media); } } @Override public void renderCSSReference(String url) { if (checkHeaderRendering()) { super.renderCSSReference(url); } } @Override public void renderCSSReference(String url, String media) { if (checkHeaderRendering()) { super.renderCSSReference(url, media); } } @Override public void renderJavaScript(CharSequence javascript, String id) { if (checkHeaderRendering()) { super.renderJavaScript(javascript, id); } } @Override public void renderCSSReference(ResourceReference reference) { if (checkHeaderRendering()) { super.renderCSSReference(reference); } } @Override public void renderJavaScriptReference(ResourceReference reference) { if (checkHeaderRendering()) { super.renderJavaScriptReference(reference); } } @Override public void renderJavaScriptReference(ResourceReference reference, String id) { if (checkHeaderRendering()) { super.renderJavaScriptReference(reference, id); } } @Override public void renderJavaScriptReference(String url) { if (checkHeaderRendering()) { super.renderJavaScriptReference(url); } } @Override public void renderJavaScriptReference(String url, String id) { if (checkHeaderRendering()) { super.renderJavaScriptReference(url, id); } } @Override public void renderString(CharSequence string) { if (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 token = Arrays.asList("javascript-event", "window", "domready", javascript); if (wasRendered(token) == false) { domReadyJavaScripts.add(javascript); markRendered(token); } } /** * * @see org.apache.wicket.markup.html.internal.HeaderResponse#renderOnLoadJavaScript(java.lang.String) */ @Override public void renderOnLoadJavaScript(String javascript) { List token = Arrays.asList("javascript-event", "window", "load", javascript); if (wasRendered(token) == false) { // execute the javascript after all other scripts are executed appendJavaScripts.add(javascript); markRendered(token); } } /** * * @see org.apache.wicket.markup.html.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; /** * 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() { public void component(final Component component, final IVisit 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(""); response.write(""); encodingBodyResponse.reset(); } /** * Static method that returns current {@link AjaxRequestTarget} or null of no * {@link AjaxRequestTarget} is available. * * @return {@link AjaxRequestTarget} instance if current request is an Ajax request, * null 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; } /** * Returns the HTML id of the last focused element. * * @return markup id of last focused element, null 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() */ public Class getPageClass() { return page.getPageClass(); } /** * @see org.apache.wicket.request.handler.IPageRequestHandler#getPageId() */ public Integer getPageId() { return page.getPageId(); } /** * @see org.apache.wicket.request.handler.IPageRequestHandler#getPageParameters() */ public PageParameters getPageParameters() { return page.getPageParameters(); } public final boolean isPageInstanceCreated() { return true; } public final Integer getRenderCount() { return page.getRenderCount(); } /** {@inheritDoc} */ public PageLogData getLogData() { return logData; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy