001    /*
002     * Apache License
003     * Version 2.0, January 2004
004     * http://www.apache.org/licenses/
005     *
006     * Copyright 2008-2010 by chenillekit.org
007     *
008     * Licensed under the Apache License, Version 2.0 (the "License");
009     * you may not use this file except in compliance with the License.
010     * You may obtain a copy of the License at
011     *
012     * http://www.apache.org/licenses/LICENSE-2.0
013     */
014    
015    package org.chenillekit.tapestry.core.components;
016    
017    import java.util.List;
018    
019    import org.apache.tapestry5.BindingConstants;
020    import org.apache.tapestry5.ClientElement;
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.annotations.Environmental;
027    import org.apache.tapestry5.annotations.Import;
028    import org.apache.tapestry5.annotations.Mixin;
029    import org.apache.tapestry5.annotations.Parameter;
030    import org.apache.tapestry5.annotations.RequestParameter;
031    import org.apache.tapestry5.annotations.SupportsInformalParameters;
032    import org.apache.tapestry5.corelib.mixins.DiscardBody;
033    import org.apache.tapestry5.ioc.annotations.Inject;
034    import org.apache.tapestry5.json.JSONLiteral;
035    import org.apache.tapestry5.json.JSONObject;
036    import org.apache.tapestry5.services.javascript.JavaScriptSupport;
037    
038    /**
039     * a "just in place" checkbox component that does not have to be embedded in a form.
040     * sends an event after click named "clicked".
041     *
042     * @version $Id: InPlaceCheckbox.java 704 2010-10-16 10:17:41Z homburgs $
043     */
044    @SupportsInformalParameters
045    @Import(library = {"../Chenillekit.js", "InPlaceCheckbox.js"})
046    public class InPlaceCheckbox implements ClientElement
047    {
048            public static final String EVENT_NAME = "clicked";
049    
050            /**
051             * If true, then the field will render out with a disabled attribute (to turn off client-side
052             * behavior). Further, a disabled field ignores any value in the request when the form is
053             * submitted.
054             */
055            @Parameter(value = "false", defaultPrefix = BindingConstants.PROP)
056            private boolean disabled;
057    
058            /**
059             * The id used to generate a page-unique client-side identifier for the component. If a
060             * component renders multiple times, a suffix will be appended to the to id to ensure
061             * uniqueness. The unique value may be accessed via the
062             * {@link #getClientId() clientId property}.
063             */
064            @Parameter(value = "prop:componentResources.id", defaultPrefix = BindingConstants.LITERAL)
065            private String clientId;
066    
067            /**
068             * The value to read or update.
069             */
070            @Parameter(required = true, defaultPrefix = BindingConstants.PROP, allowNull = false)
071            private boolean value;
072    
073            /**
074             * the javascript callback function (optional).
075             * function has one parameter: the response text
076             */
077            @Parameter(required = false, defaultPrefix = BindingConstants.LITERAL)
078            private String onCompleteCallback;
079    
080            /**
081             * the javascript callback function (optional).
082             * function has one parameter: the response text
083             */
084            @Parameter(required = false, defaultPrefix = BindingConstants.PROP)
085            private List<?> context;
086    
087            @Mixin
088            private DiscardBody discardBody;
089    
090            /**
091             * For blocks, messages, create actionlink, trigger event.
092             */
093            @Inject
094            private ComponentResources resources;
095    
096            /**
097             * RenderSupport to get unique client side id.
098             */
099            @Environmental
100            private JavaScriptSupport javascriptSupport;
101    
102            private String assignedClientId;
103    
104            private Object[] contextArray;
105    
106            void setupRender()
107            {
108                    assignedClientId = javascriptSupport.allocateClientId(clientId);
109                    contextArray = context == null ? new Object[0] : context.toArray();
110            }
111    
112            /**
113             * Tapestry render phase method.
114             * Start a tag here, end it in afterRender
115             *
116             * @param writer the markup writer
117             */
118            void beginRender(MarkupWriter writer)
119            {
120                    writer.element("input",
121                                               "type", "checkbox",
122                                               "id", getClientId(),
123                                               "checked", value ? "checked" : null);
124    
125                    resources.renderInformalParameters(writer);
126            }
127    
128            /**
129             * Tapestry render phase method. End a tag here.
130             *
131             * @param writer the markup writer
132             */
133            void afterRender(MarkupWriter writer)
134            {
135                    writer.end(); // input
136    
137                    Link link = resources.createEventLink(EventConstants.ACTION, contextArray);
138    
139                    JSONObject spec = new JSONObject();
140                    spec.put("elementId", getClientId());
141                    spec.put("requestUrl", link.toURI());
142    
143                    if (onCompleteCallback != null)
144                          spec.put("onCompleteCallback", new JSONLiteral(onCompleteCallback));
145     
146                    javascriptSupport.addInitializerCall("ckinplacecheckbox", spec);
147            }
148    
149            JSONObject onAction(EventContext context, @RequestParameter("checked") boolean checked)
150            {
151                    Object[] eventContextArray = new Object[context.getCount()+1];
152                    for (int x = 0; x < context.getCount(); x++)
153                            eventContextArray[x] = context.get(String.class, x);
154    
155                    eventContextArray[context.getCount()] = checked;
156                    resources.triggerEvent(EVENT_NAME, eventContextArray, null);
157    
158                    return new JSONObject().put("value", checked);
159            }
160    
161            /**
162             * Returns a unique id for the element. This value will be unique for any given rendering of a page. This value is
163             * intended for use as the id attribute of the client-side element, and will be used with any DHTML/Ajax related
164             * JavaScript.
165             */
166            public String getClientId()
167            {
168                    return assignedClientId;
169            }
170    }