1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2004-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.controls.sliderClasses
13{
14
15import flash.display.DisplayObject;
16import flash.display.Graphics;
17import flash.display.Sprite;
18import flash.events.Event;
19import flash.events.FocusEvent;
20import flash.events.KeyboardEvent;
21import flash.events.MouseEvent;
22import flash.geom.Point;
23import flash.utils.getTimer;
24import mx.core.FlexVersion;
25import mx.core.IFlexDisplayObject;
26import mx.core.mx_internal;
27import mx.core.UIComponent;
28import mx.effects.Tween;
29import mx.events.FlexEvent;
30import mx.events.SliderEvent;
31import mx.events.SliderEventClickTarget;
32import mx.formatters.NumberFormatter;
33import mx.managers.ISystemManager;
34import mx.managers.SystemManager;
35import mx.styles.ISimpleStyleClient;
36import mx.styles.StyleProxy;
37
38use namespace mx_internal;
39
40//--------------------------------------
41//  Events
42//--------------------------------------
43
44/**
45 *  Dispatched when the slider changes value due to mouse or keyboard interaction.
46 *
47 *  <p>If the <code>liveDragging</code> property is <code>true</code>,
48 *  the event is dispatched continuously as the user moves the thumb.
49 *  If <code>liveDragging</code> is <code>false</code>,
50 *  the event is dispatched when the user releases the slider thumb.</p>
51 *
52 *  @eventType mx.events.SliderEvent.CHANGE
53 */
54[Event(name="change", type="mx.events.SliderEvent")]
55
56/**
57 *  Dispatched when the slider's thumb is pressed and then moved by the mouse.
58 *  This event is always preceded by a <code>thumbPress</code> event.
59 *  @eventType mx.events.SliderEvent.THUMB_DRAG
60 */
61[Event(name="thumbDrag", type="mx.events.SliderEvent")]
62
63/**
64 *  Dispatched when the slider's thumb is pressed, meaning
65 *  the user presses the mouse button over the thumb.
66 *
67 *  @eventType mx.events.SliderEvent.THUMB_PRESS
68 */
69[Event(name="thumbPress", type="mx.events.SliderEvent")]
70
71/**
72 *  Dispatched when the slider's thumb is released,
73 *  meaning the user releases the mouse button after
74 *  a <code>thumbPress</code> event.
75 *
76 *  @eventType mx.events.SliderEvent.THUMB_RELEASE
77 */
78[Event(name="thumbRelease", type="mx.events.SliderEvent")]
79
80//--------------------------------------
81//  Styles
82//--------------------------------------
83
84include "../../styles/metadata/FillStyles.as";
85
86/**
87 *  The color of the black section of the border.
88 *
89 *  @default 0x919999
90 */
91[Style(name="borderColor", type="uint", format="Color", inherit="no")]
92
93/**
94 *  Invert the direction of the thumbs.
95 *  If <code>true</code>, the thumbs will be flipped.
96 *
97 *  @default false
98 */
99[Style(name="invertThumbDirection", type="Boolean", inherit="no")]
100
101/**
102 *  The y-position offset (if direction is horizontal)
103 *  or x-position offset (if direction is vertical)
104 *  of the labels relative to the track.
105 *
106  *  @default -10
107 */
108[Style(name="labelOffset", type="Number", format="Length", inherit="no")]
109
110/**
111 *  The name of the style to use for the slider label.
112 *
113 *  @default undefined
114 */
115[Style(name="labelStyleName", type="String", inherit="no")]
116
117/**
118 *  Duration in milliseconds for the sliding animation
119 *  when you click on the track to move a thumb.
120 *
121 *  @default 300
122 */
123[Style(name="slideDuration", type="Number", format="Time", inherit="no")]
124
125/**
126 *  Tweening function used by the sliding animation
127 *  when you click on the track to move a thumb.
128 *
129 *  @default undefined
130 */
131[Style(name="slideEasingFunction", type="Function", inherit="no")]
132
133/**
134 *  The y-position offset (if direction is horizontal)
135 *  or x-position offset (if direction is vertical)
136 *  of the thumb relative to the track.
137 *
138 *  @default 0
139 */
140[Style(name="thumbOffset", type="Number", format="Length", inherit="no")]
141
142/**
143 *  The color of the tick marks.
144 *  Can be a hex color value or the string name of a known color.
145 *
146 *  @default 0x6F7777.
147 */
148[Style(name="tickColor", type="uint", format="Color", inherit="no")]
149
150/**
151 *  The length in pixels of the tick marks.
152 *  If <code>direction</code> is <code>Direction.HORIZONTAL</code>,
153 *  then adjust the height of the tick marks.
154 *  If <code>direction</code> is <code>Direction.VERTICAL</code>,
155 *  then adjust the width.
156 *
157 *  @default 3
158 */
159[Style(name="tickLength", type="Number", format="Length", inherit="no")]
160
161/**
162 *  The y-position offset (if direction is horizontal)
163 *  or x-position offset (if direction is vertical)
164 *  of the tick marks relative to the track.
165 *
166 *  @default -6
167 */
168[Style(name="tickOffset", type="Number", format="Length", inherit="no")]
169
170/**
171 *  The thickness in pixels of the tick marks.
172 *  If direction is horizontal,
173 *  then adjust the width of the tick marks.
174 *  If direction is vertical,
175 *  then adjust the height.
176 *
177 *  @default 1
178 */
179[Style(name="tickThickness", type="Number", format="Length", inherit="no")]
180
181/**
182 *  The colors of the track, as an array of two colors.
183 *  You can use the same color twice for a solid track color.
184 *
185 *  <p>You use this property along with the <code>fillAlphas</code>
186 *  property. Typically you set <code>fillAlphas</code> to [ 1.0, 1.0 ]
187 *  when setting <code>trackColors</code>.</p>
188 *
189 *  @default [ 0xE7E7E7, 0xE7E7E7 ]
190 */
191[Style(name="trackColors", type="Array", arrayType="uint", format="Color", inherit="no")]
192
193/**
194 *  Specifies whether to enable track highlighting between thumbs
195 *  (or a single thumb and the beginning of the track).
196 *
197 *  @default false
198 */
199[Style(name="showTrackHighlight", type="Boolean", inherit="no")]
200
201/**
202 *  The size of the track margins, in pixels.
203 *  If <code>undefined</code>, then the track margins will be determined
204 *  by the length of the first and last labels.
205 *  If given a value, Flex attempts to fit the labels in the available space.
206 *
207 *  @default undefined
208 */
209[Style(name="trackMargin", type="Number", format="Length", inherit="no")]
210
211/**
212 *  The name of the style declaration to use for the data tip.
213 *
214 *  @default undefined
215 */
216[Style(name="dataTipStyleName", type="String", inherit="no")]
217
218/**
219 *  The offset, in pixels, of the data tip relative to the thumb.
220 *  Used in combination with the <code>dataTipPlacement</code>
221 *  style property of the HSlider and VSlider controls.
222 *
223 *  @default 16
224 */
225[Style(name="dataTipOffset", type="Number", format="Length", inherit="no")]
226
227/**
228 *  Number of decimal places to use for the data tip text.
229 *  A value of 0 means to round all values to an integer.
230 *
231 *  @default 2
232 */
233[Style(name="dataTipPrecision", type="int", inherit="no")]
234
235/**
236 *  The default skin for the slider thumb.
237 *
238 *  @default SliderThumbSkin
239 */
240[Style(name="thumbSkin", type="Class", inherit="no", states="up, over, down, disabled")]
241
242/**
243 *  The skin for the slider thumb up state.
244 *
245 *  @default SliderThumbSkin
246 */
247[Style(name="thumbUpSkin", type="Class", inherit="no")]
248
249/**
250 *  The skin for the slider thumb over state.
251 *
252 *  @default SliderThumbSkin
253 */
254[Style(name="thumbOverSkin", type="Class", inherit="no")]
255
256/**
257 *  The skin for the slider thumb down state.
258 *
259 *  @default SliderThumbSkin
260 */
261[Style(name="thumbDownSkin", type="Class", inherit="no")]
262
263/**
264 *  The skin for the slider thumb disabled state.
265 *
266 *  @default SliderThumbSkin
267 */
268[Style(name="thumbDisabledSkin", type="Class", inherit="no")]
269
270/**
271 *  The skin for the slider track when it is selected.
272 */
273[Style(name="trackHighlightSkin", type="Class", inherit="no")]
274
275/**
276 *  The skin for the slider track.
277 */
278[Style(name="trackSkin", type="Class", inherit="no")]
279
280//--------------------------------------
281//  Other metadata
282//--------------------------------------
283
284[AccessibilityClass(implementation="mx.accessibility.SliderAccImpl")]
285
286[ResourceBundle("SharedResources")]
287
288/**
289 *  The Slider class is the base class for the Flex slider controls.
290 *  The slider controls let users select a value by moving a slider thumb
291 *  between the end points of the slider
292 *  track. The current value of the slider is determined by
293 *  the relative location of the thumb between the
294 *  end points of the slider, corresponding to the slider's minimum and maximum values.
295 *  The Slider class is subclassed by HSlider and VSlider.
296 *
297 *  @mxml
298 *
299 *  <p>The Slider class cannot be used as an MXML tag. Use the <code>&lt;mx:HSlider&gt;</code>
300 *  and <code>&lt;mx:VSlider&gt;</code> tags instead. However, the Slider class does define tag
301 *  attributes used by the <code>&lt;mx:HSlider&gt;</code> and <code>&lt;mx:VSlider&gt;</code> tags. </p>
302 *
303 *  <p>The Slider class inherits all of the tag attributes
304 *  of its superclass, and adds the following tag attributes:</p>
305 *
306 *  <pre>
307 *  &lt;mx:<i>tagname</i>
308 *    <strong>Properties</strong>
309 *    allowThumbOverlap="false|true"
310 *    allowTrackClick="true|false"
311 *    dataTipFormatFunction="undefined"
312 *    direction="horizontal|vertical"
313 *    labels="undefined"
314 *    liveDragging="false|true"
315 *    maximum="10"
316 *    minimum="0"
317 *    showDataTip="true|false"
318 *    sliderDataTipClass="sliderDataTip"
319 *    sliderThumbClass="SliderThumb"
320 *    snapInterval="0"
321 *    thumbCount="1"
322 *    tickInterval="0"
323 *    tickValues="undefined"
324 *    value="<i>The value of the minimum property.</i>"
325 *
326 *    <strong>Styles</strong>
327 *    borderColor="0x919999"
328 *    dataTipOffset="16"
329 *    dataTipPrecision="2"
330 *    dataTipStyleName="undefined"
331 *    fillAlphas="[0.6, 0.4, 0.75, 0.65]"
332 *    fillColors="[0xFFFFFF, 0xCCCCCC, 0xFFFFFF, 0xEEEEEE;]"
333 *    labelOffset="-10"
334 *    labelStyleName="undefined"
335 *    showTrackHighlight="false"
336 *    slideDuration="300"
337 *    slideEasingFunction="undefined"
338 *    thumbDisabledSkin="SliderThumbSkin"
339 *    thumbDownSkin="SliderThumbSkin"
340 *    thumbOffset="0"
341 *    thumbOverSkin="SliderThumbSkin"
342 *    thumbUpSkin="SliderThumbSkin"
343 *    tickColor="0x6F7777"
344 *    tickLength="3"
345 *    tickOffset="-6"
346 *    tickThickness="1"
347 *    trackColors="[ 0xEEEEEE, 0xFFFFFF ]"
348 *    tracHighlightSkin="SliderHighlightSkin"
349 *    trackMargin="undefined"
350 *    trackSkin="SliderTrackSkin"
351 *
352 *    <strong>Events</strong>
353 *    change="<i>No default</i>"
354 *    thumbDrag="<i>No default</i>"
355 *    thumbPress="<i>No default</i>"
356 *    thumbRelease="<i>No default</i>"
357 *  /&gt;
358 *  </pre>
359 */
360public class Slider extends UIComponent
361{
362    include "../../core/Version.as";
363
364    //--------------------------------------------------------------------------
365    //
366    //  Class mixins
367    //
368    //--------------------------------------------------------------------------
369
370    /**
371     *  @private
372     *  Placeholder for mixin by SliderAccImpl.
373     */
374    mx_internal static var createAccessibilityImplementation:Function;
375
376    //--------------------------------------------------------------------------
377    //
378    //  Constructor
379    //
380    //--------------------------------------------------------------------------
381
382    /**
383     *  Constructor.
384     */
385    public function Slider()
386    {
387        super();
388
389        tabChildren = true;
390    }
391
392    //--------------------------------------------------------------------------
393    //
394    //  Variables
395    //
396    //--------------------------------------------------------------------------
397
398    /**
399     *  @private
400     */
401    private var track:IFlexDisplayObject;
402
403    /**
404     *  @private
405     */
406    private var thumbs:UIComponent;
407
408    /**
409     *  @private
410     */
411    private var thumbsChanged:Boolean = true;
412
413    /**
414     *  @private
415     */
416    private var ticks:UIComponent;
417
418    /**
419     *  @private
420     */
421    private var ticksChanged:Boolean = false;
422
423    /**
424     *  @private
425     */
426    private var labelObjects:UIComponent;
427
428    /**
429     *  @private
430     */
431    private var highlightTrack:IFlexDisplayObject;
432
433    /**
434     *  @private
435     */
436    mx_internal var innerSlider:UIComponent;
437
438    /**
439     *  @private
440     */
441    private var trackHitArea:UIComponent;
442
443    /**
444     *  @private
445     */
446    mx_internal var dataTip:SliderDataTip;
447
448    /**
449     *  @private
450     */
451    private var trackHighlightChanged:Boolean = true;
452
453    /**
454     *  @private
455     */
456    private var initValues:Boolean = true; // Always initValues at startup
457
458    /**
459     *  @private
460     */
461    private var dataFormatter:NumberFormatter;
462
463    /**
464     *  @private
465     */
466    private var interactionClickTarget:String;
467
468    /**
469     *  @private
470     */
471    private var labelStyleChanged:Boolean = false;
472
473    /**
474    *  @private
475    *  is the last interaction from the keyboard?
476    */
477    mx_internal var keyInteraction:Boolean = false;
478
479    //--------------------------------------------------------------------------
480    //
481    //  Overridden properties
482    //
483    //--------------------------------------------------------------------------
484
485    //----------------------------------
486    //  baselinePosition
487    //----------------------------------
488
489    /**
490     *  @private
491     */
492    override public function get baselinePosition():Number
493    {
494        if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0)
495            return super.baselinePosition;
496
497        if (!validateBaselinePosition())
498            return NaN;
499
500        return int(0.75 * height);
501    }
502
503    //----------------------------------
504    //  enabled
505    //----------------------------------
506
507    /**
508     *  @private
509     */
510    private var _enabled:Boolean;
511
512    /**
513     *  @private
514     */
515    private var enabledChanged:Boolean = false;
516
517    [Inspectable(category="General", enumeration="true,false", defaultValue="true")]
518
519    /**
520     *  @private
521     */
522    override public function get enabled():Boolean
523    {
524        return _enabled;
525    }
526
527    /**
528     *  @private
529     */
530    override public function set enabled(value:Boolean):void
531    {
532        _enabled = value;
533        enabledChanged = true;
534
535        invalidateProperties();
536    }
537
538    /**
539     *  @private
540     */
541    private var _tabIndex:Number;
542
543    /**
544     *  @private
545     */
546    private var tabIndexChanged:Boolean;
547
548    /**
549     *  @private
550     */
551    override public function set tabIndex(value:int):void
552    {
553        super.tabIndex = value;
554        _tabIndex = value;
555
556        tabIndexChanged = true;
557        invalidateProperties();
558    }
559
560    //--------------------------------------------------------------------------
561    //
562    //  Properties
563    //
564    //--------------------------------------------------------------------------
565
566    //----------------------------------
567    //  allowThumbOverlap
568    //----------------------------------
569
570    [Inspectable(defaultValue="false")]
571
572    /**
573     *  If set to <code>false</code>, then each thumb can only be moved to the edge of
574     *  the adjacent thumb.
575     *  If <code>true</code>, then each thumb can be moved to any position on the track.
576     *
577     *  @default false
578     */
579    public var allowThumbOverlap:Boolean = false;
580
581    //----------------------------------
582    //  allowTrackClick
583    //----------------------------------
584
585    [Inspectable(defaultValue="true")]
586
587    /**
588     *  Specifies whether clicking on the track will move the slider thumb.
589     *
590     *  @default true
591     */
592    public var allowTrackClick:Boolean = true;
593
594    //----------------------------------
595    //  dataTipFormatFunction
596    //----------------------------------
597
598    /**
599     *  @private
600     */
601    private var _dataTipFormatFunction:Function;
602
603    /**
604     *  Callback function that formats the data tip text.
605     *  The function takes a single Number as an argument
606     *  and returns a formatted String.
607     *
608     *  <p>The function has the following signature:</p>
609     *  <pre>
610     *  funcName(value:Number):String
611     *  </pre>
612     *
613     *  <p>The following example prefixes the data tip text with a dollar sign and
614     *  formats the text using the <code>dataTipPrecision</code>
615     *  of a Slider Control named 'slide': </p>
616     *
617     *  <pre>
618     *  import mx.formatters.NumberBase;
619     *  function myDataTipFormatter(value:Number):String {
620     *      var dataFormatter:NumberBase = new NumberBase(".", ",", ".", "");
621     *      return   "$ " + dataFormatter.formatPrecision(String(value), slide.getStyle("dataTipPrecision"));
622     *  }
623     *  </pre>
624     *
625     *  @default undefined
626     */
627    public function get dataTipFormatFunction():Function
628    {
629        return _dataTipFormatFunction;
630    }
631
632    /**
633     *  @private
634     */
635    public function set dataTipFormatFunction(value:Function):void
636    {
637        _dataTipFormatFunction = value;
638    }
639
640    //----------------------------------
641    //  direction
642    //----------------------------------
643
644    /**
645     *  @private
646     */
647    private var _direction:String = SliderDirection.HORIZONTAL;
648
649    /**
650     *  @private
651     */
652    private var directionChanged:Boolean = false;
653
654    [Inspectable(defaultValue="horizontal")]
655
656    /**
657     *  The orientation of the slider control.
658     *  Valid values in MXML are <code>"horizontal"</code> or <code>"vertical"</code>.
659     *
660     *  <p>In ActionScript, you use the following constants
661     *  to set this property:
662     *  <code>SliderDirection.VERTICAL</code> and
663     *  <code>SliderDirection.HORIZONTAL</code>.</p>
664     *
665     *  The HSlider and VSlider controls set this property for you;
666     *  do not set it when using those controls.
667     *
668     *  @default SliderDirection.HORIZONTAL
669     *  @see mx.controls.sliderClasses.SliderDirection
670     */
671    public function get direction():String
672    {
673        return _direction;
674    }
675
676    /**
677     *  @private
678     */
679    public function set direction(value:String):void
680    {
681        _direction = value;
682        directionChanged = true;
683
684        invalidateProperties();
685        invalidateSize();
686        invalidateDisplayList();
687    }
688
689    //----------------------------------
690    //  labels
691    //----------------------------------
692
693    /**
694     *  @private
695     */
696    private var _labels:Array = [];
697
698    /**
699     *  @private
700     */
701    private var labelsChanged:Boolean = false;
702
703    [Inspectable(category="General", arrayType="String", defaultValue="undefined")]
704
705    /**
706     *  An array of strings used for the slider labels.
707     *  Flex positions the labels at the beginning of the track,
708     *  and spaces them evenly between the beginning of the track
709     *  and the end of the track.
710     *
711     *  <p>For example, if the array contains three items,
712     *  the first item is placed at the beginning of the track,
713     *  the second item in the middle, and the last item
714     *  at the end of the track.</p>
715     *
716     *  <p>If only one label is specified, it is placed at the
717     *  beginning of the track.
718     *  By default, labels are placed above the tick marks
719     *  (if present) or above  the track.
720     *  To align the labels with the tick marks, make sure that
721     *  the number of tick marks is equal to the number of labels.</p>
722     *
723     *  @default undefined
724     */
725    public function get labels():Array
726    {
727        return _labels;
728    }
729
730    /**
731     *  @private
732     */
733    public function set labels(value:Array):void
734    {
735        _labels = value;
736        labelsChanged = true;
737
738        invalidateProperties();
739        invalidateSize();
740        invalidateDisplayList();
741    }
742
743    //----------------------------------
744    //  liveDragging
745    //----------------------------------
746
747    [Inspectable(category="General", defaultValue="false")]
748
749    /**
750     *  Specifies whether live dragging is enabled for the slider.
751     *  If <code>false</code>, Flex sets the <code>value</code> and
752     *  <code>values</code> properties and dispatches the <code>change</code>
753     *  event when the user stops dragging the slider thumb.
754     *  If <code>true</code>,  Flex sets the <code>value</code> and
755     *  <code>values</code> properties and dispatches the <code>change</code>
756     *  event continuously as the user moves the thumb.
757     *
758     *  @default false
759     */
760    public var liveDragging:Boolean = false;
761
762    //----------------------------------
763    //  maximum
764    //----------------------------------
765
766    /**
767     *  @private
768     *  Storage for the maximum property.
769     */
770    private var _maximum:Number = 10;
771
772    [Inspectable(category="General", defaultValue="10")]
773
774    /**
775     *  The maximum allowed value on the slider.
776     *
777     *  @default  10
778     */
779    public function get maximum():Number
780    {
781        return _maximum;
782    }
783
784    /**
785     *  @private
786     */
787    public function set maximum(value:Number):void
788    {
789        _maximum = value;
790        ticksChanged = true;
791        if (!initValues)
792            valuesChanged = true;
793
794        invalidateProperties();
795        invalidateDisplayList();
796    }
797
798    //----------------------------------
799    //  minimum
800    //----------------------------------
801
802    /**
803     *  @private
804     *  Storage for the minimum property.
805     */
806    private var _minimum:Number = 0;
807
808    /**
809     *  @private
810     */
811    private var minimumSet:Boolean = false;
812
813    [Inspectable(category="General", defaultValue="0")]
814
815    /**
816     *  The minimum allowed value on the slider control.
817     *
818     *  @default 0
819     */
820    public function get minimum():Number
821    {
822        return _minimum;
823    }
824
825    /**
826     *  @private
827     */
828    public function set minimum(value:Number):void
829    {
830        _minimum = value;
831        ticksChanged = true;
832
833        if (!initValues)
834            valuesChanged = true;
835
836        invalidateProperties();
837        invalidateDisplayList();
838    }
839
840    //----------------------------------
841    //  showDataTip
842    //----------------------------------
843
844    [Inspectable(category="General", defaultValue="true")]
845
846    /**
847     *  If set to <code>true</code>, show a data tip during user interaction
848     *  containing the current value of the slider.
849     *
850     *  @default true
851     */
852    public var showDataTip:Boolean = true;
853
854    //----------------------------------
855    //  sliderThumbClass
856    //----------------------------------
857
858    /**
859     *  @private
860     */
861    private var _thumbClass:Class = SliderThumb;
862
863    /**
864     *  A reference to the class to use for each thumb.
865     *
866     *  @default SliderThumb
867     */
868    public function get sliderThumbClass():Class
869    {
870        return _thumbClass;
871    }
872
873    /**
874     *  @private
875     */
876    public function set sliderThumbClass(value:Class):void
877    {
878        _thumbClass = value;
879        thumbsChanged = true;
880
881        invalidateProperties();
882        invalidateDisplayList();
883    }
884
885    //----------------------------------
886    //  sliderDataTipClass
887    //----------------------------------
888
889    /**
890     *  @private
891     */
892    private var _sliderDataTipClass:Class = SliderDataTip;
893
894    /**
895     *  A reference to the class to use for the data tip.
896     *
897     *  @default SliderDataTip
898     */
899    public function get sliderDataTipClass():Class
900    {
901        return _sliderDataTipClass;
902    }
903
904    /**
905     *  @private
906     */
907    public function set sliderDataTipClass(value:Class):void
908    {
909        _sliderDataTipClass = value;
910
911        invalidateProperties();
912    }
913
914    //----------------------------------
915    //  snapInterval
916    //----------------------------------
917
918    /**
919     *  @private
920     */
921    private var _snapInterval:Number = 0;
922
923    /**
924     *  @private
925     */
926    private var snapIntervalPrecision:int = -1;
927
928    /**
929     *  @private
930     */
931    private var snapIntervalChanged:Boolean = false;
932
933    [Inspectable(category="General", defaultValue="0")]
934
935    /**
936     *  Specifies the increment value of the slider thumb
937     *  as the user moves the thumb.
938     *  For example, if <code>snapInterval</code> is 2,
939     *  the <code>minimum</code> value is 0,
940     *  and the <code>maximum</code> value is 10,
941     *  the thumb snaps to the values 0, 2, 4, 6, 8, and 10
942     *  as the user move the thumb.
943     *  A value of 0, means that the slider moves continuously
944     *  between the <code>minimum</code> and <code>maximum</code> values.
945     *
946     *  @default 0
947     */
948    public function get snapInterval():Number
949    {
950        return _snapInterval;
951    }
952
953    /**
954     *  @private
955     */
956    public function set snapInterval(value:Number):void
957    {
958        _snapInterval = value;
959
960        var parts:Array = (new String(1 + value)).split(".");
961        if (parts.length == 2)
962            snapIntervalPrecision = parts[1].length;
963        else
964            snapIntervalPrecision = -1;
965
966        if (!isNaN(value) && value != 0)
967        {
968            snapIntervalChanged = true;
969
970            invalidateProperties();
971            invalidateDisplayList();
972        }
973    }
974
975    //----------------------------------
976    //  thumbCount
977    //----------------------------------
978
979    /**
980     *  @private
981     *  Storage for the thumbCount property.
982     */
983    private var _thumbCount:int = 1;
984
985    [Inspectable(category="General", defaultValue="1")]
986
987    /**
988     *  The number of thumbs allowed on the slider.
989     *  Possible values are 1 or 2.
990     *  If set to 1, then the <code>value</code> property contains
991     *  the current value of the slider.
992     *  If set to 2, then the <code>values</code> property contains
993     *  an array of values representing the value for each thumb.
994     *
995     *  @default 1
996     */
997    public function get thumbCount():int
998    {
999        return _thumbCount;
1000    }
1001
1002    /**
1003     *  @private
1004     */
1005    public function set thumbCount(value:int):void
1006    {
1007        var numThumbs:int = (value > 2) ? 2 : value;
1008        numThumbs = value < 1 ? 1 : value;
1009
1010        if (numThumbs != _thumbCount)
1011        {
1012            _thumbCount =  numThumbs;
1013            thumbsChanged = true;
1014
1015            initValues = true;
1016
1017            invalidateProperties();
1018            invalidateDisplayList();
1019        }
1020    }
1021
1022    //----------------------------------
1023    //  thumbStyleFilters
1024    //----------------------------------
1025
1026    /**
1027     *  Set of styles to pass from the Slider to the thumbs.
1028     *  @see mx.styles.StyleProxy
1029     */
1030    protected function get thumbStyleFilters():Object
1031    {
1032        return null;
1033    }
1034    //----------------------------------
1035    //  tickInterval
1036    //----------------------------------
1037
1038    /**
1039     *  @private
1040     */
1041    private var _tickInterval:Number = 0;
1042
1043    [Inspectable(category="General", defaultValue="0")]
1044
1045    /**
1046     *  The spacing of the tick marks relative to the <code>maximum</code> value
1047     *  of the control.
1048     *  Flex displays tick marks whenever you set the <code>tickInterval</code>
1049     *  property to a nonzero value.
1050     *
1051     *  <p>For example, if <code>tickInterval</code> is 1 and
1052     *  <code>maximum</code> is 10,  then a tick mark is placed at each
1053     *  1/10th interval along the slider.
1054     *  A value of 0 shows no tick marks. If the <code>tickValues</code> property
1055     *  is set to a non-empty Array, then this property is ignored.</p>
1056     *
1057     *  @default 0
1058     */
1059    public function get tickInterval():Number
1060    {
1061        return _tickInterval;
1062    }
1063
1064    /**
1065     *  @private
1066     */
1067    public function set tickInterval(value:Number):void
1068    {
1069        _tickInterval = value;
1070        ticksChanged = true;
1071
1072        invalidateProperties();
1073        invalidateDisplayList();
1074    }
1075
1076
1077    //----------------------------------
1078    //  tickValues
1079    //----------------------------------
1080
1081    /**
1082     *  @private
1083     */
1084    private var _tickValues:Array = [];
1085
1086    [Inspectable(category="General", defaultValue="undefined", arrayType="Number")]
1087
1088    /**
1089     *  The positions of the tick marks on the slider. The positions correspond
1090     *  to the values on the slider and should be between
1091     *  the <code>minimum</code> and <code>maximum</code> values.
1092     *  For example, if the <code>tickValues</code> property
1093     *  is [0, 2.5, 7.5, 10] and <code>maximum</code> is 10, then a tick mark is placed
1094     *  in the following positions along the slider: the beginning of the slider,
1095     *  1/4 of the way in from the left,
1096     *  3/4 of the way in from the left, and at the end of the slider.
1097     *
1098     *  <p>If this property is set to a non-empty Array, then the <code>tickInterval</code> property
1099     *  is ignored.</p>
1100     *
1101     *  @default undefined
1102     */
1103    public function get tickValues():Array
1104    {
1105        return _tickValues;
1106    }
1107
1108    /**
1109     *  @private
1110     */
1111    public function set tickValues(value:Array):void
1112    {
1113        _tickValues = value;
1114        ticksChanged = true;
1115
1116        invalidateProperties();
1117        invalidateDisplayList();
1118    }
1119
1120    //----------------------------------
1121    //  value
1122    //----------------------------------
1123
1124    [Bindable("change")]
1125    [Bindable("valueCommit")]
1126    [Inspectable(category="General", defaultValue="undefined")]
1127
1128    /**
1129     *  Contains the position of the thumb, and is a number between the
1130     *  <code>minimum</code> and <code>maximum</code> properties.
1131     *  Use the <code>value</code> property when <code>thumbCount</code> is 1.
1132     *  When <code>thumbCount</code> is greater than 1, use the
1133     *  <code>values</code> property instead.
1134     *  The default value is equal to the minimum property.
1135     */
1136    public function get value():Number
1137    {
1138        return _values[0];
1139    }
1140
1141    /**
1142     *  @private
1143     */
1144    public function set value(val:Number):void
1145    {
1146        setValueAt(val, 0, true);
1147        valuesChanged = true;
1148        minimumSet = true;
1149
1150        invalidateProperties();
1151        invalidateDisplayList();
1152
1153        dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
1154    }
1155
1156    //----------------------------------
1157    //  values
1158    //----------------------------------
1159
1160    /**
1161     *  @private
1162     */
1163    private var _values:Array = [ 0, 0 ];
1164
1165    /**
1166     *  @private
1167     */
1168    private var valuesChanged:Boolean = false;
1169
1170    [Bindable("change")]
1171    [Inspectable(category="General", arrayType="Number")]
1172
1173    /**
1174     *  An array of values for each thumb when <code>thumbCount</code>
1175     *  is greater than 1.
1176     */
1177    public function get values():Array
1178    {
1179        return _values;
1180    }
1181
1182    /**
1183     *  @private
1184     */
1185    public function set values(value:Array):void
1186    {
1187        _values = value;
1188        valuesChanged = true;
1189        minimumSet = true;
1190
1191        invalidateProperties();
1192        invalidateDisplayList();
1193    }
1194
1195    //--------------------------------------------------------------------------
1196    //
1197    //  Overridden methods
1198    //
1199    //--------------------------------------------------------------------------
1200
1201    /**
1202     *  @private
1203     */
1204    override protected function initializeAccessibility():void
1205    {
1206        if (Slider.createAccessibilityImplementation != null)
1207            Slider.createAccessibilityImplementation(this);
1208    }
1209
1210    /**
1211     *  @private
1212     */
1213    override protected function createChildren():void
1214    {
1215        super.createChildren();
1216
1217        if (!innerSlider)
1218        {
1219            innerSlider = new UIComponent();
1220            UIComponent(innerSlider).tabChildren = true;
1221            addChild(innerSlider);
1222        }
1223
1224        createBackgroundTrack();
1225
1226        if (!trackHitArea)
1227        {
1228            trackHitArea = new UIComponent();
1229            innerSlider.addChild(trackHitArea); // trackHitArea should always be on top
1230
1231            trackHitArea.addEventListener(MouseEvent.MOUSE_DOWN,
1232                                           track_mouseDownHandler);
1233        }
1234
1235        invalidateProperties(); // Force commitProperties to be called on instantiation
1236    }
1237
1238    /**
1239     *  @private
1240     */
1241    override public function styleChanged(styleProp:String):void
1242    {
1243        var anyStyle:Boolean = styleProp == null || styleProp == "styleName";
1244
1245        super.styleChanged(styleProp);
1246
1247        if (styleProp == "showTrackHighlight" || anyStyle)
1248        {
1249            trackHighlightChanged = true;
1250            invalidateProperties();
1251        }
1252
1253        if (styleProp == "trackHighlightSkin" || anyStyle)
1254        {
1255            if (innerSlider && highlightTrack)
1256            {
1257                innerSlider.removeChild(DisplayObject(highlightTrack));
1258                highlightTrack = null;
1259            }
1260            trackHighlightChanged = true;
1261            invalidateProperties();
1262        }
1263
1264        if (styleProp == "labelStyleName" || anyStyle)
1265        {
1266            labelStyleChanged = true;
1267            invalidateProperties();
1268        }
1269
1270        if (styleProp == "trackMargin" || anyStyle)
1271        {
1272            invalidateSize();
1273        }
1274
1275        if (styleProp == "trackSkin" || anyStyle)
1276        {
1277            if (track)
1278            {
1279                innerSlider.removeChild(DisplayObject(track));
1280                track = null;
1281                createBackgroundTrack();
1282            }
1283        }
1284
1285        invalidateDisplayList();
1286    }
1287
1288    /**
1289     *  @private
1290     */
1291    override protected function commitProperties():void
1292    {
1293        super.commitProperties();
1294
1295        var n:int;
1296        var i:int;
1297
1298        if (trackHighlightChanged)
1299        {
1300            trackHighlightChanged = false;
1301
1302            if (getStyle("showTrackHighlight"))
1303            {
1304                createHighlightTrack();
1305            }
1306            else if (highlightTrack)
1307            {
1308                innerSlider.removeChild(DisplayObject(highlightTrack));
1309                highlightTrack = null;
1310            }
1311        }
1312
1313        if (directionChanged)
1314        {
1315            directionChanged = false;
1316            var isHorizontal:Boolean = _direction == SliderDirection.HORIZONTAL;
1317
1318            if (isHorizontal)
1319            {
1320                DisplayObject(innerSlider).rotation = 0;
1321            }
1322            else
1323            {
1324                DisplayObject(innerSlider).rotation = -90;
1325                innerSlider.y = unscaledHeight;
1326            }
1327
1328            if (labelObjects)
1329            {
1330                for (var labelIndex:int = labelObjects.numChildren - 1; labelIndex >= 0; labelIndex--)
1331                {
1332                    var labelObj:SliderLabel = SliderLabel(
1333                        labelObjects.getChildAt(labelIndex));
1334                    labelObj.rotation = isHorizontal ? 0 : 90;
1335                }
1336            }
1337        }
1338
1339        if (labelStyleChanged && !labelsChanged)
1340        {
1341            labelStyleChanged = false;
1342
1343            if (labelObjects)
1344            {
1345                var labelStyleName:String = getStyle("labelStyleName");
1346                n = labelObjects.numChildren;
1347                for (i = 0; i < n; i++)
1348                {
1349                    ISimpleStyleClient(labelObjects.getChildAt(i)).styleName = labelStyleName;
1350                }
1351            }
1352        }
1353
1354        if (ticksChanged)
1355        {
1356            ticksChanged = false;
1357
1358            createTicks();
1359        }
1360
1361        if (labelsChanged)
1362        {
1363            labelsChanged = false;
1364
1365            createLabels();
1366        }
1367
1368        if (thumbsChanged)
1369        {
1370            thumbsChanged = false;
1371
1372            createThumbs();
1373        }
1374
1375
1376        if (initValues)
1377        {
1378            initValues = false;
1379
1380            if (!valuesChanged)
1381            {
1382                var val:Number = minimum;
1383
1384                n = _thumbCount;
1385                for (i = 0; i < n; i++)
1386                {
1387                    _values[i] = val;
1388                    setValueAt(val, i);
1389                    if (_snapInterval && _snapInterval != 0)
1390                        val += snapInterval;
1391                    else
1392                        val++;
1393                }
1394
1395                snapIntervalChanged = false;
1396            }
1397        }
1398
1399        if (snapIntervalChanged)
1400        {
1401            snapIntervalChanged = false;
1402
1403            if (!valuesChanged)
1404            {
1405                n = thumbs.numChildren;
1406                for (i = 0; i < n; i++)
1407                {
1408                    setValueAt(getValueFromX(SliderThumb(thumbs.getChildAt(i)).xPosition), i);
1409                }
1410            }
1411        }
1412
1413
1414
1415        if (valuesChanged)
1416        {
1417            valuesChanged = false;
1418
1419            n = _thumbCount;
1420            for (i = 0; i < n; i++)
1421            {
1422                setValueAt(getValueFromX(getXFromValue(Math.min(Math.max(values[i], minimum), maximum))), i);
1423            }
1424        }
1425
1426        if (enabledChanged)
1427        {
1428            enabledChanged = false;
1429
1430            n = thumbs.numChildren;
1431            for (i = 0; i < n; i++)
1432            {
1433                SliderThumb(thumbs.getChildAt(i)).enabled = _enabled;
1434            }
1435
1436            n = labelObjects ? labelObjects.numChildren : 0;
1437            for (i = 0; i < n; i++)
1438            {
1439                SliderLabel(labelObjects.getChildAt(i)).enabled = _enabled;
1440            }
1441        }
1442
1443        if (tabIndexChanged)
1444        {
1445            tabIndexChanged = false;
1446
1447            n = thumbs.numChildren;
1448            for (i = 0; i < n; i++)
1449            {
1450                SliderThumb(thumbs.getChildAt(i)).tabIndex = _tabIndex;
1451            }
1452        }
1453    }
1454
1455    /**
1456     *  Calculates the amount of space that the component takes up.
1457     *  A horizontal slider control calculates its height by examining
1458     *  the position of its labels, tick marks, and thumbs
1459     *  relative to the track.
1460     *  The height of the control is equivalent to the position
1461     *  of the bottom of the lowest element subtracted
1462     *  from the position of the top of the highest element.
1463     *  The width of a horizontal slider control defaults to 250 pixels.
1464     *  For a vertical slider control, the width and the length
1465     *  measurements are reversed.
1466     */
1467    override protected function measure():void
1468    {
1469        super.measure();
1470
1471        var isHorizontal:Boolean = (direction == SliderDirection.HORIZONTAL);
1472        var numLabels:int = labelObjects ? labelObjects.numChildren : 0;
1473        var trackMargin:Number = getStyle("trackMargin");
1474        var length:Number = DEFAULT_MEASURED_WIDTH;
1475
1476        if (!isNaN(trackMargin))
1477        {
1478            if (numLabels > 0)
1479            {
1480                length += (isHorizontal ?
1481                           SliderLabel(labelObjects.getChildAt(0)).getExplicitOrMeasuredWidth() / 2 :
1482                           SliderLabel(labelObjects.getChildAt(0)).getExplicitOrMeasuredHeight() / 2);
1483            }
1484            if (numLabels > 1)
1485            {
1486                length += (isHorizontal ?
1487                           SliderLabel(labelObjects.getChildAt(numLabels - 1)).getExplicitOrMeasuredWidth() / 2 :
1488                           SliderLabel(labelObjects.getChildAt(numLabels - 1)).getExplicitOrMeasuredHeight() / 2);
1489            }
1490            //length += track.width;
1491        }
1492
1493        var bounds:Object = getComponentBounds();
1494        var thickness:Number = bounds.lower - bounds.upper;
1495
1496        measuredMinWidth = measuredWidth = isHorizontal ? length : thickness;
1497        measuredMinHeight = measuredHeight = isHorizontal ? thickness : length;
1498    }
1499
1500    /**
1501     *  Positions the elements of the control.
1502     *  The track, thumbs, labels, and tick marks are all positioned
1503     *  and sized by this method.
1504     *  The track is sized based on the length of the labels and on the track margin.
1505     *  If you specify a <code>trackMargin</code>, then the size of the track
1506     *  is equal to the available width minus the <code>trackMargin</code> times 2.
1507     *
1508     *  <p>Tick marks are spaced at even intervals along the track starting from the beginning of the track. An additional tick mark is placed
1509     *  at the end of the track if one doesn't already exist (if the tick interval isn't a multiple of the maximum value). The tick mark
1510     *  y-position is based on the <code>tickOffset</code>. An offset of 0 places the bottom of the tick at the top of the track. Negative offsets
1511     *  move the ticks upwards while positive offsets move them downward through the track.</p>
1512     *
1513     *  <p>Labels are positioned at even intervals along the track. The labels are always horizontally centered above their
1514     *  interval position unless the <code>trackMargin</code> setting is too small. If you specify a <code>trackMargin</code>, then the first and last labels will
1515     *  position themselves at the left and right borders of the control. Labels will not crop or resize themselves if they overlap,
1516     *  so be sure to allow enough space for them to fit on the track. The y-position is based on the <code>labelOffset</code> property. An offset of 0
1517     *  places the bottom of the label at the top of the track. Unlike tick marks, the labels can not be positioned to overlap the track.
1518     *  If the offset is a positive number, then the top of the label will be positioned below the bottom of the track.</p>
1519     *
1520     *  <p>The thumbs are positioned to overlap the track. Their x-position is determined by their value. The y-position is
1521     *  controlled by the <code>thumbOffset</code> property. An offset of 0 places the center of the thumb at the center of the track. A negative
1522     *  offset moves the thumbs upwards while a positive offset moves the thumbs downwards.</p>
1523     *
1524     *  <p>The placement of the tick marks, labels and thumbs are all independent from each other. They will not attempt to reposition
1525     *  themselves if they overlap.</p>
1526     *
1527     *  <p>For a vertical slider control, the same rules apply. In the above description, substitute width for height, height for width,
1528     *  left for up or top, right for down or bottom, x-position for y-position, and y-position for x-position.</p>
1529     *
1530     *  @param unscaledWidth Specifies the width of the component, in pixels,
1531     *  in the component's coordinates, regardless of the value of the
1532     *  <code>scaleX</code> property of the component.
1533     *
1534     *  @param unscaledHeight Specifies the height of the component, in pixels,
1535     *  in the component's coordinates, regardless of the value of the
1536     *  <code>scaleY</code> property of the component.
1537     */
1538    override protected function updateDisplayList(unscaledWidth:Number,
1539                                                  unscaledHeight:Number):void
1540    {
1541        super.updateDisplayList(unscaledWidth, unscaledHeight);
1542
1543//      graphics.beginFill(0xEEEEEE);
1544//      graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
1545//      graphics.endFill();
1546
1547        var isHorizontal:Boolean = (_direction == SliderDirection.HORIZONTAL);
1548        var numLabels:int = labelObjects ? labelObjects.numChildren : 0;
1549        var numThumbs:int = thumbs ? thumbs.numChildren : 0;
1550        var trackMargin:Number = getStyle("trackMargin");
1551        var widestThumb:Number = 6;
1552        var firstThumb:SliderThumb = SliderThumb(thumbs.getChildAt(0));
1553        if (thumbs && firstThumb)
1554            widestThumb = firstThumb.getExplicitOrMeasuredWidth();
1555
1556        var trackLeftOffset:Number = widestThumb / 2; // Enough space for the thumb to rest at the edges
1557        var trackRightOffset:Number = trackLeftOffset;
1558
1559        var availSpace:Number;
1560
1561        var firstLabelSize:Number = 0;
1562        if (numLabels > 0)
1563        {
1564            var firstLabel:SliderLabel =
1565                SliderLabel(labelObjects.getChildAt(0));
1566
1567            firstLabelSize = isHorizontal ?
1568                             firstLabel.getExplicitOrMeasuredWidth() :
1569                             firstLabel.getExplicitOrMeasuredHeight();
1570        }
1571
1572        var lastLabelSize:Number = 0;
1573        if (numLabels > 1)
1574        {
1575            var lastLabel:SliderLabel =
1576                SliderLabel(labelObjects.getChildAt(numLabels - 1));
1577            lastLabelSize = isHorizontal ?
1578                            lastLabel.getExplicitOrMeasuredWidth():
1579                            lastLabel.getExplicitOrMeasuredHeight();
1580        }
1581
1582        if (!isNaN(trackMargin))
1583            availSpace = trackMargin;
1584        else
1585            availSpace = (firstLabelSize + lastLabelSize) / 2;
1586
1587        if (numLabels > 0)
1588        {
1589            if (!isNaN(trackMargin))
1590            {
1591                trackLeftOffset = Math.max(trackLeftOffset,
1592                                           availSpace / (numLabels > 1 ? 2 : 1));
1593            }
1594            else
1595            {
1596                trackLeftOffset = Math.max(trackLeftOffset, firstLabelSize / 2);
1597            }
1598        }
1599        else
1600        {
1601
1602            trackLeftOffset = Math.max(trackLeftOffset, availSpace / 2);
1603        }
1604
1605        var bounds:Object = getComponentBounds();
1606
1607        //track.x = Math.round(trackLeftOffset);
1608
1609        var trackY:Number = (((isHorizontal ? unscaledHeight : unscaledWidth) -
1610            (Number(bounds.lower) - Number(bounds.upper))) / 2) - Number(bounds.upper);
1611
1612        track.move(Math.round(trackLeftOffset), Math.round(trackY));
1613        track.setActualSize((isHorizontal ? unscaledWidth: unscaledHeight) - (trackLeftOffset * 2), track.height);
1614
1615        // Layout the thumbs' y positions.
1616        var tY:Number = track.y +
1617                        (track.height - firstThumb.getExplicitOrMeasuredHeight()) / 2 +
1618                        getStyle("thumbOffset");
1619
1620        var n:int = _thumbCount;
1621        for (var i:int = 0; i < n; i++)
1622        {
1623            var currentThumb:SliderThumb = SliderThumb(thumbs.getChildAt(i));
1624            currentThumb.move(currentThumb.x, tY);
1625            currentThumb.visible = true;
1626            currentThumb.setActualSize(currentThumb.getExplicitOrMeasuredWidth(),
1627                                 currentThumb.getExplicitOrMeasuredHeight());
1628        }
1629
1630        var g:Graphics = trackHitArea.graphics;
1631
1632        var tLength:Number = 0
1633        if (_tickInterval > 0 || (_tickValues && _tickValues.length > 0))
1634            tLength = getStyle("tickLength");
1635        g.clear();
1636        g.beginFill(0,0);
1637        var fullThumbHeight:Number = firstThumb.getExplicitOrMeasuredHeight();
1638        var halfThumbHeight:Number = (!fullThumbHeight) ? 0 : (fullThumbHeight / 2);
1639        g.drawRect(track.x,
1640                track.y - halfThumbHeight - tLength,
1641                track.width,
1642                track.height + fullThumbHeight + tLength);
1643        g.endFill();
1644
1645        if (_direction != SliderDirection.HORIZONTAL)
1646            innerSlider.y = unscaledHeight;
1647
1648        layoutTicks();
1649
1650        layoutLabels();
1651
1652        setPosFromValue(); // use the value to position the thumb's x
1653
1654        drawTrackHighlight();
1655    }
1656
1657    //--------------------------------------------------------------------------
1658    //
1659    //  Methods
1660    //
1661    //--------------------------------------------------------------------------
1662
1663    /**
1664     *  @private
1665     */
1666    private function createBackgroundTrack():void
1667    {
1668        if (!track)
1669        {
1670            var trackSkinClass:Class = getStyle("trackSkin");
1671
1672            track = new trackSkinClass();
1673
1674            if (track is ISimpleStyleClient)
1675                ISimpleStyleClient(track).styleName = this;
1676
1677            innerSlider.addChildAt(DisplayObject(track),0);
1678        }
1679    }
1680
1681    /**
1682     *  @private
1683     */
1684    private function createHighlightTrack():void
1685    {
1686        var showTrackHighlight:Boolean = getStyle("showTrackHighlight");
1687        if (!highlightTrack && showTrackHighlight)
1688        {
1689            var trackHighlightClass:Class =
1690                getStyle("trackHighlightSkin");
1691            highlightTrack = new trackHighlightClass();
1692
1693            if (highlightTrack is ISimpleStyleClient)
1694                ISimpleStyleClient(highlightTrack).styleName = this;
1695
1696            innerSlider.addChildAt(DisplayObject(highlightTrack),
1697                innerSlider.getChildIndex(DisplayObject(track)) + 1);
1698        }
1699    }
1700
1701    /**
1702     *  @private
1703     */
1704    private function createThumbs():void
1705    {
1706        var n:int;
1707        var i:int;
1708
1709        // Delete all thumb children
1710        if (thumbs)
1711        {
1712            n = thumbs.numChildren;
1713            for (i = n - 1; i >= 0; i--)
1714            {
1715                // we don't need to bother to remove the event listeners here
1716                // they will be removed by the garbage collector automatically
1717                thumbs.removeChildAt(i);
1718            }
1719        }
1720        else
1721        {
1722            thumbs = new UIComponent();
1723            thumbs.tabChildren = true;
1724            thumbs.tabEnabled = false;
1725            innerSlider.addChild(thumbs);
1726        }
1727
1728        var thumb:SliderThumb;  // We want to force the thumb to be a subclass of SliderThumb
1729
1730        n = _thumbCount;
1731        for (i = 0; i < n; i++)
1732        {
1733            thumb = SliderThumb(new _thumbClass());
1734
1735            thumb.owner = this;
1736            thumb.styleName = new StyleProxy(this, thumbStyleFilters);
1737            thumb.thumbIndex = i;
1738            thumb.visible = true;
1739            thumb.enabled = enabled;
1740
1741            thumb.upSkinName = "thumbUpSkin";
1742            thumb.downSkinName = "thumbDownSkin";
1743            thumb.disabledSkinName = "thumbDisabledSkin";
1744            thumb.overSkinName = "thumbOverSkin";
1745            thumb.skinName = "thumbSkin";
1746
1747            thumbs.addChild(thumb);
1748
1749            thumb.addEventListener(FocusEvent.FOCUS_IN,
1750                                   thumb_focusInHandler);
1751            thumb.addEventListener(FocusEvent.FOCUS_OUT,
1752                                   thumb_focusOutHandler);
1753        }
1754    }
1755
1756    /**
1757     *  @private
1758     */
1759    private function createLabels():void
1760    {
1761        var labelObj:SliderLabel;
1762
1763        if (labelObjects)
1764        {
1765            for (var i:int = labelObjects.numChildren - 1; i >= 0; i--)
1766            {
1767                labelObjects.removeChildAt(i);
1768            }
1769        }
1770        else
1771        {
1772            labelObjects = new UIComponent();
1773            innerSlider.addChildAt(labelObjects, innerSlider.getChildIndex(trackHitArea));
1774        }
1775
1776        if (_labels)
1777        {
1778            var numLabels:int = _labels.length;
1779
1780            for (var j:int = 0; j < numLabels; j++)
1781            {
1782                labelObj = new SliderLabel();
1783
1784                labelObj.text = _labels[j] is String ?
1785                                _labels[j] :
1786                                _labels[j].toString();
1787
1788                if (_direction != SliderDirection.HORIZONTAL)
1789                    labelObj.rotation = 90;
1790
1791                var labelStyleName:String = getStyle("labelStyleName");
1792                if (labelStyleName)
1793                    labelObj.styleName = labelStyleName;
1794
1795                labelObjects.addChild(labelObj);
1796            }
1797        }
1798    }
1799
1800    /**
1801     *  @private
1802     */
1803    private function createTicks():void
1804    {
1805        if (!ticks)
1806        {
1807            ticks = new UIComponent();
1808
1809            innerSlider.addChild(ticks);
1810        }
1811    }
1812
1813    /**
1814     *  @private
1815     */
1816    private function getComponentBounds():Object
1817    {
1818        var isHorizontal:Boolean = (direction == SliderDirection.HORIZONTAL);
1819        var numLabels:int = labelObjects ? labelObjects.numChildren : 0;
1820        var labelY:Number;
1821        var labelSize:Number = 0;
1822        var thumbY:Number;
1823        var upperBound:Number = 0;
1824        var lowerBound:Number = track.height;
1825
1826        if (numLabels > 0)
1827        {
1828            var sliderLabel:SliderLabel =
1829                SliderLabel(labelObjects.getChildAt(0));
1830
1831            if (isHorizontal)
1832            {
1833                labelSize = sliderLabel.getExplicitOrMeasuredHeight();
1834            }
1835            else
1836            {
1837                for (var i:int = 0; i < numLabels; i++)
1838                {
1839                    sliderLabel = SliderLabel(labelObjects.getChildAt(i));
1840                    labelSize = Math.max(labelSize,
1841                        sliderLabel.getExplicitOrMeasuredWidth());
1842                }
1843            }
1844
1845            var labOffset:Number = getStyle("labelOffset");
1846            labelY = labOffset - (labOffset > 0 ? 0 : labelSize);
1847
1848            upperBound = Math.min(upperBound, labelY);
1849            lowerBound = Math.max(lowerBound, labOffset + (labOffset > 0 ? labelSize : 0));
1850        }
1851
1852        if (ticks)
1853        {
1854            var tLen:Number = getStyle("tickLength");
1855            var tOff:Number = getStyle("tickOffset");
1856
1857            upperBound = Math.min(upperBound, tOff - tLen);
1858            lowerBound = Math.max(lowerBound, tOff);
1859        }
1860
1861        if (thumbs.numChildren > 0)
1862        {
1863            thumbY = (track.height - SliderThumb(thumbs.getChildAt(0)).getExplicitOrMeasuredHeight()) / 2 +
1864                     getStyle("thumbOffset");
1865
1866            upperBound = Math.min(upperBound, thumbY);
1867            lowerBound = Math.max(lowerBound, thumbY + SliderThumb(thumbs.getChildAt(0)).getExplicitOrMeasuredHeight());
1868        }
1869
1870        return { lower: lowerBound, upper: upperBound };
1871    }
1872
1873    /**
1874     *  @private
1875     */
1876    private function layoutTicks():void
1877    {
1878        if (ticks)
1879        {
1880            var g:Graphics = ticks.graphics;
1881            var tLength:Number = getStyle("tickLength");
1882            var tOffset:Number = getStyle("tickOffset");
1883            var tickWidth:Number = getStyle("tickThickness");
1884            var xPos:Number;
1885            var tColor:Number = getStyle("tickColor");
1886
1887            var usePositions:Boolean = _tickValues && _tickValues.length > 0 ? true : false;
1888            var positionIndex:int = 0;
1889            var val:Number = usePositions ? _tickValues[positionIndex++] : minimum;
1890
1891            g.clear();
1892
1893            if (_tickInterval > 0 || usePositions)
1894            {
1895                g.lineStyle(tickWidth,tColor,100);
1896
1897                do
1898                {
1899                    xPos = Math.round(getXFromValue(val));
1900                    g.moveTo(xPos, tLength);
1901                    g.lineTo(xPos, 0);
1902                    val = usePositions ? (positionIndex < _tickValues.length ? _tickValues[positionIndex++] : NaN) : _tickInterval + val;
1903                } while (val < maximum || (usePositions && positionIndex < _tickValues.length))
1904
1905                // draw the last tick
1906                if (!usePositions || val == maximum)
1907                {
1908                    xPos = track.x + track.width - 1;
1909                    g.moveTo(xPos, tLength);
1910                    g.lineTo(xPos, 0);
1911                }
1912
1913                ticks.y = Math.round(track.y + tOffset - tLength);
1914            }
1915        }
1916    }
1917
1918    /**
1919     *  @private
1920     */
1921    private function layoutLabels():void
1922    {
1923        var numLabels:Number = labelObjects ? labelObjects.numChildren : 0;
1924        var availSpace:Number;
1925
1926        if (numLabels > 0)
1927        {
1928            var labelInterval:Number = track.width / (numLabels - 1);
1929            // The amount of space we have available for the labels to hang past the track
1930            availSpace = Math.max((_direction == SliderDirection.HORIZONTAL ?
1931                                  unscaledWidth :
1932                                  unscaledHeight) - track.width,
1933                                  SliderThumb(thumbs.getChildAt(0)).getExplicitOrMeasuredWidth());
1934
1935            var labelPos:Number;
1936            var left:Number = track.x;
1937            var curLabel:Object;
1938
1939            for (var i:int = 0; i < numLabels; i++)
1940            {
1941                curLabel = labelObjects.getChildAt(i);
1942                curLabel.setActualSize(curLabel.getExplicitOrMeasuredWidth(), curLabel.getExplicitOrMeasuredHeight());
1943
1944                var yPos:Number = track.y  - curLabel.height + getStyle("labelOffset");
1945
1946                if (_direction == SliderDirection.HORIZONTAL)
1947                {
1948                    labelPos = curLabel.getExplicitOrMeasuredWidth() / 2;
1949
1950                    if (i == 0)
1951                        labelPos = Math.min(labelPos, availSpace / (numLabels > Number(1) ? Number(2) : Number(1)));
1952                    else if (i == (numLabels - 1))
1953                        labelPos = Math.max(labelPos, curLabel.getExplicitOrMeasuredWidth() - availSpace / 2);
1954
1955                    curLabel.move(left - labelPos,yPos);
1956                }
1957                else
1958                {
1959                    var labelOff:Number = getStyle("labelOffset");
1960
1961                    labelPos = curLabel.getExplicitOrMeasuredHeight() / 2;
1962
1963                    if (i == 0)
1964                        labelPos = Math.max(labelPos, curLabel.getExplicitOrMeasuredHeight() - availSpace / (numLabels > Number(1) ? Number(2) : Number(1)));
1965                    else if (i == (numLabels-1))
1966                        labelPos = Math.min(labelPos,availSpace / 2);
1967
1968                    curLabel.move(left + labelPos,track.y + labelOff +
1969                                  (labelOff > 0 ? 0 : -curLabel.getExplicitOrMeasuredWidth()));
1970                }
1971                left += labelInterval;
1972            }
1973        }
1974    }
1975
1976    /**
1977     *  @private
1978     */
1979    mx_internal function drawTrackHighlight():void
1980    {
1981        if (highlightTrack)
1982        {
1983            var xPos:Number;
1984            var tWidth:Number;
1985
1986            var firstThumb:SliderThumb = SliderThumb(thumbs.getChildAt(0));
1987
1988            if (_thumbCount > 1)
1989            {
1990                xPos = firstThumb.xPosition;
1991                var secondThumb:SliderThumb = SliderThumb(thumbs.getChildAt(1));
1992                tWidth = secondThumb.xPosition - firstThumb.xPosition;
1993            }
1994            else
1995            {
1996                xPos = track.x;
1997                tWidth = firstThumb.xPosition - xPos;
1998            }
1999
2000
2001            highlightTrack.move(xPos, track.y + 1);
2002            highlightTrack.setActualSize(tWidth > 0 ? tWidth : 0, highlightTrack.height);
2003        }
2004    }
2005
2006    /**
2007     *  @private
2008     *  Helper function that starts the dataTip and dispatches the press event.
2009     */
2010    mx_internal function onThumbPress(thumb:Object):void
2011    {
2012        if (showDataTip)
2013        {
2014            // Setup number formatter
2015            dataFormatter = new NumberFormatter();
2016            dataFormatter.precision = getStyle("dataTipPrecision");
2017
2018            if (!dataTip)
2019            {
2020                dataTip = SliderDataTip(new sliderDataTipClass());
2021                systemManager.toolTipChildren.addChild(dataTip);
2022
2023                var dataTipStyleName:String = getStyle("dataTipStyleName");
2024                if (dataTipStyleName)
2025                {
2026                    dataTip.styleName = dataTipStyleName;
2027                }
2028            }
2029
2030            var formattedVal:String;
2031            if (_dataTipFormatFunction != null)
2032            {
2033                formattedVal = this._dataTipFormatFunction(
2034                    getValueFromX(thumb.xPosition));
2035            }
2036            else
2037            {
2038                formattedVal = dataFormatter.format(getValueFromX(thumb.xPosition));
2039            }
2040
2041            dataTip.text = formattedVal;
2042
2043            // Tool tip has been freshly created and new text assigned to it.
2044            // Hence force a validation so that we can set the
2045            // size required to show the text completely.
2046            dataTip.validateNow();
2047            dataTip.setActualSize(dataTip.getExplicitOrMeasuredWidth(),dataTip.getExplicitOrMeasuredHeight());
2048            positionDataTip(thumb);
2049        }
2050        keyInteraction = false;
2051
2052        var event:SliderEvent = new SliderEvent(SliderEvent.THUMB_PRESS);
2053        event.value = getValueFromX(thumb.xPosition);;
2054        event.thumbIndex = thumb.thumbIndex;
2055        dispatchEvent(event);
2056    }
2057
2058    /**
2059     *  @private
2060     */
2061    mx_internal function onThumbRelease(thumb:Object):void
2062    {
2063        interactionClickTarget = SliderEventClickTarget.THUMB;
2064
2065        destroyDataTip();
2066
2067        setValueFromPos(thumb.thumbIndex);
2068
2069        dataFormatter = null;
2070
2071        var event:SliderEvent = new SliderEvent(SliderEvent.THUMB_RELEASE);
2072        event.value = getValueFromX(thumb.xPosition);;
2073        event.thumbIndex = thumb.thumbIndex;
2074        dispatchEvent(event);
2075    }
2076
2077    /**
2078     *  @private
2079     */
2080    mx_internal function onThumbMove(thumb:Object):void
2081    {
2082        var value:Number = getValueFromX(thumb.xPosition);
2083
2084        if (showDataTip)
2085        {
2086            dataTip.text = _dataTipFormatFunction != null ?
2087                           _dataTipFormatFunction(value) :
2088                           dataFormatter.format(value);
2089
2090            dataTip.setActualSize(dataTip.getExplicitOrMeasuredWidth(),
2091                                  dataTip.getExplicitOrMeasuredHeight());
2092
2093            positionDataTip(thumb);
2094        }
2095
2096        if (liveDragging)
2097        {
2098            interactionClickTarget = SliderEventClickTarget.THUMB;
2099            setValueAt(value, thumb.thumbIndex);
2100        }
2101
2102        var event:SliderEvent = new SliderEvent(SliderEvent.THUMB_DRAG);
2103        event.value = value;
2104        event.thumbIndex = thumb.thumbIndex;
2105        dispatchEvent(event);
2106    }
2107
2108    /**
2109     *  @private
2110     */
2111    private function positionDataTip(thumb:Object):void
2112    {
2113        var relX:Number;
2114        var relY:Number;
2115
2116        var tX:Number = thumb.x;
2117        var tY:Number = thumb.y;
2118
2119        var tPlacement:String =  getStyle("dataTipPlacement");
2120        var tOffset:Number = getStyle("dataTipOffset");
2121
2122        // Need to special case tooltip position because the tooltip movieclip
2123        // resides in the root movie clip, instead of the Slider movieclip
2124        if (_direction == SliderDirection.HORIZONTAL)
2125        {
2126            relX = tX;
2127            relY = tY;
2128
2129            if (tPlacement == "left")
2130            {
2131                relX -= tOffset + dataTip.width;
2132                relY += (thumb.height - dataTip.height) / 2;
2133            }
2134            else if (tPlacement == "right")
2135            {
2136                relX += tOffset + thumb.width;
2137                relY += (thumb.height - dataTip.height) / 2;
2138            }
2139            else if (tPlacement == "top")
2140            {
2141                relY -= tOffset + dataTip.height;
2142                relX -= (dataTip.width - thumb.width) / 2;
2143            }
2144            else if (tPlacement == "bottom")
2145            {
2146                relY += tOffset + thumb.height;
2147                relX -= (dataTip.width - thumb.width) / 2;
2148            }
2149        }
2150        else
2151        {
2152            relX = tY;
2153            relY = unscaledHeight - tX - (dataTip.height + thumb.width) / 2;
2154
2155            if (tPlacement == "left")
2156            {
2157                relX -= tOffset + dataTip.width;
2158            }
2159            else if (tPlacement == "right")
2160            {
2161                relX += tOffset + thumb.height;
2162            }
2163            else if (tPlacement == "top")
2164            {
2165                relY -= tOffset + (dataTip.height + thumb.width) / 2;
2166                relX -= (dataTip.width - thumb.height) / 2;
2167            }
2168            else if (tPlacement == "bottom")
2169            {
2170                relY += tOffset + (dataTip.height + thumb.width) / 2;
2171                relX -= (dataTip.width - thumb.height) / 2;
2172            }
2173        }
2174
2175        var o:Point = new Point(relX, relY);
2176        var r:Point = localToGlobal(o);
2177		r = dataTip.parent.globalToLocal(r);
2178
2179        dataTip.x = r.x < 0 ? 0 : r.x;
2180        dataTip.y = r.y < 0 ? 0 : r.y;
2181    }
2182
2183    /**
2184     *  @private
2185     */
2186    private function destroyDataTip():void
2187    {
2188        if (dataTip)
2189        {
2190            systemManager.toolTipChildren.removeChild(dataTip);
2191            dataTip = null;
2192        }
2193    }
2194
2195    /**
2196     *  @private
2197     *  Utility for finding the x position which corresponds
2198     *  to the given value.
2199     */
2200    mx_internal function getXFromValue(v:Number):Number
2201    {
2202        var val:Number;
2203
2204        if (v == minimum)
2205            val = track.x;
2206        else if (v == maximum)
2207            val = track.x + track.width;
2208        else
2209            val =  track.x + (v - minimum) * (track.width) / (maximum - minimum);
2210
2211        return val;
2212    }
2213
2214    /**
2215     *  @private
2216     */
2217    mx_internal function getXBounds(selectedThumbIndex:int):Object
2218    {
2219        var maxX:Number = track.x + track.width;
2220        var minX:Number = track.x;
2221        if (allowThumbOverlap)
2222        {
2223            return { max: maxX, min: minX };
2224        }
2225
2226        var minBound:Number = NaN;
2227        var maxBound:Number = NaN;
2228
2229        var prevThumb:SliderThumb =
2230            selectedThumbIndex > 0 ?
2231            SliderThumb(thumbs.getChildAt(selectedThumbIndex - 1)) :
2232            null;
2233
2234        var nextThumb:SliderThumb =
2235            selectedThumbIndex + 1 < thumbs.numChildren ?
2236            SliderThumb(thumbs.getChildAt(selectedThumbIndex + 1)) :
2237            null;
2238
2239        if (prevThumb)
2240            minBound = prevThumb.xPosition + prevThumb.width / 2;
2241
2242        if (nextThumb)
2243            maxBound = nextThumb.xPosition - nextThumb.width / 2;
2244
2245        if (isNaN(minBound))
2246            minBound = minX;
2247        else
2248            minBound = Math.min(Math.max(minX,minBound),maxX);
2249
2250        if (isNaN(maxBound))
2251            maxBound = maxX;
2252        else
2253            maxBound = Math.max(Math.min(maxX, maxBound),minX);
2254
2255        return { max: maxBound, min: minBound };
2256    }
2257
2258    /**
2259     *  @private
2260     *  Utility for positioning the thumb(s) from the current value.
2261     */
2262    private function setPosFromValue():void
2263    {
2264        var n:int = _thumbCount;
2265        for (var i:int = 0; i < n; i++)
2266        {
2267            var thumb:SliderThumb = SliderThumb(thumbs.getChildAt(i));
2268            thumb.xPosition = getXFromValue(values[i]);
2269        }
2270    }
2271
2272    /**
2273     *  @private
2274     *  Utility for getting a value corresponding to a given x.
2275     */
2276    mx_internal function getValueFromX(xPos:Number):Number
2277    {
2278        var v:Number = (xPos - track.x) *
2279                       (maximum - minimum) /
2280                       (track.width) + minimum;
2281
2282        // kill rounding error at the edges.
2283        if (v - minimum <= 0.002)
2284        {
2285            v = minimum;
2286        }
2287        else if (maximum - v <= 0.002)
2288        {
2289            v = maximum;
2290        }
2291        else if (!isNaN(_snapInterval) && _snapInterval != 0)
2292        {
2293            v = Math.round((v - minimum) / _snapInterval) *
2294                _snapInterval + minimum;
2295        }
2296
2297        return v;
2298    }
2299
2300    /**
2301     *  @private
2302     *  Utility for committing a value of a given thumb.
2303     */
2304    private function setValueFromPos(thumbIndex:int):void
2305    {
2306        var thumb:SliderThumb = SliderThumb(thumbs.getChildAt(thumbIndex));
2307        setValueAt(getValueFromX(thumb.xPosition), thumbIndex);
2308    }
2309
2310    /**
2311     *  @private
2312     */
2313    mx_internal function getSnapValue(value:Number, thumb:SliderThumb = null):Number
2314    {
2315        if (!isNaN(_snapInterval) && _snapInterval != 0)
2316        {
2317                var val:Number = getValueFromX(value);
2318
2319                if (thumb && (thumbs.numChildren > 1) && !allowThumbOverlap)
2320                {
2321                    var check_bounds:Boolean = true;
2322                    var bounds:Object
2323
2324                    bounds = getXBounds(thumb.thumbIndex);
2325
2326                    var prevThumb:SliderThumb =
2327                        thumb.thumbIndex > 0 ?
2328                        SliderThumb(thumbs.getChildAt(thumb.thumbIndex- 1)) :
2329                        null;
2330
2331                    var nextThumb:SliderThumb =
2332                        thumb.thumbIndex + 1 < thumbs.numChildren ?
2333                        SliderThumb(thumbs.getChildAt(thumb.thumbIndex + 1)) :
2334                        null;
2335
2336                    if (prevThumb)
2337                    {
2338                        bounds.min -= (prevThumb.width / 2);
2339                        // check if thumb is at minimum, if not we can ignore the bounds logic
2340                        if (val == minimum)
2341                            if (getValueFromX((prevThumb.xPosition - prevThumb.width/2)) != minimum)
2342                                check_bounds = false;
2343                    }
2344                    else
2345                    {
2346                        if (val == minimum)
2347                            check_bounds = false;
2348                    }
2349
2350                    if (nextThumb)
2351                    {
2352                        bounds.max +=  (nextThumb.width / 2);
2353                        // check if thumb is at maximum, if not we can ignore the bounds logic
2354                        if (val == maximum)
2355                            if (getValueFromX((nextThumb.xPosition + nextThumb.width/2)) != maximum)
2356                                check_bounds = false;
2357                    }
2358                    else
2359                    {
2360                        if (val == maximum)
2361                            check_bounds = false;
2362                    }
2363                    if (check_bounds)
2364                        val = Math.min(Math.max(val, getValueFromX(Math.round(bounds.min)) + _snapInterval),
2365                                    getValueFromX(Math.round(bounds.max)) - _snapInterval);
2366
2367                }
2368                return getXFromValue(val);
2369        }
2370
2371       return value;
2372    }
2373
2374    /**
2375     *  @private Accessed by the Thumb to find out the snap interval
2376     */
2377    mx_internal function getSnapIntervalWidth():Number
2378    {
2379        return _snapInterval * track.width / (maximum - minimum);
2380    }
2381
2382    /**
2383     *  @private
2384     */
2385    mx_internal function updateThumbValue(thumbIndex:int):void
2386    {
2387        setValueFromPos(thumbIndex);
2388    }
2389
2390    /**
2391     *  Returns the thumb object at the given index. Use this method to
2392     *  style and customize individual thumbs in a slider control.
2393     *
2394     *  @param index The zero-based index number of the thumb.
2395     *
2396     *  @return A reference to the SliderThumb object.
2397     */
2398    public function getThumbAt(index:int):SliderThumb
2399    {
2400        return index >= 0 && index < thumbs.numChildren ?
2401               SliderThumb(thumbs.getChildAt(index)) :
2402               null;
2403    }
2404
2405    /**
2406     *  This method sets the value of a slider thumb, and updates the display.
2407     *
2408     *  @param index The zero-based index number of the thumb to set
2409     *  the value of, where a value of 0 corresponds to the first thumb.
2410     *
2411     *  @param value The value to set the thumb to
2412     */
2413    public function setThumbValueAt(index:int, value:Number):void
2414    {
2415        setValueAt(value, index, true);
2416        valuesChanged = true;
2417
2418        invalidateProperties();
2419        invalidateDisplayList();
2420    }
2421
2422    /**
2423     *  @private
2424     */
2425    private function setValueAt(value:Number, index:int,
2426                                isProgrammatic:Boolean = false):void
2427    {
2428        var oldValue:Number = _values[index];
2429
2430        // we need to do the round of (to remove the floating point error)
2431        // if the stepSize had a fractional value
2432        if (snapIntervalPrecision != -1)
2433        {
2434            var scale:Number = Math.pow(10, snapIntervalPrecision);
2435            value = Math.round(value * scale) / scale;
2436        }
2437
2438        _values[index] = value;
2439
2440        if (!isProgrammatic)
2441        {
2442            var event:SliderEvent = new SliderEvent(SliderEvent.CHANGE);
2443            event.value = value;
2444            event.thumbIndex = index;
2445            event.clickTarget = interactionClickTarget;
2446            //set the triggerEvent correctly
2447            if (keyInteraction)
2448            {
2449                event.triggerEvent = new KeyboardEvent(KeyboardEvent.KEY_DOWN);
2450                //reset to mouse default
2451                keyInteraction = false;
2452            }
2453            else
2454            {
2455            	event.triggerEvent = new MouseEvent(MouseEvent.CLICK);
2456            }
2457
2458            if (!isNaN(oldValue))
2459            {
2460            	if (Math.abs(oldValue - value) > 0.002)
2461            		dispatchEvent(event)
2462            }
2463            // Handle case of changing from NaN to a valid value
2464            else if (!isNaN(value))
2465			{
2466            	dispatchEvent(event);
2467			}
2468        }
2469
2470        invalidateDisplayList();
2471    }
2472
2473    /**
2474     *  @private
2475     */
2476    mx_internal function registerMouseMove(listener:Function):void
2477    {
2478        innerSlider.addEventListener(MouseEvent.MOUSE_MOVE, listener);
2479    }
2480
2481    /**
2482     *  @private
2483     */
2484    mx_internal function unRegisterMouseMove(listener:Function):void
2485    {
2486        innerSlider.removeEventListener(MouseEvent.MOUSE_MOVE, listener);
2487    }
2488
2489    //--------------------------------------------------------------------------
2490    //
2491    //  Event handlers
2492    //
2493    //--------------------------------------------------------------------------
2494
2495    /**
2496     *  @private
2497     *  Move the thumb when pressed.
2498     */
2499    private function track_mouseDownHandler(event:MouseEvent):void
2500    {
2501        if (event.target != trackHitArea && event.target != ticks)
2502            return;
2503        if (enabled && allowTrackClick)
2504        {
2505            interactionClickTarget = SliderEventClickTarget.TRACK;
2506            //this is a mouse event
2507            keyInteraction = false;
2508            var pt:Point = new Point(event.localX, event.localY);
2509            var xM:Number = pt.x;
2510            var minIndex:Number = 0;
2511            var minDistance:Number = 10000000;
2512
2513            // find the nearest thumb
2514            var n:int = _thumbCount;
2515            for (var i:int = 0; i < n; i++)
2516            {
2517                var d:Number = Math.abs(SliderThumb(thumbs.getChildAt(i)).xPosition - xM);
2518                if (d < minDistance)
2519                {
2520                    minIndex = i;
2521                    minDistance = d;
2522                }
2523            }
2524            var thumb:SliderThumb = SliderThumb(thumbs.getChildAt(minIndex));
2525            if (!isNaN(_snapInterval) && _snapInterval != 0)
2526                xM = getXFromValue(getValueFromX(xM));
2527
2528            var duration:Number = getStyle("slideDuration");
2529            var t:Tween = new Tween(thumb, thumb.xPosition, xM, duration);
2530
2531            var easingFunction:Function = getStyle("slideEasingFunction") as Function;
2532            if (easingFunction != null)
2533                t.easingFunction = easingFunction;
2534
2535            drawTrackHighlight();
2536        }
2537    }
2538
2539    /**
2540     *  @private
2541     */
2542    private function thumb_focusInHandler(event:FocusEvent):void
2543    {
2544        dispatchEvent(event);
2545    }
2546
2547    /**
2548     *  @private
2549     */
2550    private function thumb_focusOutHandler(event:FocusEvent):void
2551    {
2552        dispatchEvent(event);
2553    }
2554
2555    /**
2556     *  @private
2557     */
2558    mx_internal function getTrackHitArea():UIComponent
2559    {
2560        return trackHitArea;
2561    }
2562
2563}
2564
2565}
2566