2 * jQuery UI Slider 1.8.2
4 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT (MIT-LICENSE.txt)
6 * and GPL (GPL-LICENSE.txt) licenses.
8 * http://docs.jquery.com/UI/Slider
18 // number of pages in a slider
19 // (how many times can you page up/down to go through the whole range)
22 $.widget( "ui.slider", $.ui
.mouse
, {
24 widgetEventPrefix
: "slide",
31 orientation
: "horizontal",
42 this._keySliding
= false;
43 this._mouseSliding
= false;
44 this._animateOff
= true;
45 this._handleIndex
= null;
46 this._detectOrientation();
50 .addClass( "ui-slider" +
51 " ui-slider-" + this.orientation
+
53 " ui-widget-content" +
57 this.element
.addClass( "ui-slider-disabled ui-disabled" );
63 if ( o
.range
=== true ) {
64 this.range
= $( "<div></div>" );
66 o
.values
= [ this._valueMin(), this._valueMin() ];
68 if ( o
.values
.length
&& o
.values
.length
!== 2 ) {
69 o
.values
= [ o
.values
[0], o
.values
[0] ];
72 this.range
= $( "<div></div>" );
76 .appendTo( this.element
)
77 .addClass( "ui-slider-range" );
79 if ( o
.range
=== "min" || o
.range
=== "max" ) {
80 this.range
.addClass( "ui-slider-range-" + o
.range
);
83 // note: this isn't the most fittingly semantic framework class for this element,
84 // but worked best visually with a variety of themes
85 this.range
.addClass( "ui-widget-header" );
88 if ( $( ".ui-slider-handle", this.element
).length
=== 0 ) {
89 $( "<a href='#'></a>" )
90 .appendTo( this.element
)
91 .addClass( "ui-slider-handle" );
94 if ( o
.values
&& o
.values
.length
) {
95 while ( $(".ui-slider-handle", this.element
).length
< o
.values
.length
) {
96 $( "<a href='#'></a>" )
97 .appendTo( this.element
)
98 .addClass( "ui-slider-handle" );
102 this.handles
= $( ".ui-slider-handle", this.element
)
103 .addClass( "ui-state-default" +
106 this.handle
= this.handles
.eq( 0 );
108 this.handles
.add( this.range
).filter( "a" )
109 .click(function( event
) {
110 event
.preventDefault();
114 $( this ).addClass( "ui-state-hover" );
117 $( this ).removeClass( "ui-state-hover" );
121 $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
122 $( this ).addClass( "ui-state-focus" );
128 $( this ).removeClass( "ui-state-focus" );
131 this.handles
.each(function( i
) {
132 $( this ).data( "index.ui-slider-handle", i
);
136 .keydown(function( event
) {
138 index
= $( this ).data( "index.ui-slider-handle" ),
144 if ( self
.options
.disabled
) {
148 switch ( event
.keyCode
) {
149 case $.ui
.keyCode
.HOME
:
150 case $.ui
.keyCode
.END
:
151 case $.ui
.keyCode
.PAGE_UP
:
152 case $.ui
.keyCode
.PAGE_DOWN
:
153 case $.ui
.keyCode
.UP
:
154 case $.ui
.keyCode
.RIGHT
:
155 case $.ui
.keyCode
.DOWN
:
156 case $.ui
.keyCode
.LEFT
:
158 if ( !self
._keySliding
) {
159 self
._keySliding
= true;
160 $( this ).addClass( "ui-state-active" );
161 allowed
= self
._start( event
, index
);
162 if ( allowed
=== false ) {
169 step
= self
.options
.step
;
170 if ( self
.options
.values
&& self
.options
.values
.length
) {
171 curVal
= newVal
= self
.values( index
);
173 curVal
= newVal
= self
.value();
176 switch ( event
.keyCode
) {
177 case $.ui
.keyCode
.HOME
:
178 newVal
= self
._valueMin();
180 case $.ui
.keyCode
.END
:
181 newVal
= self
._valueMax();
183 case $.ui
.keyCode
.PAGE_UP
:
184 newVal
= self
._trimAlignValue( curVal
+ ( (self
._valueMax() - self
._valueMin()) / numPages
) );
186 case $.ui
.keyCode
.PAGE_DOWN
:
187 newVal
= self
._trimAlignValue( curVal
- ( (self
._valueMax() - self
._valueMin()) / numPages
) );
189 case $.ui
.keyCode
.UP
:
190 case $.ui
.keyCode
.RIGHT
:
191 if ( curVal
=== self
._valueMax() ) {
194 newVal
= self
._trimAlignValue( curVal
+ step
);
196 case $.ui
.keyCode
.DOWN
:
197 case $.ui
.keyCode
.LEFT
:
198 if ( curVal
=== self
._valueMin() ) {
201 newVal
= self
._trimAlignValue( curVal
- step
);
205 self
._slide( event
, index
, newVal
);
210 .keyup(function( event
) {
211 var index
= $( this ).data( "index.ui-slider-handle" );
213 if ( self
._keySliding
) {
214 self
._keySliding
= false;
215 self
._stop( event
, index
);
216 self
._change( event
, index
);
217 $( this ).removeClass( "ui-state-active" );
222 this._refreshValue();
224 this._animateOff
= false;
227 destroy: function() {
228 this.handles
.remove();
232 .removeClass( "ui-slider" +
233 " ui-slider-horizontal" +
234 " ui-slider-vertical" +
235 " ui-slider-disabled" +
237 " ui-widget-content" +
239 .removeData( "slider" )
240 .unbind( ".slider" );
242 this._mouseDestroy();
247 _mouseCapture: function( event
) {
248 var o
= this.options
,
264 width
: this.element
.outerWidth(),
265 height
: this.element
.outerHeight()
267 this.elementOffset
= this.element
.offset();
269 position
= { x
: event
.pageX
, y
: event
.pageY
};
270 normValue
= this._normValueFromMouse( position
);
271 distance
= this._valueMax() - this._valueMin() + 1;
273 this.handles
.each(function( i
) {
274 var thisDistance
= Math
.abs( normValue
- self
.values(i
) );
275 if ( distance
> thisDistance
) {
276 distance
= thisDistance
;
277 closestHandle
= $( this );
282 // workaround for bug #3736 (if both handles of a range are at 0,
283 // the first is always used as the one with least distance,
284 // and moving it is obviously prevented by preventing negative ranges)
285 if( o
.range
=== true && this.values(1) === o
.min
) {
287 closestHandle
= $( this.handles
[index
] );
290 allowed
= this._start( event
, index
);
291 if ( allowed
=== false ) {
294 this._mouseSliding
= true;
296 self
._handleIndex
= index
;
299 .addClass( "ui-state-active" )
302 offset
= closestHandle
.offset();
303 mouseOverHandle
= !$( event
.target
).parents().andSelf().is( ".ui-slider-handle" );
304 this._clickOffset
= mouseOverHandle
? { left
: 0, top
: 0 } : {
305 left
: event
.pageX
- offset
.left
- ( closestHandle
.width() / 2 ),
306 top
: event
.pageY
- offset
.top
-
307 ( closestHandle
.height() / 2 ) -
308 ( parseInt( closestHandle
.css("borderTopWidth"), 10 ) || 0 ) -
309 ( parseInt( closestHandle
.css("borderBottomWidth"), 10 ) || 0) +
310 ( parseInt( closestHandle
.css("marginTop"), 10 ) || 0)
313 normValue
= this._normValueFromMouse( position
);
314 this._slide( event
, index
, normValue
);
315 this._animateOff
= true;
319 _mouseStart: function( event
) {
323 _mouseDrag: function( event
) {
324 var position
= { x
: event
.pageX
, y
: event
.pageY
},
325 normValue
= this._normValueFromMouse( position
);
327 this._slide( event
, this._handleIndex
, normValue
);
332 _mouseStop: function( event
) {
333 this.handles
.removeClass( "ui-state-active" );
334 this._mouseSliding
= false;
336 this._stop( event
, this._handleIndex
);
337 this._change( event
, this._handleIndex
);
339 this._handleIndex
= null;
340 this._clickOffset
= null;
341 this._animateOff
= false;
346 _detectOrientation: function() {
347 this.orientation
= ( this.options
.orientation
=== "vertical" ) ? "vertical" : "horizontal";
350 _normValueFromMouse: function( position
) {
357 if ( this.orientation
=== "horizontal" ) {
358 pixelTotal
= this.elementSize
.width
;
359 pixelMouse
= position
.x
- this.elementOffset
.left
- ( this._clickOffset
? this._clickOffset
.left
: 0 );
361 pixelTotal
= this.elementSize
.height
;
362 pixelMouse
= position
.y
- this.elementOffset
.top
- ( this._clickOffset
? this._clickOffset
.top
: 0 );
365 percentMouse
= ( pixelMouse
/ pixelTotal
);
366 if ( percentMouse
> 1 ) {
369 if ( percentMouse
< 0 ) {
372 if ( this.orientation
=== "vertical" ) {
373 percentMouse
= 1 - percentMouse
;
376 valueTotal
= this._valueMax() - this._valueMin();
377 valueMouse
= this._valueMin() + percentMouse
* valueTotal
;
379 return this._trimAlignValue( valueMouse
);
382 _start: function( event
, index
) {
384 handle
: this.handles
[ index
],
387 if ( this.options
.values
&& this.options
.values
.length
) {
388 uiHash
.value
= this.values( index
);
389 uiHash
.values
= this.values();
391 return this._trigger( "start", event
, uiHash
);
394 _slide: function( event
, index
, newVal
) {
399 if ( this.options
.values
&& this.options
.values
.length
) {
400 otherVal
= this.values( index
? 0 : 1 );
402 if ( ( this.options
.values
.length
=== 2 && this.options
.range
=== true ) &&
403 ( ( index
=== 0 && newVal
> otherVal
) || ( index
=== 1 && newVal
< otherVal
) )
408 if ( newVal
!== this.values( index
) ) {
409 newValues
= this.values();
410 newValues
[ index
] = newVal
;
411 // A slide can be canceled by returning false from the slide callback
412 allowed
= this._trigger( "slide", event
, {
413 handle
: this.handles
[ index
],
417 otherVal
= this.values( index
? 0 : 1 );
418 if ( allowed
!== false ) {
419 this.values( index
, newVal
, true );
423 if ( newVal
!== this.value() ) {
424 // A slide can be canceled by returning false from the slide callback
425 allowed
= this._trigger( "slide", event
, {
426 handle
: this.handles
[ index
],
429 if ( allowed
!== false ) {
430 this.value( newVal
);
436 _stop: function( event
, index
) {
438 handle
: this.handles
[ index
],
441 if ( this.options
.values
&& this.options
.values
.length
) {
442 uiHash
.value
= this.values( index
);
443 uiHash
.values
= this.values();
446 this._trigger( "stop", event
, uiHash
);
449 _change: function( event
, index
) {
450 if ( !this._keySliding
&& !this._mouseSliding
) {
452 handle
: this.handles
[ index
],
455 if ( this.options
.values
&& this.options
.values
.length
) {
456 uiHash
.value
= this.values( index
);
457 uiHash
.values
= this.values();
460 this._trigger( "change", event
, uiHash
);
464 value: function( newValue
) {
465 if ( arguments
.length
) {
466 this.options
.value
= this._trimAlignValue( newValue
);
467 this._refreshValue();
468 this._change( null, 0 );
471 return this._value();
474 values: function( index
, newValue
) {
479 if ( arguments
.length
> 1 ) {
480 this.options
.values
[ index
] = this._trimAlignValue( newValue
);
481 this._refreshValue();
482 this._change( null, index
);
485 if ( arguments
.length
) {
486 if ( $.isArray( arguments
[ 0 ] ) ) {
487 vals
= this.options
.values
;
488 newValues
= arguments
[ 0 ];
489 for ( i
= 0; i
< vals
.length
; i
+= 1 ) {
490 vals
[ i
] = this._trimAlignValue( newValues
[ i
] );
491 this._change( null, i
);
493 this._refreshValue();
495 if ( this.options
.values
&& this.options
.values
.length
) {
496 return this._values( index
);
502 return this._values();
506 _setOption: function( key
, value
) {
510 if ( $.isArray( this.options
.values
) ) {
511 valsLength
= this.options
.values
.length
;
514 $.Widget
.prototype._setOption
.apply( this, arguments
);
519 this.handles
.filter( ".ui-state-focus" ).blur();
520 this.handles
.removeClass( "ui-state-hover" );
521 this.handles
.attr( "disabled", "disabled" );
522 this.element
.addClass( "ui-disabled" );
524 this.handles
.removeAttr( "disabled" );
525 this.element
.removeClass( "ui-disabled" );
529 this._detectOrientation();
531 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
532 .addClass( "ui-slider-" + this.orientation
);
533 this._refreshValue();
536 this._animateOff
= true;
537 this._refreshValue();
538 this._change( null, 0 );
539 this._animateOff
= false;
542 this._animateOff
= true;
543 this._refreshValue();
544 for ( i
= 0; i
< valsLength
; i
+= 1 ) {
545 this._change( null, i
);
547 this._animateOff
= false;
552 //internal value getter
553 // _value() returns value trimmed by min and max, aligned by step
555 var val
= this.options
.value
;
556 val
= this._trimAlignValue( val
);
561 //internal values getter
562 // _values() returns array of values trimmed by min and max, aligned by step
563 // _values( index ) returns single value trimmed by min and max, aligned by step
564 _values: function( index
) {
569 if ( arguments
.length
) {
570 val
= this.options
.values
[ index
];
571 val
= this._trimAlignValue( val
);
575 // .slice() creates a copy of the array
576 // this copy gets trimmed by min and max and then returned
577 vals
= this.options
.values
.slice();
578 for ( i
= 0; i
< vals
.length
; i
+= 1) {
579 vals
[ i
] = this._trimAlignValue( vals
[ i
] );
586 // returns the step-aligned value that val is closest to, between (inclusive) min and max
587 _trimAlignValue: function( val
) {
588 if ( val
< this._valueMin() ) {
589 return this._valueMin();
591 if ( val
> this._valueMax() ) {
592 return this._valueMax();
594 var step
= ( this.options
.step
> 0 ) ? this.options
.step
: 1,
595 valModStep
= val
% step
,
596 alignValue
= val
- valModStep
;
598 if ( Math
.abs(valModStep
) * 2 >= step
) {
599 alignValue
+= ( valModStep
> 0 ) ? step
: ( -step
);
602 // Since JavaScript has problems with large floats, round
603 // the final value to 5 digits after the decimal point (see #4124)
604 return parseFloat( alignValue
.toFixed(5) );
607 _valueMin: function() {
608 return this.options
.min
;
611 _valueMax: function() {
612 return this.options
.max
;
615 _refreshValue: function() {
616 var oRange
= this.options
.range
,
619 animate
= ( !this._animateOff
) ? o
.animate
: false,
627 if ( this.options
.values
&& this.options
.values
.length
) {
628 this.handles
.each(function( i
, j
) {
629 valPercent
= ( self
.values(i
) - self
._valueMin() ) / ( self
._valueMax() - self
._valueMin() ) * 100;
630 _set
[ self
.orientation
=== "horizontal" ? "left" : "bottom" ] = valPercent
+ "%";
631 $( this ).stop( 1, 1 )[ animate
? "animate" : "css" ]( _set
, o
.animate
);
632 if ( self
.options
.range
=== true ) {
633 if ( self
.orientation
=== "horizontal" ) {
635 self
.range
.stop( 1, 1 )[ animate
? "animate" : "css" ]( { left
: valPercent
+ "%" }, o
.animate
);
638 self
.range
[ animate
? "animate" : "css" ]( { width
: ( valPercent
- lastValPercent
) + "%" }, { queue
: false, duration
: o
.animate
} );
642 self
.range
.stop( 1, 1 )[ animate
? "animate" : "css" ]( { bottom
: ( valPercent
) + "%" }, o
.animate
);
645 self
.range
[ animate
? "animate" : "css" ]( { height
: ( valPercent
- lastValPercent
) + "%" }, { queue
: false, duration
: o
.animate
} );
649 lastValPercent
= valPercent
;
652 value
= this.value();
653 valueMin
= this._valueMin();
654 valueMax
= this._valueMax();
655 valPercent
= ( valueMax
!== valueMin
) ?
656 ( value
- valueMin
) / ( valueMax
- valueMin
) * 100 :
658 _set
[ self
.orientation
=== "horizontal" ? "left" : "bottom" ] = valPercent
+ "%";
659 this.handle
.stop( 1, 1 )[ animate
? "animate" : "css" ]( _set
, o
.animate
);
661 if ( oRange
=== "min" && this.orientation
=== "horizontal" ) {
662 this.range
.stop( 1, 1 )[ animate
? "animate" : "css" ]( { width
: valPercent
+ "%" }, o
.animate
);
664 if ( oRange
=== "max" && this.orientation
=== "horizontal" ) {
665 this.range
[ animate
? "animate" : "css" ]( { width
: ( 100 - valPercent
) + "%" }, { queue
: false, duration
: o
.animate
} );
667 if ( oRange
=== "min" && this.orientation
=== "vertical" ) {
668 this.range
.stop( 1, 1 )[ animate
? "animate" : "css" ]( { height
: valPercent
+ "%" }, o
.animate
);
670 if ( oRange
=== "max" && this.orientation
=== "vertical" ) {
671 this.range
[ animate
? "animate" : "css" ]( { height
: ( 100 - valPercent
) + "%" }, { queue
: false, duration
: o
.animate
} );
678 $.extend( $.ui
.slider
, {