Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / docs / source / Slider.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.1
11  * Copyright(c) 2006-2010 Sencha Inc.
12  * licensing@sencha.com
13  * http://www.sencha.com/license
14  */
15 Ext.ns('Ext.slider');
16
17 <div id="cls-Ext.slider.Thumb"></div>/**
18  * @class Ext.slider.Thumb
19  * @extends Object
20  * Represents a single thumb element on a Slider. This would not usually be created manually and would instead
21  * be created internally by an {@link Ext.slider.MultiSlider Ext.Slider}.
22  */
23 Ext.slider.Thumb = Ext.extend(Object, {
24     
25     <div id="prop-Ext.slider.Thumb-dragging"></div>/**
26      * True while the thumb is in a drag operation
27      * @type Boolean
28      */
29     dragging: false,
30
31     <div id="cfg-Ext.slider.Thumb-slider"></div>/**
32      * @constructor
33      * @cfg {Ext.slider.MultiSlider} slider The Slider to render to (required)
34      */
35     constructor: function(config) {
36         <div id="prop-Ext.slider.Thumb-slider"></div>/**
37          * @property slider
38          * @type Ext.slider.MultiSlider
39          * The slider this thumb is contained within
40          */
41         Ext.apply(this, config || {}, {
42             cls: 'x-slider-thumb',
43
44             <div id="cfg-Ext.slider.Thumb-constrain"></div>/**
45              * @cfg {Boolean} constrain True to constrain the thumb so that it cannot overlap its siblings
46              */
47             constrain: false
48         });
49
50         Ext.slider.Thumb.superclass.constructor.call(this, config);
51
52         if (this.slider.vertical) {
53             Ext.apply(this, Ext.slider.Thumb.Vertical);
54         }
55     },
56
57     <div id="method-Ext.slider.Thumb-render"></div>/**
58      * Renders the thumb into a slider
59      */
60     render: function() {
61         this.el = this.slider.innerEl.insertFirst({cls: this.cls});
62
63         this.initEvents();
64     },
65
66     <div id="method-Ext.slider.Thumb-enable"></div>/**
67      * Enables the thumb if it is currently disabled
68      */
69     enable: function() {
70         this.disabled = false;
71         this.el.removeClass(this.slider.disabledClass);
72     },
73
74     <div id="method-Ext.slider.Thumb-disable"></div>/**
75      * Disables the thumb if it is currently enabled
76      */
77     disable: function() {
78         this.disabled = true;
79         this.el.addClass(this.slider.disabledClass);
80     },
81
82     <div id="method-Ext.slider.Thumb-initEvents"></div>/**
83      * Sets up an Ext.dd.DragTracker for this thumb
84      */
85     initEvents: function() {
86         var el = this.el;
87
88         el.addClassOnOver('x-slider-thumb-over');
89
90         this.tracker = new Ext.dd.DragTracker({
91             onBeforeStart: this.onBeforeDragStart.createDelegate(this),
92             onStart      : this.onDragStart.createDelegate(this),
93             onDrag       : this.onDrag.createDelegate(this),
94             onEnd        : this.onDragEnd.createDelegate(this),
95             tolerance    : 3,
96             autoStart    : 300
97         });
98
99         this.tracker.initEl(el);
100     },
101
102     /**
103      * @private
104      * This is tied into the internal Ext.dd.DragTracker. If the slider is currently disabled,
105      * this returns false to disable the DragTracker too.
106      * @return {Boolean} False if the slider is currently disabled
107      */
108     onBeforeDragStart : function(e) {
109         if (this.disabled) {
110             return false;
111         } else {
112             this.slider.promoteThumb(this);
113             return true;
114         }
115     },
116
117     /**
118      * @private
119      * This is tied into the internal Ext.dd.DragTracker's onStart template method. Adds the drag CSS class
120      * to the thumb and fires the 'dragstart' event
121      */
122     onDragStart: function(e){
123         this.el.addClass('x-slider-thumb-drag');
124         this.dragging = true;
125         this.dragStartValue = this.value;
126
127         this.slider.fireEvent('dragstart', this.slider, e, this);
128     },
129
130     /**
131      * @private
132      * This is tied into the internal Ext.dd.DragTracker's onDrag template method. This is called every time
133      * the DragTracker detects a drag movement. It updates the Slider's value using the position of the drag
134      */
135     onDrag: function(e) {
136         var slider   = this.slider,
137             index    = this.index,
138             newValue = this.getNewValue();
139
140         if (this.constrain) {
141             var above = slider.thumbs[index + 1],
142                 below = slider.thumbs[index - 1];
143
144             if (below != undefined && newValue <= below.value) newValue = below.value;
145             if (above != undefined && newValue >= above.value) newValue = above.value;
146         }
147
148         slider.setValue(index, newValue, false);
149         slider.fireEvent('drag', slider, e, this);
150     },
151
152     getNewValue: function() {
153         var slider   = this.slider,
154             pos      = slider.innerEl.translatePoints(this.tracker.getXY());
155
156         return Ext.util.Format.round(slider.reverseValue(pos.left), slider.decimalPrecision);
157     },
158
159     /**
160      * @private
161      * This is tied to the internal Ext.dd.DragTracker's onEnd template method. Removes the drag CSS class and
162      * fires the 'changecomplete' event with the new value
163      */
164     onDragEnd: function(e) {
165         var slider = this.slider,
166             value  = this.value;
167
168         this.el.removeClass('x-slider-thumb-drag');
169
170         this.dragging = false;
171         slider.fireEvent('dragend', slider, e);
172
173         if (this.dragStartValue != value) {
174             slider.fireEvent('changecomplete', slider, value, this);
175         }
176     },
177     
178     /**
179      * @private
180      * Destroys the thumb
181      */
182     destroy: function(){
183         Ext.destroyMembers(this, 'tracker', 'el');
184     }
185 });
186
187 <div id="cls-Ext.slider.MultiSlider"></div>/**
188  * @class Ext.slider.MultiSlider
189  * @extends Ext.BoxComponent
190  * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking and animation. Can be added as an item to any container. Example usage:
191 <pre>
192 new Ext.Slider({
193     renderTo: Ext.getBody(),
194     width: 200,
195     value: 50,
196     increment: 10,
197     minValue: 0,
198     maxValue: 100
199 });
200 </pre>
201  * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one:
202 <pre>
203 new Ext.Slider({
204     renderTo: Ext.getBody(),
205     width: 200,
206     values: [25, 50, 75],
207     minValue: 0,
208     maxValue: 100,
209
210     //this defaults to true, setting to false allows the thumbs to pass each other
211     {@link #constrainThumbs}: false
212 });
213 </pre>
214  */
215 Ext.slider.MultiSlider = Ext.extend(Ext.BoxComponent, {
216     <div id="cfg-Ext.slider.MultiSlider-value"></div>/**
217      * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.
218      */
219     <div id="cfg-Ext.slider.MultiSlider-vertical"></div>/**
220      * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.
221      */
222     vertical: false,
223     <div id="cfg-Ext.slider.MultiSlider-minValue"></div>/**
224      * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.
225      */
226     minValue: 0,
227     <div id="cfg-Ext.slider.MultiSlider-maxValue"></div>/**
228      * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.
229      */
230     maxValue: 100,
231     <div id="cfg-Ext.slider.MultiSlider-decimalPrecision."></div>/**
232      * @cfg {Number/Boolean} decimalPrecision.
233      * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>
234      * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>
235      */
236     decimalPrecision: 0,
237     <div id="cfg-Ext.slider.MultiSlider-keyIncrement"></div>/**
238      * @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.
239      */
240     keyIncrement: 1,
241     <div id="cfg-Ext.slider.MultiSlider-increment"></div>/**
242      * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
243      */
244     increment: 0,
245
246     /**
247      * @private
248      * @property clickRange
249      * @type Array
250      * Determines whether or not a click to the slider component is considered to be a user request to change the value. Specified as an array of [top, bottom],
251      * the click event's 'top' property is compared to these numbers and the click only considered a change request if it falls within them. e.g. if the 'top'
252      * value of the click event is 4 or 16, the click is not considered a change request as it falls outside of the [5, 15] range
253      */
254     clickRange: [5,15],
255
256     <div id="cfg-Ext.slider.MultiSlider-clickToChange"></div>/**
257      * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true
258      */
259     clickToChange : true,
260     <div id="cfg-Ext.slider.MultiSlider-animate"></div>/**
261      * @cfg {Boolean} animate Turn on or off animation. Defaults to true
262      */
263     animate: true,
264     <div id="cfg-Ext.slider.MultiSlider-constrainThumbs"></div>/**
265      * @cfg {Boolean} constrainThumbs True to disallow thumbs from overlapping one another. Defaults to true
266      */
267     constrainThumbs: true,
268
269     /**
270      * @private
271      * @property topThumbZIndex
272      * @type Number
273      * The number used internally to set the z index of the top thumb (see promoteThumb for details)
274      */
275     topThumbZIndex: 10000,
276
277     // private override
278     initComponent : function(){
279         if(!Ext.isDefined(this.value)){
280             this.value = this.minValue;
281         }
282
283         <div id="prop-Ext.slider.MultiSlider-thumbs"></div>/**
284          * @property thumbs
285          * @type Array
286          * Array containing references to each thumb
287          */
288         this.thumbs = [];
289
290         Ext.slider.MultiSlider.superclass.initComponent.call(this);
291
292         this.keyIncrement = Math.max(this.increment, this.keyIncrement);
293         this.addEvents(
294             <div id="event-Ext.slider.MultiSlider-beforechange"></div>/**
295              * @event beforechange
296              * Fires before the slider value is changed. By returning false from an event handler,
297              * you can cancel the event and prevent the slider from changing.
298              * @param {Ext.slider.MultiSlider} slider The slider
299              * @param {Number} newValue The new value which the slider is being changed to.
300              * @param {Number} oldValue The old value which the slider was previously.
301              */
302             'beforechange',
303
304             <div id="event-Ext.slider.MultiSlider-change"></div>/**
305              * @event change
306              * Fires when the slider value is changed.
307              * @param {Ext.slider.MultiSlider} slider The slider
308              * @param {Number} newValue The new value which the slider has been changed to.
309              * @param {Ext.slider.Thumb} thumb The thumb that was changed
310              */
311             'change',
312
313             <div id="event-Ext.slider.MultiSlider-changecomplete"></div>/**
314              * @event changecomplete
315              * Fires when the slider value is changed by the user and any drag operations have completed.
316              * @param {Ext.slider.MultiSlider} slider The slider
317              * @param {Number} newValue The new value which the slider has been changed to.
318              * @param {Ext.slider.Thumb} thumb The thumb that was changed
319              */
320             'changecomplete',
321
322             <div id="event-Ext.slider.MultiSlider-dragstart"></div>/**
323              * @event dragstart
324              * Fires after a drag operation has started.
325              * @param {Ext.slider.MultiSlider} slider The slider
326              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
327              */
328             'dragstart',
329
330             <div id="event-Ext.slider.MultiSlider-drag"></div>/**
331              * @event drag
332              * Fires continuously during the drag operation while the mouse is moving.
333              * @param {Ext.slider.MultiSlider} slider The slider
334              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
335              */
336             'drag',
337
338             <div id="event-Ext.slider.MultiSlider-dragend"></div>/**
339              * @event dragend
340              * Fires after the drag operation has completed.
341              * @param {Ext.slider.MultiSlider} slider The slider
342              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
343              */
344             'dragend'
345         );
346
347         <div id="prop-Ext.slider.MultiSlider-values"></div>/**
348          * @property values
349          * @type Array
350          * Array of values to initalize the thumbs with
351          */
352         if (this.values == undefined || Ext.isEmpty(this.values)) this.values = [0];
353
354         var values = this.values;
355
356         for (var i=0; i < values.length; i++) {
357             this.addThumb(values[i]);
358         }
359
360         if(this.vertical){
361             Ext.apply(this, Ext.slider.Vertical);
362         }
363     },
364
365     <div id="method-Ext.slider.MultiSlider-addThumb"></div>/**
366      * Creates a new thumb and adds it to the slider
367      * @param {Number} value The initial value to set on the thumb. Defaults to 0
368      */
369     addThumb: function(value) {
370         var thumb = new Ext.slider.Thumb({
371             value    : value,
372             slider   : this,
373             index    : this.thumbs.length,
374             constrain: this.constrainThumbs
375         });
376         this.thumbs.push(thumb);
377
378         //render the thumb now if needed
379         if (this.rendered) thumb.render();
380     },
381
382     /**
383      * @private
384      * Moves the given thumb above all other by increasing its z-index. This is called when as drag
385      * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is
386      * required when the thumbs are stacked on top of each other at one of the ends of the slider's
387      * range, which can result in the user not being able to move any of them.
388      * @param {Ext.slider.Thumb} topThumb The thumb to move to the top
389      */
390     promoteThumb: function(topThumb) {
391         var thumbs = this.thumbs,
392             zIndex, thumb;
393
394         for (var i = 0, j = thumbs.length; i < j; i++) {
395             thumb = thumbs[i];
396
397             if (thumb == topThumb) {
398                 zIndex = this.topThumbZIndex;
399             } else {
400                 zIndex = '';
401             }
402
403             thumb.el.setStyle('zIndex', zIndex);
404         }
405     },
406
407     // private override
408     onRender : function() {
409         this.autoEl = {
410             cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),
411             cn : {
412                 cls: 'x-slider-end',
413                 cn : {
414                     cls:'x-slider-inner',
415                     cn : [{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]
416                 }
417             }
418         };
419
420         Ext.slider.MultiSlider.superclass.onRender.apply(this, arguments);
421
422         this.endEl   = this.el.first();
423         this.innerEl = this.endEl.first();
424         this.focusEl = this.innerEl.child('.x-slider-focus');
425
426         //render each thumb
427         for (var i=0; i < this.thumbs.length; i++) {
428             this.thumbs[i].render();
429         }
430
431         //calculate the size of half a thumb
432         var thumb      = this.innerEl.child('.x-slider-thumb');
433         this.halfThumb = (this.vertical ? thumb.getHeight() : thumb.getWidth()) / 2;
434
435         this.initEvents();
436     },
437
438     /**
439      * @private
440      * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element.
441      * Creates a new DragTracker which is used to control what happens when the user drags the thumb around.
442      */
443     initEvents : function(){
444         this.mon(this.el, {
445             scope    : this,
446             mousedown: this.onMouseDown,
447             keydown  : this.onKeyDown
448         });
449
450         this.focusEl.swallowEvent("click", true);
451     },
452
453     /**
454      * @private
455      * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb',
456      * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb
457      * @param {Ext.EventObject} e The click event
458      */
459     onMouseDown : function(e){
460         if(this.disabled){
461             return;
462         }
463
464         //see if the click was on any of the thumbs
465         var thumbClicked = false;
466         for (var i=0; i < this.thumbs.length; i++) {
467             thumbClicked = thumbClicked || e.target == this.thumbs[i].el.dom;
468         }
469
470         if (this.clickToChange && !thumbClicked) {
471             var local = this.innerEl.translatePoints(e.getXY());
472             this.onClickChange(local);
473         }
474         this.focus();
475     },
476
477     /**
478      * @private
479      * Moves the thumb to the indicated position. Note that a Vertical implementation is provided in Ext.slider.Vertical.
480      * Only changes the value if the click was within this.clickRange.
481      * @param {Object} local Object containing top and left values for the click event.
482      */
483     onClickChange : function(local) {
484         if (local.top > this.clickRange[0] && local.top < this.clickRange[1]) {
485             //find the nearest thumb to the click event
486             var thumb = this.getNearest(local, 'left'),
487                 index = thumb.index;
488
489             this.setValue(index, Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);
490         }
491     },
492
493     /**
494      * @private
495      * Returns the nearest thumb to a click event, along with its distance
496      * @param {Object} local Object containing top and left values from a click event
497      * @param {String} prop The property of local to compare on. Use 'left' for horizontal sliders, 'top' for vertical ones
498      * @return {Object} The closest thumb object and its distance from the click event
499      */
500     getNearest: function(local, prop) {
501         var localValue = prop == 'top' ? this.innerEl.getHeight() - local[prop] : local[prop],
502             clickValue = this.reverseValue(localValue),
503             nearestDistance = (this.maxValue - this.minValue) + 5, //add a small fudge for the end of the slider 
504             index = 0,
505             nearest = null;
506
507         for (var i=0; i < this.thumbs.length; i++) {
508             var thumb = this.thumbs[i],
509                 value = thumb.value,
510                 dist  = Math.abs(value - clickValue);
511
512             if (Math.abs(dist <= nearestDistance)) {
513                 nearest = thumb;
514                 index = i;
515                 nearestDistance = dist;
516             }
517         }
518         return nearest;
519     },
520
521     /**
522      * @private
523      * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right
524      * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction
525      * @param {Ext.EventObject} e The Event object
526      */
527     onKeyDown : function(e){
528         /*
529          * The behaviour for keyboard handling with multiple thumbs is currently undefined.
530          * There's no real sane default for it, so leave it like this until we come up
531          * with a better way of doing it.
532          */
533         if(this.disabled || this.thumbs.length !== 1){
534             e.preventDefault();
535             return;
536         }
537         var k = e.getKey(),
538             val;
539         switch(k){
540             case e.UP:
541             case e.RIGHT:
542                 e.stopEvent();
543                 val = e.ctrlKey ? this.maxValue : this.getValue(0) + this.keyIncrement;
544                 this.setValue(0, val, undefined, true);
545             break;
546             case e.DOWN:
547             case e.LEFT:
548                 e.stopEvent();
549                 val = e.ctrlKey ? this.minValue : this.getValue(0) - this.keyIncrement;
550                 this.setValue(0, val, undefined, true);
551             break;
552             default:
553                 e.preventDefault();
554         }
555     },
556
557     /**
558      * @private
559      * If using snapping, this takes a desired new value and returns the closest snapped
560      * value to it
561      * @param {Number} value The unsnapped value
562      * @return {Number} The value of the nearest snap target
563      */
564     doSnap : function(value){
565         if (!(this.increment && value)) {
566             return value;
567         }
568         var newValue = value,
569             inc = this.increment,
570             m = value % inc;
571         if (m != 0) {
572             newValue -= m;
573             if (m * 2 >= inc) {
574                 newValue += inc;
575             } else if (m * 2 < -inc) {
576                 newValue -= inc;
577             }
578         }
579         return newValue.constrain(this.minValue,  this.maxValue);
580     },
581
582     // private
583     afterRender : function(){
584         Ext.slider.MultiSlider.superclass.afterRender.apply(this, arguments);
585
586         for (var i=0; i < this.thumbs.length; i++) {
587             var thumb = this.thumbs[i];
588
589             if (thumb.value !== undefined) {
590                 var v = this.normalizeValue(thumb.value);
591
592                 if (v !== thumb.value) {
593                     // delete this.value;
594                     this.setValue(i, v, false);
595                 } else {
596                     this.moveThumb(i, this.translateValue(v), false);
597                 }
598             }
599         };
600     },
601
602     /**
603      * @private
604      * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100,
605      * the ratio is 2
606      * @return {Number} The ratio of pixels to mapped values
607      */
608     getRatio : function(){
609         var w = this.innerEl.getWidth(),
610             v = this.maxValue - this.minValue;
611         return v == 0 ? w : (w/v);
612     },
613
614     /**
615      * @private
616      * Returns a snapped, constrained value when given a desired value
617      * @param {Number} value Raw number value
618      * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values
619      */
620     normalizeValue : function(v){
621         v = this.doSnap(v);
622         v = Ext.util.Format.round(v, this.decimalPrecision);
623         v = v.constrain(this.minValue, this.maxValue);
624         return v;
625     },
626
627     <div id="method-Ext.slider.MultiSlider-setMinValue"></div>/**
628      * Sets the minimum value for the slider instance. If the current value is less than the
629      * minimum value, the current value will be changed.
630      * @param {Number} val The new minimum value
631      */
632     setMinValue : function(val){
633         this.minValue = val;
634         var i = 0,
635             thumbs = this.thumbs,
636             len = thumbs.length,
637             t;
638             
639         for(; i < len; ++i){
640             t = thumbs[i];
641             t.value = t.value < val ? val : t.value;
642         }
643         this.syncThumb();
644     },
645
646     <div id="method-Ext.slider.MultiSlider-setMaxValue"></div>/**
647      * Sets the maximum value for the slider instance. If the current value is more than the
648      * maximum value, the current value will be changed.
649      * @param {Number} val The new maximum value
650      */
651     setMaxValue : function(val){
652         this.maxValue = val;
653         var i = 0,
654             thumbs = this.thumbs,
655             len = thumbs.length,
656             t;
657             
658         for(; i < len; ++i){
659             t = thumbs[i];
660             t.value = t.value > val ? val : t.value;
661         }
662         this.syncThumb();
663     },
664
665     <div id="method-Ext.slider.MultiSlider-setValue"></div>/**
666      * Programmatically sets the value of the Slider. Ensures that the value is constrained within
667      * the minValue and maxValue.
668      * @param {Number} index Index of the thumb to move
669      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
670      * @param {Boolean} animate Turn on or off animation, defaults to true
671      */
672     setValue : function(index, v, animate, changeComplete) {
673         var thumb = this.thumbs[index],
674             el    = thumb.el;
675
676         v = this.normalizeValue(v);
677
678         if (v !== thumb.value && this.fireEvent('beforechange', this, v, thumb.value, thumb) !== false) {
679             thumb.value = v;
680             if(this.rendered){
681                 this.moveThumb(index, this.translateValue(v), animate !== false);
682                 this.fireEvent('change', this, v, thumb);
683                 if(changeComplete){
684                     this.fireEvent('changecomplete', this, v, thumb);
685                 }
686             }
687         }
688     },
689
690     /**
691      * @private
692      */
693     translateValue : function(v) {
694         var ratio = this.getRatio();
695         return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
696     },
697
698     /**
699      * @private
700      * Given a pixel location along the slider, returns the mapped slider value for that pixel.
701      * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reverseValue(50)
702      * returns 200
703      * @param {Number} pos The position along the slider to return a mapped value for
704      * @return {Number} The mapped value for the given position
705      */
706     reverseValue : function(pos){
707         var ratio = this.getRatio();
708         return (pos + (this.minValue * ratio)) / ratio;
709     },
710
711     /**
712      * @private
713      * @param {Number} index Index of the thumb to move
714      */
715     moveThumb: function(index, v, animate){
716         var thumb = this.thumbs[index].el;
717
718         if(!animate || this.animate === false){
719             thumb.setLeft(v);
720         }else{
721             thumb.shift({left: v, stopFx: true, duration:.35});
722         }
723     },
724
725     // private
726     focus : function(){
727         this.focusEl.focus(10);
728     },
729
730     // private
731     onResize : function(w, h){
732         var thumbs = this.thumbs,
733             len = thumbs.length,
734             i = 0;
735             
736         /*
737          * If we happen to be animating during a resize, the position of the thumb will likely be off
738          * when the animation stops. As such, just stop any animations before syncing the thumbs.
739          */
740         for(; i < len; ++i){
741             thumbs[i].el.stopFx();    
742         }
743         // check to see if we're using an auto width
744         if(Ext.isNumber(w)){
745             this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));
746         }
747         this.syncThumb();
748         Ext.slider.MultiSlider.superclass.onResize.apply(this, arguments);
749     },
750
751     //private
752     onDisable: function(){
753         Ext.slider.MultiSlider.superclass.onDisable.call(this);
754
755         for (var i=0; i < this.thumbs.length; i++) {
756             var thumb = this.thumbs[i],
757                 el    = thumb.el;
758
759             thumb.disable();
760
761             if(Ext.isIE){
762                 //IE breaks when using overflow visible and opacity other than 1.
763                 //Create a place holder for the thumb and display it.
764                 var xy = el.getXY();
765                 el.hide();
766
767                 this.innerEl.addClass(this.disabledClass).dom.disabled = true;
768
769                 if (!this.thumbHolder) {
770                     this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});
771                 }
772
773                 this.thumbHolder.show().setXY(xy);
774             }
775         }
776     },
777
778     //private
779     onEnable: function(){
780         Ext.slider.MultiSlider.superclass.onEnable.call(this);
781
782         for (var i=0; i < this.thumbs.length; i++) {
783             var thumb = this.thumbs[i],
784                 el    = thumb.el;
785
786             thumb.enable();
787
788             if (Ext.isIE) {
789                 this.innerEl.removeClass(this.disabledClass).dom.disabled = false;
790
791                 if (this.thumbHolder) this.thumbHolder.hide();
792
793                 el.show();
794                 this.syncThumb();
795             }
796         }
797     },
798
799     <div id="method-Ext.slider.MultiSlider-syncThumb"></div>/**
800      * Synchronizes the thumb position to the proper proportion of the total component width based
801      * on the current slider {@link #value}.  This will be called automatically when the Slider
802      * is resized by a layout, but if it is rendered auto width, this method can be called from
803      * another resize handler to sync the Slider if necessary.
804      */
805     syncThumb : function() {
806         if (this.rendered) {
807             for (var i=0; i < this.thumbs.length; i++) {
808                 this.moveThumb(i, this.translateValue(this.thumbs[i].value));
809             }
810         }
811     },
812
813     <div id="method-Ext.slider.MultiSlider-getValue"></div>/**
814      * Returns the current value of the slider
815      * @param {Number} index The index of the thumb to return a value for
816      * @return {Number} The current value of the slider
817      */
818     getValue : function(index) {
819         return this.thumbs[index].value;
820     },
821
822     <div id="method-Ext.slider.MultiSlider-getValues"></div>/**
823      * Returns an array of values - one for the location of each thumb
824      * @return {Array} The set of thumb values
825      */
826     getValues: function() {
827         var values = [];
828
829         for (var i=0; i < this.thumbs.length; i++) {
830             values.push(this.thumbs[i].value);
831         }
832
833         return values;
834     },
835
836     // private
837     beforeDestroy : function(){
838         var thumbs = this.thumbs;
839         for(var i = 0, len = thumbs.length; i < len; ++i){
840             thumbs[i].destroy();
841             thumbs[i] = null;
842         }
843         Ext.destroyMembers(this, 'endEl', 'innerEl', 'focusEl', 'thumbHolder');
844         Ext.slider.MultiSlider.superclass.beforeDestroy.call(this);
845     }
846 });
847
848 Ext.reg('multislider', Ext.slider.MultiSlider);
849
850 <div id="cls-Ext.slider.SingleSlider"></div>/**
851  * @class Ext.slider.SingleSlider
852  * @extends Ext.slider.MultiSlider
853  * Slider which supports vertical or horizontal orientation, keyboard adjustments,
854  * configurable snapping, axis clicking and animation. Can be added as an item to
855  * any container. Example usage:
856 <pre><code>
857 new Ext.slider.SingleSlider({
858     renderTo: Ext.getBody(),
859     width: 200,
860     value: 50,
861     increment: 10,
862     minValue: 0,
863     maxValue: 100
864 });
865 </code></pre>
866  * The class Ext.slider.SingleSlider is aliased to Ext.Slider for backwards compatibility.
867  */
868 Ext.slider.SingleSlider = Ext.extend(Ext.slider.MultiSlider, {
869     constructor: function(config) {
870       config = config || {};
871
872       Ext.applyIf(config, {
873           values: [config.value || 0]
874       });
875
876       Ext.slider.SingleSlider.superclass.constructor.call(this, config);
877     },
878
879     <div id="method-Ext.slider.SingleSlider-getValue"></div>/**
880      * Returns the current value of the slider
881      * @return {Number} The current value of the slider
882      */
883     getValue: function() {
884         //just returns the value of the first thumb, which should be the only one in a single slider
885         return Ext.slider.SingleSlider.superclass.getValue.call(this, 0);
886     },
887
888     <div id="method-Ext.slider.SingleSlider-setValue"></div>/**
889      * Programmatically sets the value of the Slider. Ensures that the value is constrained within
890      * the minValue and maxValue.
891      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
892      * @param {Boolean} animate Turn on or off animation, defaults to true
893      */
894     setValue: function(value, animate) {
895         var args = Ext.toArray(arguments),
896             len  = args.length;
897
898         //this is to maintain backwards compatiblity for sliders with only one thunb. Usually you must pass the thumb
899         //index to setValue, but if we only have one thumb we inject the index here first if given the multi-slider
900         //signature without the required index. The index will always be 0 for a single slider
901         if (len == 1 || (len <= 3 && typeof arguments[1] != 'number')) {
902             args.unshift(0);
903         }
904
905         return Ext.slider.SingleSlider.superclass.setValue.apply(this, args);
906     },
907
908     <div id="method-Ext.slider.SingleSlider-syncThumb"></div>/**
909      * Synchronizes the thumb position to the proper proportion of the total component width based
910      * on the current slider {@link #value}.  This will be called automatically when the Slider
911      * is resized by a layout, but if it is rendered auto width, this method can be called from
912      * another resize handler to sync the Slider if necessary.
913      */
914     syncThumb : function() {
915         return Ext.slider.SingleSlider.superclass.syncThumb.apply(this, [0].concat(arguments));
916     },
917     
918     // private
919     getNearest : function(){
920         // Since there's only 1 thumb, it's always the nearest
921         return this.thumbs[0];    
922     }
923 });
924
925 //backwards compatibility
926 Ext.Slider = Ext.slider.SingleSlider;
927
928 Ext.reg('slider', Ext.slider.SingleSlider);
929
930 // private class to support vertical sliders
931 Ext.slider.Vertical = {
932     onResize : function(w, h){
933         this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));
934         this.syncThumb();
935     },
936
937     getRatio : function(){
938         var h = this.innerEl.getHeight(),
939             v = this.maxValue - this.minValue;
940         return h/v;
941     },
942
943     moveThumb: function(index, v, animate) {
944         var thumb = this.thumbs[index],
945             el    = thumb.el;
946
947         if (!animate || this.animate === false) {
948             el.setBottom(v);
949         } else {
950             el.shift({bottom: v, stopFx: true, duration:.35});
951         }
952     },
953
954     onClickChange : function(local) {
955         if (local.left > this.clickRange[0] && local.left < this.clickRange[1]) {
956             var thumb = this.getNearest(local, 'top'),
957                 index = thumb.index,
958                 value = this.minValue + this.reverseValue(this.innerEl.getHeight() - local.top);
959
960             this.setValue(index, Ext.util.Format.round(value, this.decimalPrecision), undefined, true);
961         }
962     }
963 };
964
965 //private class to support vertical dragging of thumbs within a slider
966 Ext.slider.Thumb.Vertical = {
967     getNewValue: function() {
968         var slider   = this.slider,
969             innerEl  = slider.innerEl,
970             pos      = innerEl.translatePoints(this.tracker.getXY()),
971             bottom   = innerEl.getHeight() - pos.top;
972
973         return slider.minValue + Ext.util.Format.round(bottom / slider.getRatio(), slider.decimalPrecision);
974     }
975 };
976 </pre>    
977 </body>
978 </html>