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    }