    001    // Copyright 2008, 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 org.apache.tapestry5.*;
    018    import org.apache.tapestry5.annotations.Environmental;
    019    import org.apache.tapestry5.annotations.Events;
    020    import org.apache.tapestry5.annotations.Parameter;
    021    import org.apache.tapestry5.annotations.SupportsInformalParameters;
    022    import org.apache.tapestry5.corelib.data.InsertPosition;
    023    import org.apache.tapestry5.dom.Element;
    024    import org.apache.tapestry5.internal.services.RequestConstants;
    025    import org.apache.tapestry5.ioc.annotations.Inject;
    026    import org.apache.tapestry5.json.JSONObject;
    027    import org.apache.tapestry5.services.ClientBehaviorSupport;
    028    import org.apache.tapestry5.services.FormSupport;
    029    import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
    030    import org.apache.tapestry5.services.ajax.JSONCallback;
    031    import org.apache.tapestry5.services.javascript.JavaScriptSupport;
    032
    033    import java.io.IOException;
    034
    035    /**
    036     * A way to add new content to an existing Form. The FormInjector emulates its tag from the template (or uses a
    037     * &lt;div&gt;). When triggered, new content is obtained from the application and is injected before or after the
    038     * element.
    039     * <p/>
    040     * On the client side, a new function, trigger(), is added to the element. Invoking this client-side function will
    041     * trigger the FormInjector; a request is sent to the server, new content is generated, and the new content is placed
    042     * before or after (per configuration) the existing FormInjector element.
    043     *
    044     * @tapestrydoc
    045     */
    046    @SupportsInformalParameters
    047    @Events(EventConstants.ACTION)
    048    public class FormInjector implements ClientElement
    049    {
    050        public static final String INJECT_EVENT = "inject";
    051
    052        /**
    053         * The context for the link (optional parameter). This list of values will be converted into strings and included in
    054         * the URI. The strings will be coerced back to whatever their values are and made available to event handler
    055         * methods.
    056         */
    057        @Parameter
    058        private Object[] context;
    059
    060        @Parameter(defaultPrefix = BindingConstants.LITERAL,
    061                value = BindingConstants.SYMBOL + ":" + ComponentParameterConstants.FORMINJECTOR_INSERT_POSITION)
    062        private InsertPosition position;
    063
    064        /**
    065         * Name of a function on the client-side Tapestry.ElementEffect object that is invoked to make added content
    066         * visible. The default value is "highlight".
    067         */
    068        @Parameter(defaultPrefix = BindingConstants.LITERAL,
    069                value = BindingConstants.SYMBOL + ":" + ComponentParameterConstants.FORMINJECTOR_SHOW_FUNCTION)
    070        private String show;
    071
    072        /**
    073         * The element name to render, which is normally the element name used to represent the FormInjector component in
    074         * the template, or "div".
    075         */
    076        @Parameter(defaultPrefix = BindingConstants.LITERAL)
    077        private String element;
    078
    079        @Environmental
    080        private JavaScriptSupport javascriptSupport;
    081
    082        @Environmental
    083        private FormSupport formSupport;
    084
    085        @Environmental
    086        private ClientBehaviorSupport clientBehaviorSupport;
    087
    088        @SuppressWarnings("unchecked")
    089        @Environmental
    090        private TrackableComponentEventCallback eventCallback;
    091
    092        private String clientId;
    093
    094        @Inject
    095        private ComponentResources resources;
    096
    097        @Inject
    098        private AjaxResponseRenderer ajaxResponseRenderer;
    099
    100        private Element clientElement;
    101
    102        String defaultElement()
    103        {
    104            return resources.getElementName("div");
    105        }
    106
    107        void beginRender(MarkupWriter writer)
    108        {
    109            clientId = javascriptSupport.allocateClientId(resources);
    110
    111            clientElement = writer.element(element, "id", clientId);
    112
    113            resources.renderInformalParameters(writer);
    114
    115            // Now work on the JavaScript side of things.
    116
    117            Link link = resources.createEventLink(INJECT_EVENT, context);
    118
    119            link.addParameter(RequestConstants.FORM_CLIENTID_PARAMETER, formSupport.getClientId());
    120            link.addParameter(RequestConstants.FORM_COMPONENTID_PARAMETER, formSupport.getFormComponentId());
    121
    122            clientBehaviorSupport.addFormInjector(clientId, link, position, show);
    123        }
    124
    125        void afterRender(MarkupWriter writer)
    126        {
    127            writer.end();
    128
    129            // Add the class name to the rendered client element. This allows nested elements to locate
    130            // the containing FormInjector element.
    131
    132            clientElement.addClassName("t-forminjector");
    133        }
    134
    135        /**
    136         * Returns the unique client-side id of the rendered element.
    137         */
    138        public String getClientId()
    139        {
    140            return clientId;
    141        }
    142
    143        /**
    144         * Invoked via an Ajax request. Triggers an action event and captures the return value. The return value from the
    145         * event notification is what will ultimately render (typically, its a Block).
    146         */
    147        void onInject(EventContext context) throws IOException
    148        {
    149            ajaxResponseRenderer.addCallback(new JSONCallback()
    150            {
    151                public void run(JSONObject reply)
    152                {
    153                    clientId = javascriptSupport.allocateClientId(resources);
    154
    155                    reply.put("elementId", clientId);
    156                }
    157            });
    158
    159            resources.triggerContextEvent(EventConstants.ACTION, context, eventCallback);
    160        }
    161    }