Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Multi.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-slider-Multi'>/**
19 </span> * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking
20  * and animation. Can be added as an item to any container.
21  *
22  * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one:
23  *
24  *     @example
25  *     Ext.create('Ext.slider.Multi', {
26  *         width: 200,
27  *         values: [25, 50, 75],
28  *         increment: 5,
29  *         minValue: 0,
30  *         maxValue: 100,
31  *
32  *         // this defaults to true, setting to false allows the thumbs to pass each other
33  *         constrainThumbs: false,
34  *         renderTo: Ext.getBody()
35  *     });
36  */
37 Ext.define('Ext.slider.Multi', {
38     extend: 'Ext.form.field.Base',
39     alias: 'widget.multislider',
40     alternateClassName: 'Ext.slider.MultiSlider',
41
42     requires: [
43         'Ext.slider.Thumb',
44         'Ext.slider.Tip',
45         'Ext.Number',
46         'Ext.util.Format',
47         'Ext.Template',
48         'Ext.layout.component.field.Slider'
49     ],
50
51     // note: {id} here is really {inputId}, but {cmpId} is available
52     fieldSubTpl: [
53         '&lt;div id=&quot;{id}&quot; class=&quot;' + Ext.baseCSSPrefix + 'slider {fieldCls} {vertical}&quot; aria-valuemin=&quot;{minValue}&quot; aria-valuemax=&quot;{maxValue}&quot; aria-valuenow=&quot;{value}&quot; aria-valuetext=&quot;{value}&quot;&gt;',
54             '&lt;div id=&quot;{cmpId}-endEl&quot; class=&quot;' + Ext.baseCSSPrefix + 'slider-end&quot; role=&quot;presentation&quot;&gt;',
55                 '&lt;div id=&quot;{cmpId}-innerEl&quot; class=&quot;' + Ext.baseCSSPrefix + 'slider-inner&quot; role=&quot;presentation&quot;&gt;',
56                     '&lt;a id=&quot;{cmpId}-focusEl&quot; class=&quot;' + Ext.baseCSSPrefix + 'slider-focus&quot; href=&quot;#&quot; tabIndex=&quot;-1&quot; hidefocus=&quot;on&quot; role=&quot;presentation&quot;&gt;&lt;/a&gt;',
57                 '&lt;/div&gt;',
58             '&lt;/div&gt;',
59         '&lt;/div&gt;',
60         {
61             disableFormats: true,
62             compiled: true
63         }
64     ],
65
66 <span id='Ext-slider-Multi-cfg-value'>    /**
67 </span>     * @cfg {Number} value
68      * A value with which to initialize the slider. Defaults to minValue. Setting this will only result in the creation
69      * of a single slider thumb; if you want multiple thumbs then use the {@link #values} config instead.
70      */
71
72 <span id='Ext-slider-Multi-cfg-values'>    /**
73 </span>     * @cfg {Number[]} values
74      * Array of Number values with which to initalize the slider. A separate slider thumb will be created for each value
75      * in this array. This will take precedence over the single {@link #value} config.
76      */
77
78 <span id='Ext-slider-Multi-cfg-vertical'>    /**
79 </span>     * @cfg {Boolean} vertical
80      * Orient the Slider vertically rather than horizontally.
81      */
82     vertical: false,
83
84 <span id='Ext-slider-Multi-cfg-minValue'>    /**
85 </span>     * @cfg {Number} minValue
86      * The minimum value for the Slider.
87      */
88     minValue: 0,
89
90 <span id='Ext-slider-Multi-cfg-maxValue'>    /**
91 </span>     * @cfg {Number} maxValue
92      * The maximum value for the Slider.
93      */
94     maxValue: 100,
95
96 <span id='Ext-slider-Multi-cfg-decimalPrecision'>    /**
97 </span>     * @cfg {Number/Boolean} decimalPrecision The number of decimal places to which to round the Slider's value.
98      *
99      * To disable rounding, configure as **false**.
100      */
101     decimalPrecision: 0,
102
103 <span id='Ext-slider-Multi-cfg-keyIncrement'>    /**
104 </span>     * @cfg {Number} keyIncrement
105      * How many units to change the Slider when adjusting with keyboard navigation. If the increment
106      * config is larger, it will be used instead.
107      */
108     keyIncrement: 1,
109
110 <span id='Ext-slider-Multi-cfg-increment'>    /**
111 </span>     * @cfg {Number} increment
112      * How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
113      */
114     increment: 0,
115
116 <span id='Ext-slider-Multi-property-clickRange'>    /**
117 </span>     * @private
118      * @property {Number[]} clickRange
119      * 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],
120      * 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'
121      * 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
122      */
123     clickRange: [5,15],
124
125 <span id='Ext-slider-Multi-cfg-clickToChange'>    /**
126 </span>     * @cfg {Boolean} clickToChange
127      * Determines whether or not clicking on the Slider axis will change the slider.
128      */
129     clickToChange : true,
130
131 <span id='Ext-slider-Multi-cfg-animate'>    /**
132 </span>     * @cfg {Boolean} animate
133      * Turn on or off animation.
134      */
135     animate: true,
136
137 <span id='Ext-slider-Multi-property-dragging'>    /**
138 </span>     * @property {Boolean} dragging
139      * True while the thumb is in a drag operation
140      */
141     dragging: false,
142
143 <span id='Ext-slider-Multi-cfg-constrainThumbs'>    /**
144 </span>     * @cfg {Boolean} constrainThumbs
145      * True to disallow thumbs from overlapping one another.
146      */
147     constrainThumbs: true,
148
149     componentLayout: 'sliderfield',
150
151 <span id='Ext-slider-Multi-cfg-useTips'>    /**
152 </span>     * @cfg {Boolean} useTips
153      * True to use an Ext.slider.Tip to display tips for the value.
154      */
155     useTips : true,
156
157 <span id='Ext-slider-Multi-cfg-tipText'>    /**
158 </span>     * @cfg {Function} tipText
159      * A function used to display custom text for the slider tip. Defaults to null, which will use the default on the
160      * plugin.
161      */
162     tipText : null,
163
164     ariaRole: 'slider',
165
166     // private override
167     initValue: function() {
168         var me = this,
169             extValue = Ext.value,
170             // Fallback for initial values: values config -&gt; value config -&gt; minValue config -&gt; 0
171             values = extValue(me.values, [extValue(me.value, extValue(me.minValue, 0))]),
172             i = 0,
173             len = values.length;
174
175         // Store for use in dirty check
176         me.originalValue = values;
177
178         // Add a thumb for each value
179         for (; i &lt; len; i++) {
180             me.addThumb(values[i]);
181         }
182     },
183
184     // private override
185     initComponent : function() {
186         var me = this,
187             tipPlug,
188             hasTip;
189
190 <span id='Ext-slider-Multi-property-thumbs'>        /**
191 </span>         * @property {Array} thumbs
192          * Array containing references to each thumb
193          */
194         me.thumbs = [];
195
196         me.keyIncrement = Math.max(me.increment, me.keyIncrement);
197
198         me.addEvents(
199 <span id='Ext-slider-Multi-event-beforechange'>            /**
200 </span>             * @event beforechange
201              * Fires before the slider value is changed. By returning false from an event handler, you can cancel the
202              * event and prevent the slider from changing.
203              * @param {Ext.slider.Multi} slider The slider
204              * @param {Number} newValue The new value which the slider is being changed to.
205              * @param {Number} oldValue The old value which the slider was previously.
206              */
207             'beforechange',
208
209 <span id='Ext-slider-Multi-event-change'>            /**
210 </span>             * @event change
211              * Fires when the slider value is changed.
212              * @param {Ext.slider.Multi} slider The slider
213              * @param {Number} newValue The new value which the slider has been changed to.
214              * @param {Ext.slider.Thumb} thumb The thumb that was changed
215              */
216             'change',
217
218 <span id='Ext-slider-Multi-event-changecomplete'>            /**
219 </span>             * @event changecomplete
220              * Fires when the slider value is changed by the user and any drag operations have completed.
221              * @param {Ext.slider.Multi} slider The slider
222              * @param {Number} newValue The new value which the slider has been changed to.
223              * @param {Ext.slider.Thumb} thumb The thumb that was changed
224              */
225             'changecomplete',
226
227 <span id='Ext-slider-Multi-event-dragstart'>            /**
228 </span>             * @event dragstart
229              * Fires after a drag operation has started.
230              * @param {Ext.slider.Multi} slider The slider
231              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
232              */
233             'dragstart',
234
235 <span id='Ext-slider-Multi-event-drag'>            /**
236 </span>             * @event drag
237              * Fires continuously during the drag operation while the mouse is moving.
238              * @param {Ext.slider.Multi} slider The slider
239              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
240              */
241             'drag',
242
243 <span id='Ext-slider-Multi-event-dragend'>            /**
244 </span>             * @event dragend
245              * Fires after the drag operation has completed.
246              * @param {Ext.slider.Multi} slider The slider
247              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
248              */
249             'dragend'
250         );
251
252         if (me.vertical) {
253             Ext.apply(me, Ext.slider.Multi.Vertical);
254         }
255
256         me.callParent();
257
258         // only can use it if it exists.
259         if (me.useTips) {
260             tipPlug = me.tipText ? {getText: me.tipText} : {};
261             me.plugins = me.plugins || [];
262             Ext.each(me.plugins, function(plug){
263                 if (plug.isSliderTip) {
264                     hasTip = true;
265                     return false;
266                 }
267             });
268             if (!hasTip) {
269                 me.plugins.push(Ext.create('Ext.slider.Tip', tipPlug));
270             }
271         }
272     },
273
274 <span id='Ext-slider-Multi-method-addThumb'>    /**
275 </span>     * Creates a new thumb and adds it to the slider
276      * @param {Number} value The initial value to set on the thumb. Defaults to 0
277      * @return {Ext.slider.Thumb} The thumb
278      */
279     addThumb: function(value) {
280         var me = this,
281             thumb = Ext.create('Ext.slider.Thumb', {
282             value    : value,
283             slider   : me,
284             index    : me.thumbs.length,
285             constrain: me.constrainThumbs
286         });
287         me.thumbs.push(thumb);
288
289         //render the thumb now if needed
290         if (me.rendered) {
291             thumb.render();
292         }
293
294         return thumb;
295     },
296
297 <span id='Ext-slider-Multi-method-promoteThumb'>    /**
298 </span>     * @private
299      * Moves the given thumb above all other by increasing its z-index. This is called when as drag
300      * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is
301      * required when the thumbs are stacked on top of each other at one of the ends of the slider's
302      * range, which can result in the user not being able to move any of them.
303      * @param {Ext.slider.Thumb} topThumb The thumb to move to the top
304      */
305     promoteThumb: function(topThumb) {
306         var thumbs = this.thumbs,
307             ln = thumbs.length,
308             zIndex, thumb, i;
309
310         for (i = 0; i &lt; ln; i++) {
311             thumb = thumbs[i];
312
313             if (thumb == topThumb) {
314                 thumb.bringToFront();
315             } else {
316                 thumb.sendToBack();
317             }
318         }
319     },
320
321     // private override
322     onRender : function() {
323         var me = this,
324             i = 0,
325             thumbs = me.thumbs,
326             len = thumbs.length,
327             thumb;
328
329         Ext.applyIf(me.subTplData, {
330             vertical: me.vertical ? Ext.baseCSSPrefix + 'slider-vert' : Ext.baseCSSPrefix + 'slider-horz',
331             minValue: me.minValue,
332             maxValue: me.maxValue,
333             value: me.value
334         });
335
336         me.addChildEls('endEl', 'innerEl', 'focusEl');
337
338         me.callParent(arguments);
339
340         //render each thumb
341         for (; i &lt; len; i++) {
342             thumbs[i].render();
343         }
344
345         //calculate the size of half a thumb
346         thumb = me.innerEl.down('.' + Ext.baseCSSPrefix + 'slider-thumb');
347         me.halfThumb = (me.vertical ? thumb.getHeight() : thumb.getWidth()) / 2;
348
349     },
350
351 <span id='Ext-slider-Multi-method-onChange'>    /**
352 </span>     * Utility method to set the value of the field when the slider changes.
353      * @param {Object} slider The slider object.
354      * @param {Object} v The new value.
355      * @private
356      */
357     onChange : function(slider, v) {
358         this.setValue(v, undefined, true);
359     },
360
361 <span id='Ext-slider-Multi-method-initEvents'>    /**
362 </span>     * @private
363      * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element.
364      */
365     initEvents : function() {
366         var me = this;
367
368         me.mon(me.el, {
369             scope    : me,
370             mousedown: me.onMouseDown,
371             keydown  : me.onKeyDown,
372             change : me.onChange
373         });
374
375         me.focusEl.swallowEvent(&quot;click&quot;, true);
376     },
377
378 <span id='Ext-slider-Multi-method-onMouseDown'>    /**
379 </span>     * @private
380      * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb',
381      * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb
382      * @param {Ext.EventObject} e The click event
383      */
384     onMouseDown : function(e) {
385         var me = this,
386             thumbClicked = false,
387             i = 0,
388             thumbs = me.thumbs,
389             len = thumbs.length,
390             local;
391
392         if (me.disabled) {
393             return;
394         }
395
396         //see if the click was on any of the thumbs
397         for (; i &lt; len; i++) {
398             thumbClicked = thumbClicked || e.target == thumbs[i].el.dom;
399         }
400
401         if (me.clickToChange &amp;&amp; !thumbClicked) {
402             local = me.innerEl.translatePoints(e.getXY());
403             me.onClickChange(local);
404         }
405         me.focus();
406     },
407
408 <span id='Ext-slider-Multi-method-onClickChange'>    /**
409 </span>     * @private
410      * Moves the thumb to the indicated position. Note that a Vertical implementation is provided in Ext.slider.Multi.Vertical.
411      * Only changes the value if the click was within this.clickRange.
412      * @param {Object} local Object containing top and left values for the click event.
413      */
414     onClickChange : function(local) {
415         var me = this,
416             thumb, index;
417
418         if (local.top &gt; me.clickRange[0] &amp;&amp; local.top &lt; me.clickRange[1]) {
419             //find the nearest thumb to the click event
420             thumb = me.getNearest(local, 'left');
421             if (!thumb.disabled) {
422                 index = thumb.index;
423                 me.setValue(index, Ext.util.Format.round(me.reverseValue(local.left), me.decimalPrecision), undefined, true);
424             }
425         }
426     },
427
428 <span id='Ext-slider-Multi-method-getNearest'>    /**
429 </span>     * @private
430      * Returns the nearest thumb to a click event, along with its distance
431      * @param {Object} local Object containing top and left values from a click event
432      * @param {String} prop The property of local to compare on. Use 'left' for horizontal sliders, 'top' for vertical ones
433      * @return {Object} The closest thumb object and its distance from the click event
434      */
435     getNearest: function(local, prop) {
436         var me = this,
437             localValue = prop == 'top' ? me.innerEl.getHeight() - local[prop] : local[prop],
438             clickValue = me.reverseValue(localValue),
439             nearestDistance = (me.maxValue - me.minValue) + 5, //add a small fudge for the end of the slider
440             index = 0,
441             nearest = null,
442             thumbs = me.thumbs,
443             i = 0,
444             len = thumbs.length,
445             thumb,
446             value,
447             dist;
448
449         for (; i &lt; len; i++) {
450             thumb = me.thumbs[i];
451             value = thumb.value;
452             dist  = Math.abs(value - clickValue);
453
454             if (Math.abs(dist &lt;= nearestDistance)) {
455                 nearest = thumb;
456                 index = i;
457                 nearestDistance = dist;
458             }
459         }
460         return nearest;
461     },
462
463 <span id='Ext-slider-Multi-method-onKeyDown'>    /**
464 </span>     * @private
465      * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right
466      * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction
467      * @param {Ext.EventObject} e The Event object
468      */
469     onKeyDown : function(e) {
470         /*
471          * The behaviour for keyboard handling with multiple thumbs is currently undefined.
472          * There's no real sane default for it, so leave it like this until we come up
473          * with a better way of doing it.
474          */
475         var me = this,
476             k,
477             val;
478
479         if(me.disabled || me.thumbs.length !== 1) {
480             e.preventDefault();
481             return;
482         }
483         k = e.getKey();
484
485         switch(k) {
486             case e.UP:
487             case e.RIGHT:
488                 e.stopEvent();
489                 val = e.ctrlKey ? me.maxValue : me.getValue(0) + me.keyIncrement;
490                 me.setValue(0, val, undefined, true);
491             break;
492             case e.DOWN:
493             case e.LEFT:
494                 e.stopEvent();
495                 val = e.ctrlKey ? me.minValue : me.getValue(0) - me.keyIncrement;
496                 me.setValue(0, val, undefined, true);
497             break;
498             default:
499                 e.preventDefault();
500         }
501     },
502
503     // private
504     afterRender : function() {
505         var me = this,
506             i = 0,
507             thumbs = me.thumbs,
508             len = thumbs.length,
509             thumb,
510             v;
511
512         me.callParent(arguments);
513
514         for (; i &lt; len; i++) {
515             thumb = thumbs[i];
516
517             if (thumb.value !== undefined) {
518                 v = me.normalizeValue(thumb.value);
519                 if (v !== thumb.value) {
520                     // delete this.value;
521                     me.setValue(i, v, false);
522                 } else {
523                     thumb.move(me.translateValue(v), false);
524                 }
525             }
526         }
527     },
528
529 <span id='Ext-slider-Multi-method-getRatio'>    /**
530 </span>     * @private
531      * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100,
532      * the ratio is 2
533      * @return {Number} The ratio of pixels to mapped values
534      */
535     getRatio : function() {
536         var w = this.innerEl.getWidth(),
537             v = this.maxValue - this.minValue;
538         return v === 0 ? w : (w/v);
539     },
540
541 <span id='Ext-slider-Multi-method-normalizeValue'>    /**
542 </span>     * @private
543      * Returns a snapped, constrained value when given a desired value
544      * @param {Number} value Raw number value
545      * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values
546      */
547     normalizeValue : function(v) {
548         var me = this;
549
550         v = Ext.Number.snap(v, this.increment, this.minValue, this.maxValue);
551         v = Ext.util.Format.round(v, me.decimalPrecision);
552         v = Ext.Number.constrain(v, me.minValue, me.maxValue);
553         return v;
554     },
555
556 <span id='Ext-slider-Multi-method-setMinValue'>    /**
557 </span>     * Sets the minimum value for the slider instance. If the current value is less than the minimum value, the current
558      * value will be changed.
559      * @param {Number} val The new minimum value
560      */
561     setMinValue : function(val) {
562         var me = this,
563             i = 0,
564             thumbs = me.thumbs,
565             len = thumbs.length,
566             t;
567
568         me.minValue = val;
569         if (me.rendered) {
570             me.inputEl.dom.setAttribute('aria-valuemin', val);
571         }
572
573         for (; i &lt; len; ++i) {
574             t = thumbs[i];
575             t.value = t.value &lt; val ? val : t.value;
576         }
577         me.syncThumbs();
578     },
579
580 <span id='Ext-slider-Multi-method-setMaxValue'>    /**
581 </span>     * Sets the maximum value for the slider instance. If the current value is more than the maximum value, the current
582      * value will be changed.
583      * @param {Number} val The new maximum value
584      */
585     setMaxValue : function(val) {
586         var me = this,
587             i = 0,
588             thumbs = me.thumbs,
589             len = thumbs.length,
590             t;
591
592         me.maxValue = val;
593         if (me.rendered) {
594             me.inputEl.dom.setAttribute('aria-valuemax', val);
595         }
596
597         for (; i &lt; len; ++i) {
598             t = thumbs[i];
599             t.value = t.value &gt; val ? val : t.value;
600         }
601         me.syncThumbs();
602     },
603
604 <span id='Ext-slider-Multi-method-setValue'>    /**
605 </span>     * Programmatically sets the value of the Slider. Ensures that the value is constrained within the minValue and
606      * maxValue.
607      * @param {Number} index Index of the thumb to move
608      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
609      * @param {Boolean} [animate=true] Turn on or off animation
610      */
611     setValue : function(index, value, animate, changeComplete) {
612         var me = this,
613             thumb = me.thumbs[index];
614
615         // ensures value is contstrained and snapped
616         value = me.normalizeValue(value);
617
618         if (value !== thumb.value &amp;&amp; me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) {
619             thumb.value = value;
620             if (me.rendered) {
621                 // TODO this only handles a single value; need a solution for exposing multiple values to aria.
622                 // Perhaps this should go on each thumb element rather than the outer element.
623                 me.inputEl.set({
624                     'aria-valuenow': value,
625                     'aria-valuetext': value
626                 });
627
628                 thumb.move(me.translateValue(value), Ext.isDefined(animate) ? animate !== false : me.animate);
629
630                 me.fireEvent('change', me, value, thumb);
631                 if (changeComplete) {
632                     me.fireEvent('changecomplete', me, value, thumb);
633                 }
634             }
635         }
636     },
637
638 <span id='Ext-slider-Multi-method-translateValue'>    /**
639 </span>     * @private
640      */
641     translateValue : function(v) {
642         var ratio = this.getRatio();
643         return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
644     },
645
646 <span id='Ext-slider-Multi-method-reverseValue'>    /**
647 </span>     * @private
648      * Given a pixel location along the slider, returns the mapped slider value for that pixel.
649      * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reverseValue(50)
650      * returns 200
651      * @param {Number} pos The position along the slider to return a mapped value for
652      * @return {Number} The mapped value for the given position
653      */
654     reverseValue : function(pos) {
655         var ratio = this.getRatio();
656         return (pos + (this.minValue * ratio)) / ratio;
657     },
658
659     // private
660     focus : function() {
661         this.focusEl.focus(10);
662     },
663
664     //private
665     onDisable: function() {
666         var me = this,
667             i = 0,
668             thumbs = me.thumbs,
669             len = thumbs.length,
670             thumb,
671             el,
672             xy;
673
674         me.callParent();
675
676         for (; i &lt; len; i++) {
677             thumb = thumbs[i];
678             el = thumb.el;
679
680             thumb.disable();
681
682             if(Ext.isIE) {
683                 //IE breaks when using overflow visible and opacity other than 1.
684                 //Create a place holder for the thumb and display it.
685                 xy = el.getXY();
686                 el.hide();
687
688                 me.innerEl.addCls(me.disabledCls).dom.disabled = true;
689
690                 if (!me.thumbHolder) {
691                     me.thumbHolder = me.endEl.createChild({cls: Ext.baseCSSPrefix + 'slider-thumb ' + me.disabledCls});
692                 }
693
694                 me.thumbHolder.show().setXY(xy);
695             }
696         }
697     },
698
699     //private
700     onEnable: function() {
701         var me = this,
702             i = 0,
703             thumbs = me.thumbs,
704             len = thumbs.length,
705             thumb,
706             el;
707
708         this.callParent();
709
710         for (; i &lt; len; i++) {
711             thumb = thumbs[i];
712             el = thumb.el;
713
714             thumb.enable();
715
716             if (Ext.isIE) {
717                 me.innerEl.removeCls(me.disabledCls).dom.disabled = false;
718
719                 if (me.thumbHolder) {
720                     me.thumbHolder.hide();
721                 }
722
723                 el.show();
724                 me.syncThumbs();
725             }
726         }
727     },
728
729 <span id='Ext-slider-Multi-method-syncThumbs'>    /**
730 </span>     * Synchronizes thumbs position to the proper proportion of the total component width based on the current slider
731      * {@link #value}. This will be called automatically when the Slider is resized by a layout, but if it is rendered
732      * auto width, this method can be called from another resize handler to sync the Slider if necessary.
733      */
734     syncThumbs : function() {
735         if (this.rendered) {
736             var thumbs = this.thumbs,
737                 length = thumbs.length,
738                 i = 0;
739
740             for (; i &lt; length; i++) {
741                 thumbs[i].move(this.translateValue(thumbs[i].value));
742             }
743         }
744     },
745
746 <span id='Ext-slider-Multi-method-getValue'>    /**
747 </span>     * Returns the current value of the slider
748      * @param {Number} index The index of the thumb to return a value for
749      * @return {Number/Number[]} The current value of the slider at the given index, or an array of all thumb values if
750      * no index is given.
751      */
752     getValue : function(index) {
753         return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues();
754     },
755
756 <span id='Ext-slider-Multi-method-getValues'>    /**
757 </span>     * Returns an array of values - one for the location of each thumb
758      * @return {Number[]} The set of thumb values
759      */
760     getValues: function() {
761         var values = [],
762             i = 0,
763             thumbs = this.thumbs,
764             len = thumbs.length;
765
766         for (; i &lt; len; i++) {
767             values.push(thumbs[i].value);
768         }
769
770         return values;
771     },
772
773     getSubmitValue: function() {
774         var me = this;
775         return (me.disabled || !me.submitValue) ? null : me.getValue();
776     },
777
778     reset: function() {
779         var me = this,
780             Array = Ext.Array;
781         Array.forEach(Array.from(me.originalValue), function(val, i) {
782             me.setValue(i, val);
783         });
784         me.clearInvalid();
785         // delete here so we reset back to the original state
786         delete me.wasValid;
787     },
788
789     // private
790     beforeDestroy : function() {
791         var me = this;
792
793         Ext.destroy(me.innerEl, me.endEl, me.focusEl);
794         Ext.each(me.thumbs, function(thumb) {
795             Ext.destroy(thumb);
796         }, me);
797
798         me.callParent();
799     },
800
801     statics: {
802         // Method overrides to support slider with vertical orientation
803         Vertical: {
804             getRatio : function() {
805                 var h = this.innerEl.getHeight(),
806                     v = this.maxValue - this.minValue;
807                 return h/v;
808             },
809
810             onClickChange : function(local) {
811                 var me = this,
812                     thumb, index, bottom;
813
814                 if (local.left &gt; me.clickRange[0] &amp;&amp; local.left &lt; me.clickRange[1]) {
815                     thumb = me.getNearest(local, 'top');
816                     if (!thumb.disabled) {
817                         index = thumb.index;
818                         bottom =  me.reverseValue(me.innerEl.getHeight() - local.top);
819
820                         me.setValue(index, Ext.util.Format.round(me.minValue + bottom, me.decimalPrecision), undefined, true);
821                     }
822                 }
823             }
824         }
825     }
826 });
827 </pre>
828 </body>
829 </html>