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 org.apache.tapestry5.Asset;
018 import org.apache.tapestry5.ComponentResources;
019 import org.apache.tapestry5.MarkupWriter;
020 import org.apache.tapestry5.ValueEncoder;
021 import org.apache.tapestry5.annotations.Component;
022 import org.apache.tapestry5.annotations.Environmental;
023 import org.apache.tapestry5.annotations.Import;
024 import org.apache.tapestry5.annotations.Parameter;
025 import org.apache.tapestry5.annotations.Path;
026 import org.apache.tapestry5.annotations.Property;
027 import org.apache.tapestry5.corelib.base.AbstractField;
028 import org.apache.tapestry5.corelib.components.Label;
029 import org.apache.tapestry5.corelib.components.Loop;
030 import org.apache.tapestry5.corelib.components.Radio;
031 import org.apache.tapestry5.corelib.components.RadioGroup;
032 import org.apache.tapestry5.ioc.annotations.Inject;
033 import org.apache.tapestry5.ioc.services.PropertyAccess;
034 import org.apache.tapestry5.json.JSONObject;
035 import org.apache.tapestry5.services.Environment;
036 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
037 import org.chenillekit.tapestry.core.internal.GenericValueEncoder;
038
039 import java.util.List;
040
041
042 /**
043 * This component provides the ability to associate a RadioGroup and its
044 * subordinate Radio fields with a set of values displayed as a rating scale.
045 * This is typified by the "star field" where grayed stars represent the choices
046 * and highlighted stars represent the chosen value and all values up to the
047 * chosen value from left to right.
048 * <p/>
049 * This is in fact that default visual appearance. However, the images can be
050 * overridden via parameters and the entire component can, of course, be styled
051 * via CSS.
052 * <p/>
053 * As an added benefit, since the underlying representation is simply a
054 * RadioGroup with Radio fields it should degrade well when JS and/or CSS is
055 * disabled. This should keep the component rather accessible.
056 * <p/>
057 * By default the first value display image will be hidden as this typically
058 * will indicate no value.
059 *
060 * @version $Id: RatingField.java 674 2010-07-29 12:47:25Z homburgs $
061 */
062 @Import(library = {"../Chenillekit.js", "Rating.js"}, stylesheet = {"Rating.css"})
063 public class RatingField<T> extends AbstractField
064 {
065 /**
066 * The value to read or update.
067 */
068 @Parameter(required = true)
069 @Property
070 private T value;
071
072 /**
073 * the rateable value list.
074 */
075 @Parameter(required = true)
076 @Property
077 private List<T> source;
078
079 /**
080 * Encoder used to translate between server-side objects
081 * and client-side strings.
082 */
083 @Parameter
084 private ValueEncoder encoder;
085
086 /**
087 * the optional Selected-Image
088 */
089 @Parameter(required = false)
090 private Asset selectedImage;
091
092 /**
093 * the optional UnSelected-Image
094 */
095 @Parameter(required = false)
096 private Asset unselectedImage;
097
098 @Inject
099 @Path("rating_default_selected.gif")
100 private Asset defaultSelectedImage;
101
102 @Inject
103 @Path("rating_default_unselected.gif")
104 private Asset defaultUnselectedImage;
105
106 @Inject
107 private Environment environment;
108
109 @Environmental
110 private JavaScriptSupport javascriptSupport;
111
112 @Inject
113 private ComponentResources componentResources;
114
115 @Inject
116 private PropertyAccess propertyAccess;
117
118 @Component(parameters = {"value=prop:value", "encoder=encoder"})
119 private RadioGroup radioGroup;
120
121 @Component(parameters = {"source=prop:source", "value=loopValue"})
122 private Loop loop;
123
124 @Property
125 private T loopValue;
126
127 @Component(parameters = {"value=loopValue", "label=prop:radioLabel"})
128 private Radio radio;
129
130 @Component(parameters = {"for=radio"})
131 private Label label;
132
133 /**
134 * Returns the image representing an unselected value.
135 *
136 * @return
137 */
138 public Asset getUnselectedImage()
139 {
140 return (unselectedImage == null) ? defaultUnselectedImage : unselectedImage;
141 }
142
143 /**
144 * Returns the image representing a selected value.
145 *
146 * @return
147 */
148 public Asset getSelectedImage()
149 {
150 return selectedImage == null ? defaultSelectedImage : selectedImage;
151 }
152
153 /**
154 * Returns an appropriate ValueEncoder implementation based on the value
155 * type.
156 *
157 * @return
158 */
159 @SuppressWarnings("unchecked")
160 public ValueEncoder getEncoder()
161 {
162 if (encoder == null)
163 encoder = new GenericValueEncoder(source);
164
165 return encoder;
166 }
167
168 /**
169 * Returns a reasonable label for the radio value. If the value is primitive
170 * it will be returned as is. Otherwise the toString() method will be called
171 * on the value object.
172 *
173 * @return
174 */
175 public String getRadioLabel()
176 {
177 return loopValue.toString();
178 }
179
180 /**
181 * Method implemented by subclasses to actually do the work of processing the submission of the form. The element's
182 * elementName property will already have been set. This method is only invoked if the field is <strong>not {@link
183 * #isDisabled() disabled}</strong>.
184 *
185 * @param elementName the name of the element (used to find the correct parameter in the request)
186 */
187 protected void processSubmission(String elementName)
188 {
189 }
190
191 public void afterRender(MarkupWriter writer)
192 {
193 JSONObject options = new JSONObject();
194
195 options.put("disabled", isDisabled());
196
197 //
198 // Let subclasses do more.
199 //
200 configure(options);
201
202 javascriptSupport.addScript("new Ck.RatingField('%s', '%s', '%s', %s);",
203 getClientId(),
204 getSelectedImage().toClientURL(),
205 getUnselectedImage().toClientURL(),
206 options);
207 }
208
209 /**
210 * Invoked to allow subclasses to further configure the parameters passed to this component's javascript
211 * options. Subclasses may override this method to configure additional features of the Window.
212 * <p/>
213 * This implementation does nothing.
214 *
215 * @param options windows option object
216 */
217 protected void configure(JSONObject options)
218 {
219 }
220 }