Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / docs / source / Slider.html
1 <html>\r
2 <head>\r
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    \r
4   <title>The source code</title>\r
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
7 </head>\r
8 <body  onload="prettyPrint();">\r
9     <pre class="prettyprint lang-js"><div id="cls-Ext.Slider"></div>/**\r
10  * @class Ext.Slider\r
11  * @extends Ext.BoxComponent\r
12  * Slider which supports vertical or horizontal orientation, keyboard adjustments,\r
13  * configurable snapping, axis clicking and animation. Can be added as an item to\r
14  * any container. Example usage:\r
15 <pre><code>\r
16 new Ext.Slider({\r
17     renderTo: Ext.getBody(),\r
18     width: 200,\r
19     value: 50,\r
20     increment: 10,\r
21     minValue: 0,\r
22     maxValue: 100\r
23 });\r
24 </code></pre>\r
25  */\r
26 Ext.Slider = Ext.extend(Ext.BoxComponent, {\r
27     <div id="cfg-Ext.Slider-value"></div>/**\r
28      * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.\r
29      */\r
30     <div id="cfg-Ext.Slider-vertical"></div>/**\r
31      * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.\r
32      */\r
33     vertical: false,\r
34     <div id="cfg-Ext.Slider-minValue"></div>/**\r
35      * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.\r
36      */\r
37     minValue: 0,\r
38     <div id="cfg-Ext.Slider-maxValue"></div>/**\r
39      * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.\r
40      */\r
41     maxValue: 100,\r
42     <div id="cfg-Ext.Slider-decimalPrecision."></div>/**\r
43      * @cfg {Number/Boolean} decimalPrecision.\r
44      * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>\r
45      * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>\r
46      */\r
47     decimalPrecision: 0,\r
48     <div id="cfg-Ext.Slider-keyIncrement"></div>/**\r
49      * @cfg {Number} keyIncrement How many units to change the Slider when adjusting with keyboard navigation. Defaults to 1. If the increment config is larger, it will be used instead.\r
50      */\r
51     keyIncrement: 1,\r
52     <div id="cfg-Ext.Slider-increment"></div>/**\r
53      * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.\r
54      */\r
55     increment: 0,\r
56     // private\r
57     clickRange: [5,15],\r
58     <div id="cfg-Ext.Slider-clickToChange"></div>/**\r
59      * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true\r
60      */\r
61     clickToChange : true,\r
62     <div id="cfg-Ext.Slider-animate"></div>/**\r
63      * @cfg {Boolean} animate Turn on or off animation. Defaults to true\r
64      */\r
65     animate: true,\r
66 \r
67     <div id="prop-Ext.Slider-dragging"></div>/**\r
68      * True while the thumb is in a drag operation\r
69      * @type boolean\r
70      */\r
71     dragging: false,\r
72 \r
73     // private override\r
74     initComponent : function(){\r
75         if(!Ext.isDefined(this.value)){\r
76             this.value = this.minValue;\r
77         }\r
78         Ext.Slider.superclass.initComponent.call(this);\r
79         this.keyIncrement = Math.max(this.increment, this.keyIncrement);\r
80         this.addEvents(\r
81             <div id="event-Ext.Slider-beforechange"></div>/**\r
82              * @event beforechange\r
83              * Fires before the slider value is changed. By returning false from an event handler,\r
84              * you can cancel the event and prevent the slider from changing.\r
85              * @param {Ext.Slider} slider The slider\r
86              * @param {Number} newValue The new value which the slider is being changed to.\r
87              * @param {Number} oldValue The old value which the slider was previously.\r
88              */\r
89             'beforechange',\r
90             <div id="event-Ext.Slider-change"></div>/**\r
91              * @event change\r
92              * Fires when the slider value is changed.\r
93              * @param {Ext.Slider} slider The slider\r
94              * @param {Number} newValue The new value which the slider has been changed to.\r
95              */\r
96             'change',\r
97             <div id="event-Ext.Slider-changecomplete"></div>/**\r
98              * @event changecomplete\r
99              * Fires when the slider value is changed by the user and any drag operations have completed.\r
100              * @param {Ext.Slider} slider The slider\r
101              * @param {Number} newValue The new value which the slider has been changed to.\r
102              */\r
103             'changecomplete',\r
104             <div id="event-Ext.Slider-dragstart"></div>/**\r
105              * @event dragstart\r
106              * Fires after a drag operation has started.\r
107              * @param {Ext.Slider} slider The slider\r
108              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
109              */\r
110             'dragstart',\r
111             <div id="event-Ext.Slider-drag"></div>/**\r
112              * @event drag\r
113              * Fires continuously during the drag operation while the mouse is moving.\r
114              * @param {Ext.Slider} slider The slider\r
115              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
116              */\r
117             'drag',\r
118             <div id="event-Ext.Slider-dragend"></div>/**\r
119              * @event dragend\r
120              * Fires after the drag operation has completed.\r
121              * @param {Ext.Slider} slider The slider\r
122              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
123              */\r
124             'dragend'\r
125         );\r
126 \r
127         if(this.vertical){\r
128             Ext.apply(this, Ext.Slider.Vertical);\r
129         }\r
130     },\r
131 \r
132     // private override\r
133     onRender : function(){\r
134         this.autoEl = {\r
135             cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),\r
136             cn:{cls:'x-slider-end',cn:{cls:'x-slider-inner',cn:[{cls:'x-slider-thumb'},{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]}}\r
137         };\r
138         Ext.Slider.superclass.onRender.apply(this, arguments);\r
139         this.endEl = this.el.first();\r
140         this.innerEl = this.endEl.first();\r
141         this.thumb = this.innerEl.first();\r
142         this.halfThumb = (this.vertical ? this.thumb.getHeight() : this.thumb.getWidth())/2;\r
143         this.focusEl = this.thumb.next();\r
144         this.initEvents();\r
145     },\r
146 \r
147     // private override\r
148     initEvents : function(){\r
149         this.thumb.addClassOnOver('x-slider-thumb-over');\r
150         this.mon(this.el, {\r
151             scope: this,\r
152             mousedown: this.onMouseDown,\r
153             keydown: this.onKeyDown\r
154         });\r
155 \r
156         this.focusEl.swallowEvent("click", true);\r
157 \r
158         this.tracker = new Ext.dd.DragTracker({\r
159             onBeforeStart: this.onBeforeDragStart.createDelegate(this),\r
160             onStart: this.onDragStart.createDelegate(this),\r
161             onDrag: this.onDrag.createDelegate(this),\r
162             onEnd: this.onDragEnd.createDelegate(this),\r
163             tolerance: 3,\r
164             autoStart: 300\r
165         });\r
166         this.tracker.initEl(this.thumb);\r
167     },\r
168 \r
169     // private override\r
170     onMouseDown : function(e){\r
171         if(this.disabled){\r
172             return;\r
173         }\r
174         if(this.clickToChange && e.target != this.thumb.dom){\r
175             var local = this.innerEl.translatePoints(e.getXY());\r
176             this.onClickChange(local);\r
177         }\r
178         this.focus();\r
179     },\r
180 \r
181     // private\r
182     onClickChange : function(local){\r
183         if(local.top > this.clickRange[0] && local.top < this.clickRange[1]){\r
184             this.setValue(Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);\r
185         }\r
186     },\r
187 \r
188     // private\r
189     onKeyDown : function(e){\r
190         if(this.disabled){e.preventDefault();return;}\r
191         var k = e.getKey();\r
192         switch(k){\r
193             case e.UP:\r
194             case e.RIGHT:\r
195                 e.stopEvent();\r
196                 if(e.ctrlKey){\r
197                     this.setValue(this.maxValue, undefined, true);\r
198                 }else{\r
199                     this.setValue(this.value+this.keyIncrement, undefined, true);\r
200                 }\r
201             break;\r
202             case e.DOWN:\r
203             case e.LEFT:\r
204                 e.stopEvent();\r
205                 if(e.ctrlKey){\r
206                     this.setValue(this.minValue, undefined, true);\r
207                 }else{\r
208                     this.setValue(this.value-this.keyIncrement, undefined, true);\r
209                 }\r
210             break;\r
211             default:\r
212                 e.preventDefault();\r
213         }\r
214     },\r
215 \r
216     // private\r
217     doSnap : function(value){\r
218         if(!(this.increment && value)){\r
219             return value;\r
220         }\r
221         var newValue = value,\r
222             inc = this.increment,\r
223             m = value % inc;\r
224         if(m != 0){\r
225             newValue -= m;\r
226             if(m * 2 > inc){\r
227                 newValue += inc;\r
228             }else if(m * 2 < -inc){\r
229                 newValue -= inc;\r
230             }\r
231         }\r
232         return newValue.constrain(this.minValue,  this.maxValue);\r
233     },\r
234 \r
235     // private\r
236     afterRender : function(){\r
237         Ext.Slider.superclass.afterRender.apply(this, arguments);\r
238         if(this.value !== undefined){\r
239             var v = this.normalizeValue(this.value);\r
240             if(v !== this.value){\r
241                 delete this.value;\r
242                 this.setValue(v, false);\r
243             }else{\r
244                 this.moveThumb(this.translateValue(v), false);\r
245             }\r
246         }\r
247     },\r
248 \r
249     // private\r
250     getRatio : function(){\r
251         var w = this.innerEl.getWidth(),\r
252             v = this.maxValue - this.minValue;\r
253         return v == 0 ? w : (w/v);\r
254     },\r
255 \r
256     // private\r
257     normalizeValue : function(v){\r
258         v = this.doSnap(v);\r
259         v = Ext.util.Format.round(v, this.decimalPrecision);\r
260         v = v.constrain(this.minValue, this.maxValue);\r
261         return v;\r
262     },\r
263     \r
264     <div id="method-Ext.Slider-setMinValue"></div>/**\r
265      * Sets the minimum value for the slider instance. If the current value is less than the \r
266      * minimum value, the current value will be changed.\r
267      * @param {Number} val The new minimum value\r
268      */\r
269     setMinValue : function(val){\r
270         this.minValue = val;\r
271         this.syncThumb();\r
272         if(this.value < val){\r
273             this.setValue(val);\r
274         }\r
275     },\r
276     \r
277     <div id="method-Ext.Slider-setMaxValue"></div>/**\r
278      * Sets the maximum value for the slider instance. If the current value is more than the \r
279      * maximum value, the current value will be changed.\r
280      * @param {Number} val The new maximum value\r
281      */\r
282     setMaxValue : function(val){\r
283         this.maxValue = val;\r
284         this.syncThumb();\r
285         if(this.value > val){\r
286             this.setValue(val);\r
287         }\r
288     },\r
289 \r
290     <div id="method-Ext.Slider-setValue"></div>/**\r
291      * Programmatically sets the value of the Slider. Ensures that the value is constrained within\r
292      * the minValue and maxValue.\r
293      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)\r
294      * @param {Boolean} animate Turn on or off animation, defaults to true\r
295      */\r
296     setValue : function(v, animate, changeComplete){\r
297         v = this.normalizeValue(v);\r
298         if(v !== this.value && this.fireEvent('beforechange', this, v, this.value) !== false){\r
299             this.value = v;\r
300             this.moveThumb(this.translateValue(v), animate !== false);\r
301             this.fireEvent('change', this, v);\r
302             if(changeComplete){\r
303                 this.fireEvent('changecomplete', this, v);\r
304             }\r
305         }\r
306     },\r
307 \r
308     // private\r
309     translateValue : function(v){\r
310         var ratio = this.getRatio();\r
311         return (v * ratio) - (this.minValue * ratio) - this.halfThumb;\r
312     },\r
313 \r
314     reverseValue : function(pos){\r
315         var ratio = this.getRatio();\r
316         return (pos + (this.minValue * ratio)) / ratio;\r
317     },\r
318 \r
319     // private\r
320     moveThumb: function(v, animate){\r
321         if(!animate || this.animate === false){\r
322             this.thumb.setLeft(v);\r
323         }else{\r
324             this.thumb.shift({left: v, stopFx: true, duration:.35});\r
325         }\r
326     },\r
327 \r
328     // private\r
329     focus : function(){\r
330         this.focusEl.focus(10);\r
331     },\r
332 \r
333     // private\r
334     onBeforeDragStart : function(e){\r
335         return !this.disabled;\r
336     },\r
337 \r
338     // private\r
339     onDragStart: function(e){\r
340         this.thumb.addClass('x-slider-thumb-drag');\r
341         this.dragging = true;\r
342         this.dragStartValue = this.value;\r
343         this.fireEvent('dragstart', this, e);\r
344     },\r
345 \r
346     // private\r
347     onDrag: function(e){\r
348         var pos = this.innerEl.translatePoints(this.tracker.getXY());\r
349         this.setValue(Ext.util.Format.round(this.reverseValue(pos.left), this.decimalPrecision), false);\r
350         this.fireEvent('drag', this, e);\r
351     },\r
352 \r
353     // private\r
354     onDragEnd: function(e){\r
355         this.thumb.removeClass('x-slider-thumb-drag');\r
356         this.dragging = false;\r
357         this.fireEvent('dragend', this, e);\r
358         if(this.dragStartValue != this.value){\r
359             this.fireEvent('changecomplete', this, this.value);\r
360         }\r
361     },\r
362 \r
363     // private\r
364     onResize : function(w, h){\r
365         this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));\r
366         this.syncThumb();\r
367         Ext.Slider.superclass.onResize.apply(this, arguments);\r
368     },\r
369 \r
370     //private\r
371     onDisable: function(){\r
372         Ext.Slider.superclass.onDisable.call(this);\r
373         this.thumb.addClass(this.disabledClass);\r
374         if(Ext.isIE){\r
375             //IE breaks when using overflow visible and opacity other than 1.\r
376             //Create a place holder for the thumb and display it.\r
377             var xy = this.thumb.getXY();\r
378             this.thumb.hide();\r
379             this.innerEl.addClass(this.disabledClass).dom.disabled = true;\r
380             if (!this.thumbHolder){\r
381                 this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});\r
382             }\r
383             this.thumbHolder.show().setXY(xy);\r
384         }\r
385     },\r
386 \r
387     //private\r
388     onEnable: function(){\r
389         Ext.Slider.superclass.onEnable.call(this);\r
390         this.thumb.removeClass(this.disabledClass);\r
391         if(Ext.isIE){\r
392             this.innerEl.removeClass(this.disabledClass).dom.disabled = false;\r
393             if(this.thumbHolder){\r
394                 this.thumbHolder.hide();\r
395             }\r
396             this.thumb.show();\r
397             this.syncThumb();\r
398         }\r
399     },\r
400 \r
401     <div id="method-Ext.Slider-syncThumb"></div>/**\r
402      * Synchronizes the thumb position to the proper proportion of the total component width based\r
403      * on the current slider {@link #value}.  This will be called automatically when the Slider\r
404      * is resized by a layout, but if it is rendered auto width, this method can be called from\r
405      * another resize handler to sync the Slider if necessary.\r
406      */\r
407     syncThumb : function(){\r
408         if(this.rendered){\r
409             this.moveThumb(this.translateValue(this.value));\r
410         }\r
411     },\r
412 \r
413     <div id="method-Ext.Slider-getValue"></div>/**\r
414      * Returns the current value of the slider\r
415      * @return {Number} The current value of the slider\r
416      */\r
417     getValue : function(){\r
418         return this.value;\r
419     },\r
420 \r
421     // private\r
422     beforeDestroy : function(){\r
423         Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder');\r
424         Ext.Slider.superclass.beforeDestroy.call(this);\r
425     }\r
426 });\r
427 Ext.reg('slider', Ext.Slider);\r
428 \r
429 // private class to support vertical sliders\r
430 Ext.Slider.Vertical = {\r
431     onResize : function(w, h){\r
432         this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));\r
433         this.syncThumb();\r
434     },\r
435 \r
436     getRatio : function(){\r
437         var h = this.innerEl.getHeight(),\r
438             v = this.maxValue - this.minValue;\r
439         return h/v;\r
440     },\r
441 \r
442     moveThumb: function(v, animate){\r
443         if(!animate || this.animate === false){\r
444             this.thumb.setBottom(v);\r
445         }else{\r
446             this.thumb.shift({bottom: v, stopFx: true, duration:.35});\r
447         }\r
448     },\r
449 \r
450     onDrag: function(e){\r
451         var pos = this.innerEl.translatePoints(this.tracker.getXY()),\r
452             bottom = this.innerEl.getHeight()-pos.top;\r
453         this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), false);\r
454         this.fireEvent('drag', this, e);\r
455     },\r
456 \r
457     onClickChange : function(local){\r
458         if(local.left > this.clickRange[0] && local.left < this.clickRange[1]){\r
459             var bottom = this.innerEl.getHeight() - local.top;\r
460             this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), undefined, true);\r
461         }\r
462     }\r
463 };</pre>    \r
464 </body>\r
465 </html>