001    // Copyright 2007, 2008, 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.Environmental;
019    import org.apache.tapestry5.annotations.Parameter;
020    import org.apache.tapestry5.annotations.SupportsInformalParameters;
021    import org.apache.tapestry5.dom.Element;
022    import org.apache.tapestry5.ioc.annotations.Inject;
023    import org.apache.tapestry5.json.JSONObject;
024    import org.apache.tapestry5.services.ClientBehaviorSupport;
025    
026    
027    /**
028     * A Zone is portion of the output page designed for easy dynamic updating via Ajax or other client-side effects.  A
029     * Zone renders out as a <div> element (or whatever is specified in the template) and may have content initially,
030     * or may only get its content as a result of client side activity.
031     * <p/>
032     * Often, Zones are initially invisible, in which case the visible parameter may be set to false (it defaults to true).
033     * <p/>
034     * When a user clicks an {@link org.apache.tapestry5.corelib.components.ActionLink} whose zone parameter is set, the
035     * corresponding client-side Tapestry.ZoneManager object is located. It will update the content of the Zone's
036     * &lt;div&gt; and then invoke either a show method (if the div is not visible) or an update method (if the div is
037     * visible).  The show and update parameters are the <em>names</em> of functions attached to the Tapestry.ElementEffect
038     * object.    Likewise, a {@link org.apache.tapestry5.corelib.components.Form} component may also trigger an update of a
039     * client-side Zone.
040     * <p/>
041     * The server side event handler can return a {@link org.apache.tapestry5.Block} or a component to render as the new
042     * content on the client side. Often, re-rendering the Zone's {@linkplain #getBody() body} is useful. Multiple
043     * client-side zones may be updated by returning a {@link org.apache.tapestry5.ajax.MultiZoneUpdate}.
044     * <p/>
045     * Renders informal parameters, adding CSS class "t-zone" and possibly, "t-invisible".
046     * <p/>
047     * You will often want to specify the id parameter of the Zone, in addition to it's Tapestry component id; this "locks
048     * down" the client-side id, so the same value is used even in later partial renders of the page (essential if the Zone
049     * is nested inside another Zone).  When you specify the client-side id, it is used exactly as provided (meaning that
050     * you are responsible for ensuring that there will not be an id conflict even in the face of multiple partial renders
051     * of the page). Failure to provide an explicit id results in a new, and non-predictable, id being generated for each
052     * partial render, which will often result in client-side failures to locate the element to update when the Zone is
053     * triggered.
054     * <p/>
055     * After the client-side content is updated, a client-side event is fired on the zone's element. The constant
056     * Tapestry.ZONE_UPDATED_EVENT can be used to listen to the event.
057     */
058    @SupportsInformalParameters
059    public class Zone implements ClientElement
060    {
061        /**
062         * Name of a function on the client-side Tapestry.ElementEffect object that is invoked to make the Zone's
063         * &lt;div&gt; visible before being updated.  If not specified, then the basic "show" method is used.
064         */
065        @Parameter(defaultPrefix = BindingConstants.LITERAL)
066        private String show;
067    
068        /**
069         * Name of a function on the client-side Tapestry.ElementEffect object that is invoked after the Zone's content has
070         * been updated. If not specified, then the basic "highlight" method is used, which performs a classic "yellow fade"
071         * to indicate to the user that and update has taken place.
072         */
073        @Parameter(defaultPrefix = BindingConstants.LITERAL)
074        private String update;
075    
076        /**
077         * The element name to render for the zone; this defaults to the element actually used in the template, or "div" if
078         * no specific element was specified.
079         */
080        @Parameter(required = true, allowNull = false, defaultPrefix = BindingConstants.LITERAL)
081        private String elementName;
082    
083        /**
084         * If bound, then the id attribute of the rendered element will be this exact value. If not bound, then a unique id
085         * is generated for the element.
086         */
087        @Parameter(name = "id", defaultPrefix = BindingConstants.LITERAL)
088        private String idParameter;
089    
090        @Environmental
091        private RenderSupport renderSupport;
092    
093        @Environmental
094        private ClientBehaviorSupport clientBehaviorSupport;
095    
096        /**
097         * If true (the default) then the zone will render normally.  If false, then the "t-invisible" CSS class is added,
098         * which will make the zone initially invisible.
099         */
100        @Parameter
101        private boolean visible = true;
102    
103        @Inject
104        private ComponentResources resources;
105    
106        private String clientId;
107    
108        String defaultElementName()
109        {
110            return resources.getElementName("div");
111        }
112    
113        void beginRender(MarkupWriter writer)
114        {
115            clientId = resources.isBound("id") ? idParameter : renderSupport.allocateClientId(resources);
116    
117            Element e = writer.element(elementName, "id", clientId);
118    
119            resources.renderInformalParameters(writer);
120    
121            e.addClassName("t-zone");
122    
123            if (!visible) e.addClassName(CSSClassConstants.INVISIBLE);
124    
125            // And continue on to render the body
126    
127            JSONObject spec = new JSONObject();
128            spec.put("div", clientId);
129    
130            clientBehaviorSupport.addZone(clientId, show, update);
131        }
132    
133        void afterRender(MarkupWriter writer)
134        {
135            writer.end(); // div
136        }
137    
138        /**
139         * The client id of the Zone; this is set when the Zone renders and will either be the value bound to the id
140         * parameter, or an allocated unique id.
141         *
142         * @return client-side element id
143         */
144        public String getClientId()
145        {
146            return clientId;
147        }
148    
149        /**
150         * Returns the zone's body (the content enclosed by its start and end tags). This is often used as part of an Ajax
151         * partial page render to update the client with a fresh render of the content inside the zone.
152         *
153         * @return the zone's body as a Block
154         */
155        public Block getBody()
156        {
157            return resources.getBody();
158        }
159    }