2 * jQuery UI Slider 1.7.2
\r
4 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
\r
5 * Dual licensed under the MIT (MIT-LICENSE.txt)
\r
6 * and GPL (GPL-LICENSE.txt) licenses.
\r
8 * http://docs.jquery.com/UI/Slider
\r
16 $.widget("ui.slider", $.extend({}, $.ui.mouse, {
\r
20 var self = this, o = this.options;
\r
21 this._keySliding = false;
\r
22 this._handleIndex = null;
\r
23 this._detectOrientation();
\r
27 .addClass("ui-slider"
\r
28 + " ui-slider-" + this.orientation
\r
30 + " ui-widget-content"
\r
31 + " ui-corner-all");
\r
37 if (o.range === true) {
\r
38 this.range = $('<div></div>');
\r
39 if (!o.values) o.values = [this._valueMin(), this._valueMin()];
\r
40 if (o.values.length && o.values.length != 2) {
\r
41 o.values = [o.values[0], o.values[0]];
\r
44 this.range = $('<div></div>');
\r
48 .appendTo(this.element)
\r
49 .addClass("ui-slider-range");
\r
51 if (o.range == "min" || o.range == "max") {
\r
52 this.range.addClass("ui-slider-range-" + o.range);
\r
55 // note: this isn't the most fittingly semantic framework class for this element,
\r
56 // but worked best visually with a variety of themes
\r
57 this.range.addClass("ui-widget-header");
\r
61 if ($(".ui-slider-handle", this.element).length == 0)
\r
62 $('<a href="#"></a>')
\r
63 .appendTo(this.element)
\r
64 .addClass("ui-slider-handle");
\r
66 if (o.values && o.values.length) {
\r
67 while ($(".ui-slider-handle", this.element).length < o.values.length)
\r
68 $('<a href="#"></a>')
\r
69 .appendTo(this.element)
\r
70 .addClass("ui-slider-handle");
\r
73 this.handles = $(".ui-slider-handle", this.element)
\r
74 .addClass("ui-state-default"
\r
75 + " ui-corner-all");
\r
77 this.handle = this.handles.eq(0);
\r
79 this.handles.add(this.range).filter("a")
\r
80 .click(function(event) {
\r
81 event.preventDefault();
\r
85 $(this).addClass('ui-state-hover');
\r
88 $(this).removeClass('ui-state-hover');
\r
92 $(".ui-slider .ui-state-focus").removeClass('ui-state-focus'); $(this).addClass('ui-state-focus');
\r
98 $(this).removeClass('ui-state-focus');
\r
101 this.handles.each(function(i) {
\r
102 $(this).data("index.ui-slider-handle", i);
\r
105 this.handles.keydown(function(event) {
\r
109 var index = $(this).data("index.ui-slider-handle");
\r
111 if (self.options.disabled)
\r
114 switch (event.keyCode) {
\r
115 case $.ui.keyCode.HOME:
\r
116 case $.ui.keyCode.END:
\r
117 case $.ui.keyCode.UP:
\r
118 case $.ui.keyCode.RIGHT:
\r
119 case $.ui.keyCode.DOWN:
\r
120 case $.ui.keyCode.LEFT:
\r
122 if (!self._keySliding) {
\r
123 self._keySliding = true;
\r
124 $(this).addClass("ui-state-active");
\r
125 self._start(event, index);
\r
130 var curVal, newVal, step = self._step();
\r
131 if (self.options.values && self.options.values.length) {
\r
132 curVal = newVal = self.values(index);
\r
134 curVal = newVal = self.value();
\r
137 switch (event.keyCode) {
\r
138 case $.ui.keyCode.HOME:
\r
139 newVal = self._valueMin();
\r
141 case $.ui.keyCode.END:
\r
142 newVal = self._valueMax();
\r
144 case $.ui.keyCode.UP:
\r
145 case $.ui.keyCode.RIGHT:
\r
146 if(curVal == self._valueMax()) return;
\r
147 newVal = curVal + step;
\r
149 case $.ui.keyCode.DOWN:
\r
150 case $.ui.keyCode.LEFT:
\r
151 if(curVal == self._valueMin()) return;
\r
152 newVal = curVal - step;
\r
156 self._slide(event, index, newVal);
\r
160 }).keyup(function(event) {
\r
162 var index = $(this).data("index.ui-slider-handle");
\r
164 if (self._keySliding) {
\r
165 self._stop(event, index);
\r
166 self._change(event, index);
\r
167 self._keySliding = false;
\r
168 $(this).removeClass("ui-state-active");
\r
173 this._refreshValue();
\r
177 destroy: function() {
\r
179 this.handles.remove();
\r
180 this.range.remove();
\r
183 .removeClass("ui-slider"
\r
184 + " ui-slider-horizontal"
\r
185 + " ui-slider-vertical"
\r
186 + " ui-slider-disabled"
\r
188 + " ui-widget-content"
\r
189 + " ui-corner-all")
\r
190 .removeData("slider")
\r
191 .unbind(".slider");
\r
193 this._mouseDestroy();
\r
197 _mouseCapture: function(event) {
\r
199 var o = this.options;
\r
204 this.elementSize = {
\r
205 width: this.element.outerWidth(),
\r
206 height: this.element.outerHeight()
\r
208 this.elementOffset = this.element.offset();
\r
210 var position = { x: event.pageX, y: event.pageY };
\r
211 var normValue = this._normValueFromMouse(position);
\r
213 var distance = this._valueMax() - this._valueMin() + 1, closestHandle;
\r
214 var self = this, index;
\r
215 this.handles.each(function(i) {
\r
216 var thisDistance = Math.abs(normValue - self.values(i));
\r
217 if (distance > thisDistance) {
\r
218 distance = thisDistance;
\r
219 closestHandle = $(this);
\r
224 // workaround for bug #3736 (if both handles of a range are at 0,
\r
225 // the first is always used as the one with least distance,
\r
226 // and moving it is obviously prevented by preventing negative ranges)
\r
227 if(o.range == true && this.values(1) == o.min) {
\r
228 closestHandle = $(this.handles[++index]);
\r
231 this._start(event, index);
\r
233 self._handleIndex = index;
\r
236 .addClass("ui-state-active")
\r
239 var offset = closestHandle.offset();
\r
240 var mouseOverHandle = !$(event.target).parents().andSelf().is('.ui-slider-handle');
\r
241 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
\r
242 left: event.pageX - offset.left - (closestHandle.width() / 2),
\r
243 top: event.pageY - offset.top
\r
244 - (closestHandle.height() / 2)
\r
245 - (parseInt(closestHandle.css('borderTopWidth'),10) || 0)
\r
246 - (parseInt(closestHandle.css('borderBottomWidth'),10) || 0)
\r
247 + (parseInt(closestHandle.css('marginTop'),10) || 0)
\r
250 normValue = this._normValueFromMouse(position);
\r
251 this._slide(event, index, normValue);
\r
256 _mouseStart: function(event) {
\r
260 _mouseDrag: function(event) {
\r
262 var position = { x: event.pageX, y: event.pageY };
\r
263 var normValue = this._normValueFromMouse(position);
\r
265 this._slide(event, this._handleIndex, normValue);
\r
271 _mouseStop: function(event) {
\r
273 this.handles.removeClass("ui-state-active");
\r
274 this._stop(event, this._handleIndex);
\r
275 this._change(event, this._handleIndex);
\r
276 this._handleIndex = null;
\r
277 this._clickOffset = null;
\r
283 _detectOrientation: function() {
\r
284 this.orientation = this.options.orientation == 'vertical' ? 'vertical' : 'horizontal';
\r
287 _normValueFromMouse: function(position) {
\r
289 var pixelTotal, pixelMouse;
\r
290 if ('horizontal' == this.orientation) {
\r
291 pixelTotal = this.elementSize.width;
\r
292 pixelMouse = position.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0);
\r
294 pixelTotal = this.elementSize.height;
\r
295 pixelMouse = position.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0);
\r
298 var percentMouse = (pixelMouse / pixelTotal);
\r
299 if (percentMouse > 1) percentMouse = 1;
\r
300 if (percentMouse < 0) percentMouse = 0;
\r
301 if ('vertical' == this.orientation)
\r
302 percentMouse = 1 - percentMouse;
\r
304 var valueTotal = this._valueMax() - this._valueMin(),
\r
305 valueMouse = percentMouse * valueTotal,
\r
306 valueMouseModStep = valueMouse % this.options.step,
\r
307 normValue = this._valueMin() + valueMouse - valueMouseModStep;
\r
309 if (valueMouseModStep > (this.options.step / 2))
\r
310 normValue += this.options.step;
\r
312 // Since JavaScript has problems with large floats, round
\r
313 // the final value to 5 digits after the decimal point (see #4124)
\r
314 return parseFloat(normValue.toFixed(5));
\r
318 _start: function(event, index) {
\r
320 handle: this.handles[index],
\r
321 value: this.value()
\r
323 if (this.options.values && this.options.values.length) {
\r
324 uiHash.value = this.values(index);
\r
325 uiHash.values = this.values();
\r
327 this._trigger("start", event, uiHash);
\r
330 _slide: function(event, index, newVal) {
\r
332 var handle = this.handles[index];
\r
334 if (this.options.values && this.options.values.length) {
\r
336 var otherVal = this.values(index ? 0 : 1);
\r
338 if ((this.options.values.length == 2 && this.options.range === true) &&
\r
339 ((index == 0 && newVal > otherVal) || (index == 1 && newVal < otherVal))){
\r
343 if (newVal != this.values(index)) {
\r
344 var newValues = this.values();
\r
345 newValues[index] = newVal;
\r
346 // A slide can be canceled by returning false from the slide callback
\r
347 var allowed = this._trigger("slide", event, {
\r
348 handle: this.handles[index],
\r
352 var otherVal = this.values(index ? 0 : 1);
\r
353 if (allowed !== false) {
\r
354 this.values(index, newVal, ( event.type == 'mousedown' && this.options.animate ), true);
\r
360 if (newVal != this.value()) {
\r
361 // A slide can be canceled by returning false from the slide callback
\r
362 var allowed = this._trigger("slide", event, {
\r
363 handle: this.handles[index],
\r
366 if (allowed !== false) {
\r
367 this._setData('value', newVal, ( event.type == 'mousedown' && this.options.animate ));
\r
376 _stop: function(event, index) {
\r
378 handle: this.handles[index],
\r
379 value: this.value()
\r
381 if (this.options.values && this.options.values.length) {
\r
382 uiHash.value = this.values(index);
\r
383 uiHash.values = this.values();
\r
385 this._trigger("stop", event, uiHash);
\r
388 _change: function(event, index) {
\r
390 handle: this.handles[index],
\r
391 value: this.value()
\r
393 if (this.options.values && this.options.values.length) {
\r
394 uiHash.value = this.values(index);
\r
395 uiHash.values = this.values();
\r
397 this._trigger("change", event, uiHash);
\r
400 value: function(newValue) {
\r
402 if (arguments.length) {
\r
403 this._setData("value", newValue);
\r
404 this._change(null, 0);
\r
407 return this._value();
\r
411 values: function(index, newValue, animated, noPropagation) {
\r
413 if (arguments.length > 1) {
\r
414 this.options.values[index] = newValue;
\r
415 this._refreshValue(animated);
\r
416 if(!noPropagation) this._change(null, index);
\r
419 if (arguments.length) {
\r
420 if (this.options.values && this.options.values.length) {
\r
421 return this._values(index);
\r
423 return this.value();
\r
426 return this._values();
\r
431 _setData: function(key, value, animated) {
\r
433 $.widget.prototype._setData.apply(this, arguments);
\r
438 this.handles.filter(".ui-state-focus").blur();
\r
439 this.handles.removeClass("ui-state-hover");
\r
440 this.handles.attr("disabled", "disabled");
\r
442 this.handles.removeAttr("disabled");
\r
444 case 'orientation':
\r
446 this._detectOrientation();
\r
449 .removeClass("ui-slider-horizontal ui-slider-vertical")
\r
450 .addClass("ui-slider-" + this.orientation);
\r
451 this._refreshValue(animated);
\r
454 this._refreshValue(animated);
\r
460 _step: function() {
\r
461 var step = this.options.step;
\r
465 _value: function() {
\r
467 var val = this.options.value;
\r
468 if (val < this._valueMin()) val = this._valueMin();
\r
469 if (val > this._valueMax()) val = this._valueMax();
\r
475 _values: function(index) {
\r
477 if (arguments.length) {
\r
478 var val = this.options.values[index];
\r
479 if (val < this._valueMin()) val = this._valueMin();
\r
480 if (val > this._valueMax()) val = this._valueMax();
\r
484 return this.options.values;
\r
489 _valueMin: function() {
\r
490 var valueMin = this.options.min;
\r
494 _valueMax: function() {
\r
495 var valueMax = this.options.max;
\r
499 _refreshValue: function(animate) {
\r
501 var oRange = this.options.range, o = this.options, self = this;
\r
503 if (this.options.values && this.options.values.length) {
\r
505 this.handles.each(function(i, j) {
\r
506 var valPercent = (self.values(i) - self._valueMin()) / (self._valueMax() - self._valueMin()) * 100;
\r
507 var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
\r
508 $(this).stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
\r
509 if (self.options.range === true) {
\r
510 if (self.orientation == 'horizontal') {
\r
511 (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ left: valPercent + '%' }, o.animate);
\r
512 (i == 1) && self.range[animate ? 'animate' : 'css']({ width: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
\r
514 (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ bottom: (valPercent) + '%' }, o.animate);
\r
515 (i == 1) && self.range[animate ? 'animate' : 'css']({ height: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
\r
518 lastValPercent = valPercent;
\r
521 var value = this.value(),
\r
522 valueMin = this._valueMin(),
\r
523 valueMax = this._valueMax(),
\r
524 valPercent = valueMax != valueMin
\r
525 ? (value - valueMin) / (valueMax - valueMin) * 100
\r
527 var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
\r
528 this.handle.stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
\r
530 (oRange == "min") && (this.orientation == "horizontal") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ width: valPercent + '%' }, o.animate);
\r
531 (oRange == "max") && (this.orientation == "horizontal") && this.range[animate ? 'animate' : 'css']({ width: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
\r
532 (oRange == "min") && (this.orientation == "vertical") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ height: valPercent + '%' }, o.animate);
\r
533 (oRange == "max") && (this.orientation == "vertical") && this.range[animate ? 'animate' : 'css']({ height: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
\r
540 $.extend($.ui.slider, {
\r
541 getter: "value values",
\r
543 eventPrefix: "slide",
\r
550 orientation: 'horizontal',
\r