    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    }