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