001 // Copyright 2009 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 org.apache.tapestry5.*;
018 import org.apache.tapestry5.annotations.*;
019 import org.apache.tapestry5.dom.Element;
020 import org.apache.tapestry5.internal.services.ComponentResultProcessorWrapper;
021 import org.apache.tapestry5.ioc.annotations.Inject;
022 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
023 import org.apache.tapestry5.json.JSONObject;
024 import org.apache.tapestry5.services.ComponentEventResultProcessor;
025
026 import java.io.IOException;
027
028 /**
029 * A component used to implement the <a href="http://en.wikipedia.org/wiki/Progressive_enhancement">progressive
030 * enhancement</a> web design strategy; the component renders itself with a simplified initial content (i.e., "loading
031 * ...") and an Ajax request then supplies the component's true body. This results in much faster page loads. You can
032 * even nest these!
033 * <p/>
034 * The component simply does not render its body on initial render. On the subsequent action event request, it fires a
035 * {@link org.apache.tapestry5.EventConstants#PROGRESSIVE_DISPLAY} event to inform the container about the (optional)
036 * event context. The event handler method may return a renderable object; if not then the component's body is rendered
037 * as the partial markup response.
038 *
039 * @since 5.1.0.1
040 */
041 @SupportsInformalParameters
042 @IncludeJavaScriptLibrary("ProgressiveDisplay.js")
043 @Events(EventConstants.PROGRESSIVE_DISPLAY)
044 public class ProgressiveDisplay
045 {
046 /**
047 * The initial content to display until the real content arrives. Defaults to "Loading ..." and an Ajax activity
048 * icon.
049 */
050 @Parameter(defaultPrefix = BindingConstants.LITERAL, value = "block:defaultInitial")
051 private Block initial;
052
053 /**
054 * If provided, this is the event context, which will be provided via the {@link
055 * org.apache.tapestry5.EventConstants#PROGRESSIVE_DISPLAY event}.
056 */
057 @Parameter
058 private Object[] context;
059
060 @Inject
061 private ComponentResources resources;
062
063 @Environmental
064 private RenderSupport renderSupport;
065
066 @Environmental
067 private ComponentEventResultProcessor resultProcessor;
068
069 /**
070 * Name of a function on the client-side Tapestry.ElementEffect object that is invoked after the elements's body
071 * content has been updated. If not specified, then the basic "highlight" method is used, which performs a classic
072 * "yellow fade" to indicate to the user that and update has taken place.
073 */
074 @Parameter(defaultPrefix = BindingConstants.LITERAL)
075 private String update;
076
077 Block beginRender(MarkupWriter writer)
078 {
079 String clientId = renderSupport.allocateClientId(resources);
080 String elementName = resources.getElementName("div");
081
082 Element e = writer.element(elementName, "id", clientId);
083
084 resources.renderInformalParameters(writer);
085
086 e.addClassName("t-zone");
087
088 Link link = resources.createEventLink(EventConstants.ACTION, context);
089
090 JSONObject spec = new JSONObject();
091
092 if (InternalUtils.isNonBlank(update))
093 spec.put("update", update.toLowerCase());
094
095 spec.put("element", clientId);
096 spec.put("url", link.toAbsoluteURI());
097
098 renderSupport.addInit("progressiveDisplay", spec);
099
100 return initial;
101 }
102
103 Object onAction(EventContext context) throws IOException
104 {
105 ComponentResultProcessorWrapper wrapper = new ComponentResultProcessorWrapper(resultProcessor);
106
107 resources.triggerContextEvent(EventConstants.PROGRESSIVE_DISPLAY, context, wrapper);
108
109 if (wrapper.isAborted())
110 return null;
111
112 return resources.getBody();
113 }
114
115 boolean beforeRenderBody()
116 {
117 return false;
118 }
119
120 void afterRender(MarkupWriter writer)
121 {
122 writer.end();
123 }
124 }