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.yui;
016
017 import org.apache.tapestry5.Asset;
018 import org.apache.tapestry5.Binding;
019 import org.apache.tapestry5.BindingConstants;
020 import org.apache.tapestry5.ComponentResources;
021 import org.apache.tapestry5.FieldTranslator;
022 import org.apache.tapestry5.FieldValidationSupport;
023 import org.apache.tapestry5.FieldValidator;
024 import org.apache.tapestry5.MarkupWriter;
025 import org.apache.tapestry5.NullFieldStrategy;
026 import org.apache.tapestry5.ValidationException;
027 import org.apache.tapestry5.ValidationTracker;
028 import org.apache.tapestry5.annotations.Environmental;
029 import org.apache.tapestry5.annotations.Import;
030 import org.apache.tapestry5.annotations.Parameter;
031 import org.apache.tapestry5.annotations.Path;
032 import org.apache.tapestry5.ioc.annotations.Inject;
033 import org.apache.tapestry5.services.ComponentDefaultProvider;
034 import org.apache.tapestry5.services.Request;
035 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
036 import org.chenillekit.tapestry.core.base.AbstractYuiField;
037
038 /**
039 * @version $Id: Slider.java 674 2010-07-29 12:47:25Z homburgs $
040 */
041 @Import(library = {"${yahoo.yui}/dragdrop/dragdrop${yahoo.yui.mode}.js",
042 "${yahoo.yui}/slider/slider${yahoo.yui.mode}.js",
043 "../../Chenillekit.js", "Slider.js"},
044 stylesheet = {"${yahoo.yui}/slider/assets/skins/sam/slider.css"})
045 public class Slider extends AbstractYuiField
046 {
047 /**
048 * The value to be read and updated. This is not necessarily a string, a translator may be provided to convert
049 * between client side and server side representations. If not bound, a default binding is made to a property of the
050 * container matching the component's id. If no such property exists, then you will see a runtime exception due to
051 * the unbound value parameter.
052 */
053 @Parameter(required = true, principal = true)
054 private Number value;
055
056 /**
057 * The user presentable label for the field. If not provided, a reasonable label is generated from the component's
058 * id, first by looking for a message key named "id-label" (substituting the component's actual id), then by
059 * converting the actual id to a presentable string (for example, "userId" to "User Id").
060 */
061 @Parameter(defaultPrefix = BindingConstants.LITERAL)
062 private String label;
063
064 @Parameter(required = false, defaultPrefix = BindingConstants.PROP, value = "false")
065 private boolean vertical;
066
067 @Parameter(required = false, defaultPrefix = BindingConstants.LITERAL)
068 private String changeCallback;
069
070 @Parameter(required = false, defaultPrefix = BindingConstants.PROP, value = "200")
071 private int length;
072
073 @Parameter(required = false, defaultPrefix = BindingConstants.PROP, value = "0")
074 private int ticks;
075
076 /**
077 * The object that will perform input validation (which occurs after translation). The validate binding prefix is
078 * generally used to provide this object in a declarative fashion.
079 */
080 @Parameter(defaultPrefix = BindingConstants.VALIDATE)
081 @SuppressWarnings("unchecked")
082 private FieldValidator<Object> validate;
083
084 @Inject
085 private Request request;
086
087 /**
088 * The object which will perform translation between server-side and client-side representations. If not specified,
089 * a value will usually be generated based on the type of the value parameter.
090 */
091 @Parameter(required = true, allowNull = false, defaultPrefix = BindingConstants.TRANSLATE)
092 private FieldTranslator<Object> translate;
093
094 /**
095 * Defines how nulls on the server side, or sent from the client side, are treated. The selected strategy may
096 * replace the nulls with some other value. The default strategy leaves nulls alone. Another built-in strategy,
097 * zero, replaces nulls with the value 0.
098 */
099 @Parameter(defaultPrefix = BindingConstants.NULLFIELDSTRATEGY, value = "default")
100 private NullFieldStrategy nulls;
101
102 @Inject
103 private FieldValidationSupport fieldValidationSupport;
104
105 @Inject
106 private ComponentResources resources;
107
108 /**
109 * RenderSupport to get unique client side id.
110 */
111 @Environmental
112 private JavaScriptSupport javascriptSupport;
113
114 @Environmental
115 private ValidationTracker tracker;
116
117 /**
118 * RenderSupport to get unique client side id.
119 */
120 @Inject
121 @Path("${yahoo.yui}/slider/assets/thumb-n.gif")
122 private Asset sliderThumb;
123
124 @Inject
125 private ComponentDefaultProvider defaultProvider;
126
127 /**
128 * Computes a default value for the "translate" parameter using {@link org.apache.tapestry5.services.ComponentDefaultProvider#defaultTranslator(String,
129 * org.apache.tapestry5.ComponentResources)}.
130 */
131 final Binding defaultTranslate()
132 {
133 return defaultProvider.defaultTranslatorBinding("value", resources);
134 }
135
136 /**
137 * Computes a default value for the "validate" parameter using {@link org.apache.tapestry5.services.FieldValidatorDefaultSource}.
138 */
139 final Binding defaultValidate()
140 {
141 return defaultProvider.defaultValidatorBinding("value", resources);
142 }
143
144 /**
145 * The default value is a property of the container whose name matches the component's id. May return null if the
146 * container does not have a matching property.
147 *
148 * @deprecated Likely to be removed in the future, use {@link org.apache.tapestry5.annotations.Parameter#autoconnect()}
149 * instead
150 */
151 final Binding defaultValue()
152 {
153 return createDefaultParameterBinding("value");
154 }
155
156 /**
157 * Tapestry render phase method.
158 * Start a tag here, end it in afterRender
159 *
160 * @param writer the markup writer
161 */
162 void beginRender(MarkupWriter writer)
163 {
164 writer.element("div", "id", getClientId() + "bg", "class", "yui-" + (vertical ? "v" : "h") + "-slider", "tabindex", "-1");
165 writer.element("div", "id", getClientId() + "thumb", "class", "yui-slider-thumb");
166 writer.element("img", "src", sliderThumb.toClientURL());
167 writer.end();
168 writer.end();
169 writer.end();
170
171
172 writer.element("input",
173 "type", "hidden",
174 "id", getClientId() + "Value",
175 "name", getControlName(),
176 "value", value);
177 writer.end();
178 }
179
180 /**
181 * Tapestry render phase method. End a tag here.
182 *
183 * @param writer the markup writer
184 */
185
186 void afterRender(MarkupWriter writer)
187 {
188 javascriptSupport.addScript("new Ck.YuiSlider('%s', '%s', %d, 0, %d, %d, '%s');",
189 getClientId(), (vertical ? "vert" : "horiz"), value, length, ticks, changeCallback);
190 }
191
192
193 /**
194 * Method implemented by subclasses to actually do the work of processing the submission of the form. The element's
195 * elementName property will already have been set. This method is only invoked if the field is <strong>not {@link
196 * #isDisabled() disabled}</strong>.
197 *
198 * @param elementName the name of the element (used to find the correct parameter in the request)
199 */
200 protected void processSubmission(String elementName)
201 {
202 String rawValue = request.getParameter(elementName);
203
204 tracker.recordInput(this, rawValue);
205
206 try
207 {
208 Object translated = fieldValidationSupport.parseClient(rawValue, resources, translate, nulls);
209
210 fieldValidationSupport.validate(translated, resources, validate);
211
212 value = (Number) translated;
213 }
214 catch (ValidationException ex)
215 {
216 tracker.recordError(this, ex.getMessage());
217 }
218 }
219 }