2 * jQuery UI Slider 1.8.24
4 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
8 * http://docs.jquery.com/UI/Slider
15 (function( $, undefined ) {
17 // number of pages in a slider
18 // (how many times can you page up/down to go through the whole range)
21 $.widget( "ui.slider", $.ui
.mouse
, {
23 widgetEventPrefix
: "slide",
30 orientation
: "horizontal",
40 existingHandles
= this.element
.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
41 handle
= "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
42 handleCount
= ( o
.values
&& o
.values
.length
) || 1,
45 this._keySliding
= false;
46 this._mouseSliding
= false;
47 this._animateOff
= true;
48 this._handleIndex
= null;
49 this._detectOrientation();
53 .addClass( "ui-slider" +
54 " ui-slider-" + this.orientation
+
56 " ui-widget-content" +
58 ( o
.disabled
? " ui-slider-disabled ui-disabled" : "" ) );
63 if ( o
.range
=== true ) {
65 o
.values
= [ this._valueMin(), this._valueMin() ];
67 if ( o
.values
.length
&& o
.values
.length
!== 2 ) {
68 o
.values
= [ o
.values
[0], o
.values
[0] ];
72 this.range
= $( "<div></div>" )
73 .appendTo( this.element
)
74 .addClass( "ui-slider-range" +
75 // note: this isn't the most fittingly semantic framework class for this element,
76 // but worked best visually with a variety of themes
78 ( ( o
.range
=== "min" || o
.range
=== "max" ) ? " ui-slider-range-" + o
.range
: "" ) );
81 for ( var i
= existingHandles
.length
; i
< handleCount
; i
+= 1 ) {
82 handles
.push( handle
);
85 this.handles
= existingHandles
.add( $( handles
.join( "" ) ).appendTo( self
.element
) );
87 this.handle
= this.handles
.eq( 0 );
89 this.handles
.add( this.range
).filter( "a" )
90 .click(function( event
) {
91 event
.preventDefault();
95 $( this ).addClass( "ui-state-hover" );
98 $( this ).removeClass( "ui-state-hover" );
102 $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
103 $( this ).addClass( "ui-state-focus" );
109 $( this ).removeClass( "ui-state-focus" );
112 this.handles
.each(function( i
) {
113 $( this ).data( "index.ui-slider-handle", i
);
117 .keydown(function( event
) {
118 var index
= $( this ).data( "index.ui-slider-handle" ),
124 if ( self
.options
.disabled
) {
128 switch ( event
.keyCode
) {
129 case $.ui
.keyCode
.HOME
:
130 case $.ui
.keyCode
.END
:
131 case $.ui
.keyCode
.PAGE_UP
:
132 case $.ui
.keyCode
.PAGE_DOWN
:
133 case $.ui
.keyCode
.UP
:
134 case $.ui
.keyCode
.RIGHT
:
135 case $.ui
.keyCode
.DOWN
:
136 case $.ui
.keyCode
.LEFT
:
137 event
.preventDefault();
138 if ( !self
._keySliding
) {
139 self
._keySliding
= true;
140 $( this ).addClass( "ui-state-active" );
141 allowed
= self
._start( event
, index
);
142 if ( allowed
=== false ) {
149 step
= self
.options
.step
;
150 if ( self
.options
.values
&& self
.options
.values
.length
) {
151 curVal
= newVal
= self
.values( index
);
153 curVal
= newVal
= self
.value();
156 switch ( event
.keyCode
) {
157 case $.ui
.keyCode
.HOME
:
158 newVal
= self
._valueMin();
160 case $.ui
.keyCode
.END
:
161 newVal
= self
._valueMax();
163 case $.ui
.keyCode
.PAGE_UP
:
164 newVal
= self
._trimAlignValue( curVal
+ ( (self
._valueMax() - self
._valueMin()) / numPages
) );
166 case $.ui
.keyCode
.PAGE_DOWN
:
167 newVal
= self
._trimAlignValue( curVal
- ( (self
._valueMax() - self
._valueMin()) / numPages
) );
169 case $.ui
.keyCode
.UP
:
170 case $.ui
.keyCode
.RIGHT
:
171 if ( curVal
=== self
._valueMax() ) {
174 newVal
= self
._trimAlignValue( curVal
+ step
);
176 case $.ui
.keyCode
.DOWN
:
177 case $.ui
.keyCode
.LEFT
:
178 if ( curVal
=== self
._valueMin() ) {
181 newVal
= self
._trimAlignValue( curVal
- step
);
185 self
._slide( event
, index
, newVal
);
187 .keyup(function( event
) {
188 var index
= $( this ).data( "index.ui-slider-handle" );
190 if ( self
._keySliding
) {
191 self
._keySliding
= false;
192 self
._stop( event
, index
);
193 self
._change( event
, index
);
194 $( this ).removeClass( "ui-state-active" );
199 this._refreshValue();
201 this._animateOff
= false;
204 destroy: function() {
205 this.handles
.remove();
209 .removeClass( "ui-slider" +
210 " ui-slider-horizontal" +
211 " ui-slider-vertical" +
212 " ui-slider-disabled" +
214 " ui-widget-content" +
216 .removeData( "slider" )
217 .unbind( ".slider" );
219 this._mouseDestroy();
224 _mouseCapture: function( event
) {
225 var o
= this.options
,
241 width
: this.element
.outerWidth(),
242 height
: this.element
.outerHeight()
244 this.elementOffset
= this.element
.offset();
246 position
= { x
: event
.pageX
, y
: event
.pageY
};
247 normValue
= this._normValueFromMouse( position
);
248 distance
= this._valueMax() - this._valueMin() + 1;
250 this.handles
.each(function( i
) {
251 var thisDistance
= Math
.abs( normValue
- self
.values(i
) );
252 if ( distance
> thisDistance
) {
253 distance
= thisDistance
;
254 closestHandle
= $( this );
259 // workaround for bug #3736 (if both handles of a range are at 0,
260 // the first is always used as the one with least distance,
261 // and moving it is obviously prevented by preventing negative ranges)
262 if( o
.range
=== true && this.values(1) === o
.min
) {
264 closestHandle
= $( this.handles
[index
] );
267 allowed
= this._start( event
, index
);
268 if ( allowed
=== false ) {
271 this._mouseSliding
= true;
273 self
._handleIndex
= index
;
276 .addClass( "ui-state-active" )
279 offset
= closestHandle
.offset();
280 mouseOverHandle
= !$( event
.target
).parents().andSelf().is( ".ui-slider-handle" );
281 this._clickOffset
= mouseOverHandle
? { left
: 0, top
: 0 } : {
282 left
: event
.pageX
- offset
.left
- ( closestHandle
.width() / 2 ),
283 top
: event
.pageY
- offset
.top
-
284 ( closestHandle
.height() / 2 ) -
285 ( parseInt( closestHandle
.css("borderTopWidth"), 10 ) || 0 ) -
286 ( parseInt( closestHandle
.css("borderBottomWidth"), 10 ) || 0) +
287 ( parseInt( closestHandle
.css("marginTop"), 10 ) || 0)
290 if ( !this.handles
.hasClass( "ui-state-hover" ) ) {
291 this._slide( event
, index
, normValue
);
293 this._animateOff
= true;
297 _mouseStart: function( event
) {
301 _mouseDrag: function( event
) {
302 var position
= { x
: event
.pageX
, y
: event
.pageY
},
303 normValue
= this._normValueFromMouse( position
);
305 this._slide( event
, this._handleIndex
, normValue
);
310 _mouseStop: function( event
) {
311 this.handles
.removeClass( "ui-state-active" );
312 this._mouseSliding
= false;
314 this._stop( event
, this._handleIndex
);
315 this._change( event
, this._handleIndex
);
317 this._handleIndex
= null;
318 this._clickOffset
= null;
319 this._animateOff
= false;
324 _detectOrientation: function() {
325 this.orientation
= ( this.options
.orientation
=== "vertical" ) ? "vertical" : "horizontal";
328 _normValueFromMouse: function( position
) {
335 if ( this.orientation
=== "horizontal" ) {
336 pixelTotal
= this.elementSize
.width
;
337 pixelMouse
= position
.x
- this.elementOffset
.left
- ( this._clickOffset
? this._clickOffset
.left
: 0 );
339 pixelTotal
= this.elementSize
.height
;
340 pixelMouse
= position
.y
- this.elementOffset
.top
- ( this._clickOffset
? this._clickOffset
.top
: 0 );
343 percentMouse
= ( pixelMouse
/ pixelTotal
);
344 if ( percentMouse
> 1 ) {
347 if ( percentMouse
< 0 ) {
350 if ( this.orientation
=== "vertical" ) {
351 percentMouse
= 1 - percentMouse
;
354 valueTotal
= this._valueMax() - this._valueMin();
355 valueMouse
= this._valueMin() + percentMouse
* valueTotal
;
357 return this._trimAlignValue( valueMouse
);
360 _start: function( event
, index
) {
362 handle
: this.handles
[ index
],
365 if ( this.options
.values
&& this.options
.values
.length
) {
366 uiHash
.value
= this.values( index
);
367 uiHash
.values
= this.values();
369 return this._trigger( "start", event
, uiHash
);
372 _slide: function( event
, index
, newVal
) {
377 if ( this.options
.values
&& this.options
.values
.length
) {
378 otherVal
= this.values( index
? 0 : 1 );
380 if ( ( this.options
.values
.length
=== 2 && this.options
.range
=== true ) &&
381 ( ( index
=== 0 && newVal
> otherVal
) || ( index
=== 1 && newVal
< otherVal
) )
386 if ( newVal
!== this.values( index
) ) {
387 newValues
= this.values();
388 newValues
[ index
] = newVal
;
389 // A slide can be canceled by returning false from the slide callback
390 allowed
= this._trigger( "slide", event
, {
391 handle
: this.handles
[ index
],
395 otherVal
= this.values( index
? 0 : 1 );
396 if ( allowed
!== false ) {
397 this.values( index
, newVal
, true );
401 if ( newVal
!== this.value() ) {
402 // A slide can be canceled by returning false from the slide callback
403 allowed
= this._trigger( "slide", event
, {
404 handle
: this.handles
[ index
],
407 if ( allowed
!== false ) {
408 this.value( newVal
);
414 _stop: function( event
, index
) {
416 handle
: this.handles
[ index
],
419 if ( this.options
.values
&& this.options
.values
.length
) {
420 uiHash
.value
= this.values( index
);
421 uiHash
.values
= this.values();
424 this._trigger( "stop", event
, uiHash
);
427 _change: function( event
, index
) {
428 if ( !this._keySliding
&& !this._mouseSliding
) {
430 handle
: this.handles
[ index
],
433 if ( this.options
.values
&& this.options
.values
.length
) {
434 uiHash
.value
= this.values( index
);
435 uiHash
.values
= this.values();
438 this._trigger( "change", event
, uiHash
);
442 value: function( newValue
) {
443 if ( arguments
.length
) {
444 this.options
.value
= this._trimAlignValue( newValue
);
445 this._refreshValue();
446 this._change( null, 0 );
450 return this._value();
453 values: function( index
, newValue
) {
458 if ( arguments
.length
> 1 ) {
459 this.options
.values
[ index
] = this._trimAlignValue( newValue
);
460 this._refreshValue();
461 this._change( null, index
);
465 if ( arguments
.length
) {
466 if ( $.isArray( arguments
[ 0 ] ) ) {
467 vals
= this.options
.values
;
468 newValues
= arguments
[ 0 ];
469 for ( i
= 0; i
< vals
.length
; i
+= 1 ) {
470 vals
[ i
] = this._trimAlignValue( newValues
[ i
] );
471 this._change( null, i
);
473 this._refreshValue();
475 if ( this.options
.values
&& this.options
.values
.length
) {
476 return this._values( index
);
482 return this._values();
486 _setOption: function( key
, value
) {
490 if ( $.isArray( this.options
.values
) ) {
491 valsLength
= this.options
.values
.length
;
494 $.Widget
.prototype._setOption
.apply( this, arguments
);
499 this.handles
.filter( ".ui-state-focus" ).blur();
500 this.handles
.removeClass( "ui-state-hover" );
501 this.handles
.propAttr( "disabled", true );
502 this.element
.addClass( "ui-disabled" );
504 this.handles
.propAttr( "disabled", false );
505 this.element
.removeClass( "ui-disabled" );
509 this._detectOrientation();
511 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
512 .addClass( "ui-slider-" + this.orientation
);
513 this._refreshValue();
516 this._animateOff
= true;
517 this._refreshValue();
518 this._change( null, 0 );
519 this._animateOff
= false;
522 this._animateOff
= true;
523 this._refreshValue();
524 for ( i
= 0; i
< valsLength
; i
+= 1 ) {
525 this._change( null, i
);
527 this._animateOff
= false;
532 //internal value getter
533 // _value() returns value trimmed by min and max, aligned by step
535 var val
= this.options
.value
;
536 val
= this._trimAlignValue( val
);
541 //internal values getter
542 // _values() returns array of values trimmed by min and max, aligned by step
543 // _values( index ) returns single value trimmed by min and max, aligned by step
544 _values: function( index
) {
549 if ( arguments
.length
) {
550 val
= this.options
.values
[ index
];
551 val
= this._trimAlignValue( val
);
555 // .slice() creates a copy of the array
556 // this copy gets trimmed by min and max and then returned
557 vals
= this.options
.values
.slice();
558 for ( i
= 0; i
< vals
.length
; i
+= 1) {
559 vals
[ i
] = this._trimAlignValue( vals
[ i
] );
566 // returns the step-aligned value that val is closest to, between (inclusive) min and max
567 _trimAlignValue: function( val
) {
568 if ( val
<= this._valueMin() ) {
569 return this._valueMin();
571 if ( val
>= this._valueMax() ) {
572 return this._valueMax();
574 var step
= ( this.options
.step
> 0 ) ? this.options
.step
: 1,
575 valModStep
= (val
- this._valueMin()) % step
,
576 alignValue
= val
- valModStep
;
578 if ( Math
.abs(valModStep
) * 2 >= step
) {
579 alignValue
+= ( valModStep
> 0 ) ? step
: ( -step
);
582 // Since JavaScript has problems with large floats, round
583 // the final value to 5 digits after the decimal point (see #4124)
584 return parseFloat( alignValue
.toFixed(5) );
587 _valueMin: function() {
588 return this.options
.min
;
591 _valueMax: function() {
592 return this.options
.max
;
595 _refreshValue: function() {
596 var oRange
= this.options
.range
,
599 animate
= ( !this._animateOff
) ? o
.animate
: false,
607 if ( this.options
.values
&& this.options
.values
.length
) {
608 this.handles
.each(function( i
, j
) {
609 valPercent
= ( self
.values(i
) - self
._valueMin() ) / ( self
._valueMax() - self
._valueMin() ) * 100;
610 _set
[ self
.orientation
=== "horizontal" ? "left" : "bottom" ] = valPercent
+ "%";
611 $( this ).stop( 1, 1 )[ animate
? "animate" : "css" ]( _set
, o
.animate
);
612 if ( self
.options
.range
=== true ) {
613 if ( self
.orientation
=== "horizontal" ) {
615 self
.range
.stop( 1, 1 )[ animate
? "animate" : "css" ]( { left
: valPercent
+ "%" }, o
.animate
);
618 self
.range
[ animate
? "animate" : "css" ]( { width
: ( valPercent
- lastValPercent
) + "%" }, { queue
: false, duration
: o
.animate
} );
622 self
.range
.stop( 1, 1 )[ animate
? "animate" : "css" ]( { bottom
: ( valPercent
) + "%" }, o
.animate
);
625 self
.range
[ animate
? "animate" : "css" ]( { height
: ( valPercent
- lastValPercent
) + "%" }, { queue
: false, duration
: o
.animate
} );
629 lastValPercent
= valPercent
;
632 value
= this.value();
633 valueMin
= this._valueMin();
634 valueMax
= this._valueMax();
635 valPercent
= ( valueMax
!== valueMin
) ?
636 ( value
- valueMin
) / ( valueMax
- valueMin
) * 100 :
638 _set
[ self
.orientation
=== "horizontal" ? "left" : "bottom" ] = valPercent
+ "%";
639 this.handle
.stop( 1, 1 )[ animate
? "animate" : "css" ]( _set
, o
.animate
);
641 if ( oRange
=== "min" && this.orientation
=== "horizontal" ) {
642 this.range
.stop( 1, 1 )[ animate
? "animate" : "css" ]( { width
: valPercent
+ "%" }, o
.animate
);
644 if ( oRange
=== "max" && this.orientation
=== "horizontal" ) {
645 this.range
[ animate
? "animate" : "css" ]( { width
: ( 100 - valPercent
) + "%" }, { queue
: false, duration
: o
.animate
} );
647 if ( oRange
=== "min" && this.orientation
=== "vertical" ) {
648 this.range
.stop( 1, 1 )[ animate
? "animate" : "css" ]( { height
: valPercent
+ "%" }, o
.animate
);
650 if ( oRange
=== "max" && this.orientation
=== "vertical" ) {
651 this.range
[ animate
? "animate" : "css" ]( { height
: ( 100 - valPercent
) + "%" }, { queue
: false, duration
: o
.animate
} );
658 $.extend( $.ui
.slider
, {