2 * jQuery UI Slider 1.7.1
4 * Copyright (c) 2009 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
16 $.widget("ui.slider", $.extend({}, $.ui
.mouse
, {
20 var self
= this, o
= this.options
;
21 this._keySliding
= false;
22 this._handleIndex
= null;
23 this._detectOrientation();
28 + " ui-slider-" + this.orientation
30 + " ui-widget-content"
37 if (o
.range
=== true) {
38 this.range
= $('<div></div>');
39 if (!o
.values
) o
.values
= [this._valueMin(), this._valueMin()];
40 if (o
.values
.length
&& o
.values
.length
!= 2) {
41 o
.values
= [o
.values
[0], o
.values
[0]];
44 this.range
= $('<div></div>');
48 .appendTo(this.element
)
49 .addClass("ui-slider-range");
51 if (o
.range
== "min" || o
.range
== "max") {
52 this.range
.addClass("ui-slider-range-" + o
.range
);
55 // note: this isn't the most fittingly semantic framework class for this element,
56 // but worked best visually with a variety of themes
57 this.range
.addClass("ui-widget-header");
61 if ($(".ui-slider-handle", this.element
).length
== 0)
63 .appendTo(this.element
)
64 .addClass("ui-slider-handle");
66 if (o
.values
&& o
.values
.length
) {
67 while ($(".ui-slider-handle", this.element
).length
< o
.values
.length
)
69 .appendTo(this.element
)
70 .addClass("ui-slider-handle");
73 this.handles
= $(".ui-slider-handle", this.element
)
74 .addClass("ui-state-default"
77 this.handle
= this.handles
.eq(0);
79 this.handles
.add(this.range
).filter("a")
80 .click(function(event
) { event
.preventDefault(); })
81 .hover(function() { $(this).addClass('ui-state-hover'); }, function() { $(this).removeClass('ui-state-hover'); })
82 .focus(function() { $(".ui-slider .ui-state-focus").removeClass('ui-state-focus'); $(this).addClass('ui-state-focus'); })
83 .blur(function() { $(this).removeClass('ui-state-focus'); });
85 this.handles
.each(function(i
) {
86 $(this).data("index.ui-slider-handle", i
);
89 this.handles
.keydown(function(event
) {
93 var index
= $(this).data("index.ui-slider-handle");
95 if (self
.options
.disabled
)
98 switch (event
.keyCode
) {
99 case $.ui
.keyCode
.HOME
:
100 case $.ui
.keyCode
.END
:
101 case $.ui
.keyCode
.UP
:
102 case $.ui
.keyCode
.RIGHT
:
103 case $.ui
.keyCode
.DOWN
:
104 case $.ui
.keyCode
.LEFT
:
106 if (!self
._keySliding
) {
107 self
._keySliding
= true;
108 $(this).addClass("ui-state-active");
109 self
._start(event
, index
);
114 var curVal
, newVal
, step
= self
._step();
115 if (self
.options
.values
&& self
.options
.values
.length
) {
116 curVal
= newVal
= self
.values(index
);
118 curVal
= newVal
= self
.value();
121 switch (event
.keyCode
) {
122 case $.ui
.keyCode
.HOME
:
123 newVal
= self
._valueMin();
125 case $.ui
.keyCode
.END
:
126 newVal
= self
._valueMax();
128 case $.ui
.keyCode
.UP
:
129 case $.ui
.keyCode
.RIGHT
:
130 if(curVal
== self
._valueMax()) return;
131 newVal
= curVal
+ step
;
133 case $.ui
.keyCode
.DOWN
:
134 case $.ui
.keyCode
.LEFT
:
135 if(curVal
== self
._valueMin()) return;
136 newVal
= curVal
- step
;
140 self
._slide(event
, index
, newVal
);
144 }).keyup(function(event
) {
146 var index
= $(this).data("index.ui-slider-handle");
148 if (self
._keySliding
) {
149 self
._stop(event
, index
);
150 self
._change(event
, index
);
151 self
._keySliding
= false;
152 $(this).removeClass("ui-state-active");
157 this._refreshValue();
161 destroy: function() {
163 this.handles
.remove();
167 .removeClass("ui-slider"
168 + " ui-slider-horizontal"
169 + " ui-slider-vertical"
170 + " ui-slider-disabled"
172 + " ui-widget-content"
174 .removeData("slider")
177 this._mouseDestroy();
181 _mouseCapture: function(event
) {
183 var o
= this.options
;
189 width
: this.element
.outerWidth(),
190 height
: this.element
.outerHeight()
192 this.elementOffset
= this.element
.offset();
194 var position
= { x
: event
.pageX
, y
: event
.pageY
};
195 var normValue
= this._normValueFromMouse(position
);
197 var distance
= this._valueMax() - this._valueMin() + 1, closestHandle
;
198 var self
= this, index
;
199 this.handles
.each(function(i
) {
200 var thisDistance
= Math
.abs(normValue
- self
.values(i
));
201 if (distance
> thisDistance
) {
202 distance
= thisDistance
;
203 closestHandle
= $(this);
208 // workaround for bug #3736 (if both handles of a range are at 0,
209 // the first is always used as the one with least distance,
210 // and moving it is obviously prevented by preventing negative ranges)
211 if(o
.range
== true && this.values(1) == o
.min
) {
212 closestHandle
= $(this.handles
[++index
]);
215 this._start(event
, index
);
217 self
._handleIndex
= index
;
220 .addClass("ui-state-active")
223 var offset
= closestHandle
.offset();
224 var mouseOverHandle
= !$(event
.target
).parents().andSelf().is('.ui-slider-handle');
225 this._clickOffset
= mouseOverHandle
? { left
: 0, top
: 0 } : {
226 left
: event
.pageX
- offset
.left
- (closestHandle
.width() / 2),
227 top
: event
.pageY
- offset
.top
228 - (closestHandle
.height() / 2)
229 - (parseInt(closestHandle
.css('borderTopWidth'),10) || 0)
230 - (parseInt(closestHandle
.css('borderBottomWidth'),10) || 0)
231 + (parseInt(closestHandle
.css('marginTop'),10) || 0)
234 normValue
= this._normValueFromMouse(position
);
235 this._slide(event
, index
, normValue
);
240 _mouseStart: function(event
) {
244 _mouseDrag: function(event
) {
246 var position
= { x
: event
.pageX
, y
: event
.pageY
};
247 var normValue
= this._normValueFromMouse(position
);
249 this._slide(event
, this._handleIndex
, normValue
);
255 _mouseStop: function(event
) {
257 this.handles
.removeClass("ui-state-active");
258 this._stop(event
, this._handleIndex
);
259 this._change(event
, this._handleIndex
);
260 this._handleIndex
= null;
261 this._clickOffset
= null;
267 _detectOrientation: function() {
268 this.orientation
= this.options
.orientation
== 'vertical' ? 'vertical' : 'horizontal';
271 _normValueFromMouse: function(position
) {
273 var pixelTotal
, pixelMouse
;
274 if ('horizontal' == this.orientation
) {
275 pixelTotal
= this.elementSize
.width
;
276 pixelMouse
= position
.x
- this.elementOffset
.left
- (this._clickOffset
? this._clickOffset
.left
: 0);
278 pixelTotal
= this.elementSize
.height
;
279 pixelMouse
= position
.y
- this.elementOffset
.top
- (this._clickOffset
? this._clickOffset
.top
: 0);
282 var percentMouse
= (pixelMouse
/ pixelTotal
);
283 if (percentMouse
> 1) percentMouse
= 1;
284 if (percentMouse
< 0) percentMouse
= 0;
285 if ('vertical' == this.orientation
)
286 percentMouse
= 1 - percentMouse
;
288 var valueTotal
= this._valueMax() - this._valueMin(),
289 valueMouse
= percentMouse
* valueTotal
,
290 valueMouseModStep
= valueMouse
% this.options
.step
,
291 normValue
= this._valueMin() + valueMouse
- valueMouseModStep
;
293 if (valueMouseModStep
> (this.options
.step
/ 2))
294 normValue
+= this.options
.step
;
296 // Since JavaScript has problems with large floats, round
297 // the final value to 5 digits after the decimal point (see #4124)
298 return parseFloat(normValue
.toFixed(5));
302 _start: function(event
, index
) {
304 handle
: this.handles
[index
],
307 if (this.options
.values
&& this.options
.values
.length
) {
308 uiHash
.value
= this.values(index
)
309 uiHash
.values
= this.values()
311 this._trigger("start", event
, uiHash
);
314 _slide: function(event
, index
, newVal
) {
316 var handle
= this.handles
[index
];
318 if (this.options
.values
&& this.options
.values
.length
) {
320 var otherVal
= this.values(index
? 0 : 1);
322 if ((index
== 0 && newVal
>= otherVal
) || (index
== 1 && newVal
<= otherVal
))
325 if (newVal
!= this.values(index
)) {
326 var newValues
= this.values();
327 newValues
[index
] = newVal
;
328 // A slide can be canceled by returning false from the slide callback
329 var allowed
= this._trigger("slide", event
, {
330 handle
: this.handles
[index
],
334 var otherVal
= this.values(index
? 0 : 1);
335 if (allowed
!== false) {
336 this.values(index
, newVal
, ( event
.type
== 'mousedown' && this.options
.animate
), true);
342 if (newVal
!= this.value()) {
343 // A slide can be canceled by returning false from the slide callback
344 var allowed
= this._trigger("slide", event
, {
345 handle
: this.handles
[index
],
348 if (allowed
!== false) {
349 this._setData('value', newVal
, ( event
.type
== 'mousedown' && this.options
.animate
));
358 _stop: function(event
, index
) {
360 handle
: this.handles
[index
],
363 if (this.options
.values
&& this.options
.values
.length
) {
364 uiHash
.value
= this.values(index
)
365 uiHash
.values
= this.values()
367 this._trigger("stop", event
, uiHash
);
370 _change: function(event
, index
) {
372 handle
: this.handles
[index
],
375 if (this.options
.values
&& this.options
.values
.length
) {
376 uiHash
.value
= this.values(index
)
377 uiHash
.values
= this.values()
379 this._trigger("change", event
, uiHash
);
382 value: function(newValue
) {
384 if (arguments
.length
) {
385 this._setData("value", newValue
);
386 this._change(null, 0);
389 return this._value();
393 values: function(index
, newValue
, animated
, noPropagation
) {
395 if (arguments
.length
> 1) {
396 this.options
.values
[index
] = newValue
;
397 this._refreshValue(animated
);
398 if(!noPropagation
) this._change(null, index
);
401 if (arguments
.length
) {
402 if (this.options
.values
&& this.options
.values
.length
) {
403 return this._values(index
);
408 return this._values();
413 _setData: function(key
, value
, animated
) {
415 $.widget
.prototype._setData
.apply(this, arguments
);
420 this._detectOrientation();
423 .removeClass("ui-slider-horizontal ui-slider-vertical")
424 .addClass("ui-slider-" + this.orientation
);
425 this._refreshValue(animated
);
428 this._refreshValue(animated
);
435 var step
= this.options
.step
;
441 var val
= this.options
.value
;
442 if (val
< this._valueMin()) val
= this._valueMin();
443 if (val
> this._valueMax()) val
= this._valueMax();
449 _values: function(index
) {
451 if (arguments
.length
) {
452 var val
= this.options
.values
[index
];
453 if (val
< this._valueMin()) val
= this._valueMin();
454 if (val
> this._valueMax()) val
= this._valueMax();
458 return this.options
.values
;
463 _valueMin: function() {
464 var valueMin
= this.options
.min
;
468 _valueMax: function() {
469 var valueMax
= this.options
.max
;
473 _refreshValue: function(animate
) {
475 var oRange
= this.options
.range
, o
= this.options
, self
= this;
477 if (this.options
.values
&& this.options
.values
.length
) {
479 this.handles
.each(function(i
, j
) {
480 var valPercent
= (self
.values(i
) - self
._valueMin()) / (self
._valueMax() - self
._valueMin()) * 100;
481 var _set
= {}; _set
[self
.orientation
== 'horizontal' ? 'left' : 'bottom'] = valPercent
+ '%';
482 $(this).stop(1,1)[animate
? 'animate' : 'css'](_set
, o
.animate
);
483 if (self
.options
.range
=== true) {
484 if (self
.orientation
== 'horizontal') {
485 (i
== 0) && self
.range
.stop(1,1)[animate
? 'animate' : 'css']({ left
: valPercent
+ '%' }, o
.animate
);
486 (i
== 1) && self
.range
[animate
? 'animate' : 'css']({ width
: (valPercent
- lastValPercent
) + '%' }, { queue
: false, duration
: o
.animate
});
488 (i
== 0) && self
.range
.stop(1,1)[animate
? 'animate' : 'css']({ bottom
: (valPercent
) + '%' }, o
.animate
);
489 (i
== 1) && self
.range
[animate
? 'animate' : 'css']({ height
: (valPercent
- lastValPercent
) + '%' }, { queue
: false, duration
: o
.animate
});
492 lastValPercent
= valPercent
;
495 var value
= this.value(),
496 valueMin
= this._valueMin(),
497 valueMax
= this._valueMax(),
498 valPercent
= valueMax
!= valueMin
499 ? (value
- valueMin
) / (valueMax
- valueMin
) * 100
501 var _set
= {}; _set
[self
.orientation
== 'horizontal' ? 'left' : 'bottom'] = valPercent
+ '%';
502 this.handle
.stop(1,1)[animate
? 'animate' : 'css'](_set
, o
.animate
);
504 (oRange
== "min") && (this.orientation
== "horizontal") && this.range
.stop(1,1)[animate
? 'animate' : 'css']({ width
: valPercent
+ '%' }, o
.animate
);
505 (oRange
== "max") && (this.orientation
== "horizontal") && this.range
[animate
? 'animate' : 'css']({ width
: (100 - valPercent
) + '%' }, { queue
: false, duration
: o
.animate
});
506 (oRange
== "min") && (this.orientation
== "vertical") && this.range
.stop(1,1)[animate
? 'animate' : 'css']({ height
: valPercent
+ '%' }, o
.animate
);
507 (oRange
== "max") && (this.orientation
== "vertical") && this.range
[animate
? 'animate' : 'css']({ height
: (100 - valPercent
) + '%' }, { queue
: false, duration
: o
.animate
});
514 $.extend($.ui
.slider
, {
515 getter
: "value values",
517 eventPrefix
: "slide",
524 orientation
: 'horizontal',