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.io.UnsupportedEncodingException;
018    import java.util.List;
019    
020    import org.apache.tapestry5.BindingConstants;
021    import org.apache.tapestry5.ClientElement;
022    import org.apache.tapestry5.ComponentResources;
023    import org.apache.tapestry5.EventConstants;
024    import org.apache.tapestry5.Link;
025    import org.apache.tapestry5.MarkupWriter;
026    import org.apache.tapestry5.StreamResponse;
027    import org.apache.tapestry5.annotations.Environmental;
028    import org.apache.tapestry5.annotations.Import;
029    import org.apache.tapestry5.annotations.Parameter;
030    import org.apache.tapestry5.annotations.SupportsInformalParameters;
031    import org.apache.tapestry5.ioc.Messages;
032    import org.apache.tapestry5.ioc.annotations.Inject;
033    import org.apache.tapestry5.json.JSONObject;
034    import org.apache.tapestry5.services.Request;
035    import org.apache.tapestry5.services.javascript.JavaScriptSupport;
036    import org.apache.tapestry5.util.TextStreamResponse;
037    
038    
039    /**
040     * a "just in place" edit component that dont must emmbedded in a form.
041     *
042     * @version $Id: InPlaceEditor.java 704 2010-10-16 10:17:41Z homburgs $
043     */
044    @SupportsInformalParameters
045    @Import(library = {"${tapestry.scriptaculous}/controls.js", "InPlaceEditor.js"})
046    public class InPlaceEditor implements ClientElement
047    {
048            public final static String SAVE_EVENT = "save";
049    
050            /**
051             * The id used to generate a page-unique client-side identifier for the component. If a
052             * component renders multiple times, a suffix will be appended to the to id to ensure
053             * uniqueness. The uniqued value may be accessed via the
054             * {@link #getClientId() clientId property}.
055             */
056            @Parameter(value = "prop:componentResources.id", defaultPrefix = BindingConstants.LITERAL)
057            private String clientId;
058    
059            /**
060             * The value to be read and updated. This is not necessarily a string, a translator may be provided to convert
061             * between client side and server side representations. If not bound, a default binding is made to a property of the
062             * container matching the component's id. If no such property exists, then you will see a runtime exception due to
063             * the unbound value parameter.
064             */
065            @Parameter(required = true, principal = true)
066            private String value;
067    
068            /**
069             * Size of the input text tag.
070             */
071            @Parameter(value = "20", required = false, defaultPrefix = BindingConstants.PROP)
072            private int size;
073    
074            /**
075             * The context for the link (optional parameter). This list of values will be converted into strings and included in
076             * the URI.
077             */
078            @Parameter(required = false)
079            private List<?> context;
080    
081            @Inject
082            private ComponentResources resources;
083    
084            @Inject
085            private Messages messages;
086    
087            @Environmental
088            private JavaScriptSupport javascriptSupport;
089    
090            @Inject
091            private Request request;
092    
093            private String assignedClientId;
094    
095            private Object[] contextArray;
096    
097            void setupRender()
098            {
099                    assignedClientId = javascriptSupport.allocateClientId(clientId);
100                    contextArray = context == null ? new Object[0] : context.toArray();
101            }
102    
103            void beginRender(MarkupWriter writer)
104            {
105                    writer.element("span", "id", getClientId());
106    
107                    if (value != null && value.length() > 0)
108                            writer.write(value);
109                    else
110                            writer.writeRaw(messages.get("empty"));
111            }
112    
113            void afterRender(MarkupWriter writer)
114            {
115                    Link link = resources.createEventLink(EventConstants.ACTION, contextArray);
116                    JSONObject spec = new JSONObject();
117    
118                    writer.end();
119    
120                    spec.put("clientId", getClientId());
121                    spec.put("href", link.toURI());
122                    JSONObject opts = new JSONObject();
123                    opts.put("cancelControl", "button");
124                    opts.put("cancelText", messages.get("cancelbutton"));
125                    opts.put("clickToEditText", messages.get("title"));
126                    opts.put("savingText", messages.get("saving"));
127                    opts.put("okText", messages.get("savebutton"));
128                    opts.put("okText", messages.get("savebutton"));
129                    opts.put("htmlResponse", Boolean.TRUE);
130                    opts.put("size", size);
131                    opts.put("stripLoadedTextTags", Boolean.TRUE);
132                    spec.put("options", opts);
133    
134                    javascriptSupport.addInitializerCall("ckinplaceeditor", spec);
135            }
136    
137            StreamResponse onAction(String value) throws UnsupportedEncodingException
138            {
139                    String valueText = request.getParameter("value");
140    
141                    resources.triggerEvent(SAVE_EVENT, new Object[]{value, valueText}, null);
142    
143                    if (valueText == null || valueText.length() == 0)
144                            valueText = messages.get("empty");
145    
146                    return new TextStreamResponse("text/html", new String(valueText.getBytes("UTF8")));
147            }
148    
149            /**
150             * Returns a unique id for the element. This value will be unique for any given rendering of a page. This value is
151             * intended for use as the id attribute of the client-side element, and will be used with any DHTML/Ajax related
152             * JavaScript.
153             */
154            public String getClientId()
155            {
156                    return assignedClientId;
157            }
158    }