001 // Copyright 2009, 2010, 2011 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.corelib.components;
016
017 import java.io.IOException;
018
019 import org.apache.tapestry5.BindingConstants;
020 import org.apache.tapestry5.Block;
021 import org.apache.tapestry5.ComponentResources;
022 import org.apache.tapestry5.EventConstants;
023 import org.apache.tapestry5.EventContext;
024 import org.apache.tapestry5.Link;
025 import org.apache.tapestry5.MarkupWriter;
026 import org.apache.tapestry5.TrackableComponentEventCallback;
027 import org.apache.tapestry5.ajax.MultiZoneUpdate;
028 import org.apache.tapestry5.annotations.Environmental;
029 import org.apache.tapestry5.annotations.Events;
030 import org.apache.tapestry5.annotations.Import;
031 import org.apache.tapestry5.annotations.Parameter;
032 import org.apache.tapestry5.annotations.SupportsInformalParameters;
033 import org.apache.tapestry5.dom.Element;
034 import org.apache.tapestry5.ioc.annotations.Inject;
035 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
036 import org.apache.tapestry5.json.JSONObject;
037 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
038
039 /**
040 * A component used to implement the <a
041 * href="http://en.wikipedia.org/wiki/Progressive_enhancement">progressive
042 * enhancement</a> web design strategy; the component renders itself with a
043 * simplified initial content (i.e., "loading
044 * ...") and an Ajax request then supplies the component's true body. This
045 * results in much faster page loads. You can
046 * even nest these!
047 * <p/>
048 * The component simply does not render its body on initial render. On the subsequent action event request, it fires a
049 * {@link org.apache.tapestry5.EventConstants#PROGRESSIVE_DISPLAY} event to inform the container about the (optional)
050 * event context. The event handler method may return a renderable object; if not then the component's body is rendered
051 * as the partial markup response.
052 *
053 * @since 5.1.0.1
054 * @tapestrydoc
055 */
056 @SupportsInformalParameters
057 @Import(library = "ProgressiveDisplay.js")
058 @Events(EventConstants.PROGRESSIVE_DISPLAY)
059 @SuppressWarnings("all")
060 public class ProgressiveDisplay
061 {
062 /**
063 * The initial content to display until the real content arrives. Defaults
064 * to "Loading ..." and an Ajax activity
065 * icon.
066 */
067 @Parameter(defaultPrefix = BindingConstants.LITERAL, value = "block:defaultInitial")
068 private Block initial;
069
070 /**
071 * If provided, this is the event context, which will be provided via the
072 * {@link org.apache.tapestry5.EventConstants#PROGRESSIVE_DISPLAY event}.
073 */
074 @Parameter
075 private Object[] context;
076
077 @Inject
078 private ComponentResources resources;
079
080 @Environmental
081 private JavaScriptSupport jsSupport;
082
083 @Environmental
084 private TrackableComponentEventCallback eventCallback;
085
086 /**
087 * Name of a function on the client-side Tapestry.ElementEffect object that
088 * is invoked after the elements's body
089 * content has been updated. If not specified, then the basic "highlight"
090 * method is used, which performs a classic
091 * "yellow fade" to indicate to the user that and update has taken place.
092 */
093 @Parameter(defaultPrefix = BindingConstants.LITERAL)
094 private String update;
095
096 Block beginRender(MarkupWriter writer)
097 {
098 String clientId = jsSupport.allocateClientId(resources);
099 String elementName = resources.getElementName("div");
100
101 Element e = writer.element(elementName, "id", clientId);
102
103 resources.renderInformalParameters(writer);
104
105 e.addClassName("t-zone");
106
107 Link link = resources.createEventLink(EventConstants.ACTION, context);
108
109 JSONObject spec = new JSONObject();
110
111 if (InternalUtils.isNonBlank(update))
112 spec.put("update", update.toLowerCase());
113
114 spec.put("element", clientId);
115 spec.put("url", link.toURI());
116
117 jsSupport.addInitializerCall("progressiveDisplay", spec);
118
119 return initial;
120 }
121
122 Object onAction(EventContext context) throws IOException
123 {
124 resources.triggerContextEvent(EventConstants.PROGRESSIVE_DISPLAY, context, eventCallback);
125
126 if (eventCallback.isAborted())
127 return null;
128
129 return getBody();
130 }
131
132 boolean beforeRenderBody()
133 {
134 return false;
135 }
136
137 void afterRender(MarkupWriter writer)
138 {
139 writer.end();
140 }
141
142 /**
143 * Returns the body of the ProgressiveDisplay, which is sometimes (in the
144 * context of a {@linkplain MultiZoneUpdate multi-zone update})
145 * the content to be included.
146 *
147 * @since 5.2.0
148 * @return body of component
149 */
150 public Block getBody()
151 {
152 return resources.getBody();
153 }
154 }