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 }