ui.slider.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /*
  2. * jQuery UI Slider 1.7.2
  3. *
  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.
  7. *
  8. * http://docs.jquery.com/UI/Slider
  9. *
  10. * Depends:
  11. * ui.core.js
  12. */
  13. (function($) {
  14. $.widget("ui.slider", $.extend({}, $.ui.mouse, {
  15. _init: function() {
  16. var self = this, o = this.options;
  17. this._keySliding = false;
  18. this._handleIndex = null;
  19. this._detectOrientation();
  20. this._mouseInit();
  21. this.element
  22. .addClass("ui-slider"
  23. + " ui-slider-" + this.orientation
  24. + " ui-widget"
  25. + " ui-widget-content"
  26. + " ui-corner-all");
  27. this.range = $([]);
  28. if (o.range) {
  29. if (o.range === true) {
  30. this.range = $('<div></div>');
  31. if (!o.values) o.values = [this._valueMin(), this._valueMin()];
  32. if (o.values.length && o.values.length != 2) {
  33. o.values = [o.values[0], o.values[0]];
  34. }
  35. } else {
  36. this.range = $('<div></div>');
  37. }
  38. this.range
  39. .appendTo(this.element)
  40. .addClass("ui-slider-range");
  41. if (o.range == "min" || o.range == "max") {
  42. this.range.addClass("ui-slider-range-" + o.range);
  43. }
  44. // note: this isn't the most fittingly semantic framework class for this element,
  45. // but worked best visually with a variety of themes
  46. this.range.addClass("ui-widget-header");
  47. }
  48. if ($(".ui-slider-handle", this.element).length == 0)
  49. $('<a href="#"></a>')
  50. .appendTo(this.element)
  51. .addClass("ui-slider-handle");
  52. if (o.values && o.values.length) {
  53. while ($(".ui-slider-handle", this.element).length < o.values.length)
  54. $('<a href="#"></a>')
  55. .appendTo(this.element)
  56. .addClass("ui-slider-handle");
  57. }
  58. this.handles = $(".ui-slider-handle", this.element)
  59. .addClass("ui-state-default"
  60. + " ui-corner-all");
  61. this.handle = this.handles.eq(0);
  62. this.handles.add(this.range).filter("a")
  63. .click(function(event) {
  64. event.preventDefault();
  65. })
  66. .hover(function() {
  67. if (!o.disabled) {
  68. $(this).addClass('ui-state-hover');
  69. }
  70. }, function() {
  71. $(this).removeClass('ui-state-hover');
  72. })
  73. .focus(function() {
  74. if (!o.disabled) {
  75. $(".ui-slider .ui-state-focus").removeClass('ui-state-focus'); $(this).addClass('ui-state-focus');
  76. } else {
  77. $(this).blur();
  78. }
  79. })
  80. .blur(function() {
  81. $(this).removeClass('ui-state-focus');
  82. });
  83. this.handles.each(function(i) {
  84. $(this).data("index.ui-slider-handle", i);
  85. });
  86. this.handles.keydown(function(event) {
  87. var ret = true;
  88. var index = $(this).data("index.ui-slider-handle");
  89. if (self.options.disabled)
  90. return;
  91. switch (event.keyCode) {
  92. case $.ui.keyCode.HOME:
  93. case $.ui.keyCode.END:
  94. case $.ui.keyCode.UP:
  95. case $.ui.keyCode.RIGHT:
  96. case $.ui.keyCode.DOWN:
  97. case $.ui.keyCode.LEFT:
  98. ret = false;
  99. if (!self._keySliding) {
  100. self._keySliding = true;
  101. $(this).addClass("ui-state-active");
  102. self._start(event, index);
  103. }
  104. break;
  105. }
  106. var curVal, newVal, step = self._step();
  107. if (self.options.values && self.options.values.length) {
  108. curVal = newVal = self.values(index);
  109. } else {
  110. curVal = newVal = self.value();
  111. }
  112. switch (event.keyCode) {
  113. case $.ui.keyCode.HOME:
  114. newVal = self._valueMin();
  115. break;
  116. case $.ui.keyCode.END:
  117. newVal = self._valueMax();
  118. break;
  119. case $.ui.keyCode.UP:
  120. case $.ui.keyCode.RIGHT:
  121. if(curVal == self._valueMax()) return;
  122. newVal = curVal + step;
  123. break;
  124. case $.ui.keyCode.DOWN:
  125. case $.ui.keyCode.LEFT:
  126. if(curVal == self._valueMin()) return;
  127. newVal = curVal - step;
  128. break;
  129. }
  130. self._slide(event, index, newVal);
  131. return ret;
  132. }).keyup(function(event) {
  133. var index = $(this).data("index.ui-slider-handle");
  134. if (self._keySliding) {
  135. self._stop(event, index);
  136. self._change(event, index);
  137. self._keySliding = false;
  138. $(this).removeClass("ui-state-active");
  139. }
  140. });
  141. this._refreshValue();
  142. },
  143. destroy: function() {
  144. this.handles.remove();
  145. this.range.remove();
  146. this.element
  147. .removeClass("ui-slider"
  148. + " ui-slider-horizontal"
  149. + " ui-slider-vertical"
  150. + " ui-slider-disabled"
  151. + " ui-widget"
  152. + " ui-widget-content"
  153. + " ui-corner-all")
  154. .removeData("slider")
  155. .unbind(".slider");
  156. this._mouseDestroy();
  157. },
  158. _mouseCapture: function(event) {
  159. var o = this.options;
  160. if (o.disabled)
  161. return false;
  162. this.elementSize = {
  163. width: this.element.outerWidth(),
  164. height: this.element.outerHeight()
  165. };
  166. this.elementOffset = this.element.offset();
  167. var position = { x: event.pageX, y: event.pageY };
  168. var normValue = this._normValueFromMouse(position);
  169. var distance = this._valueMax() - this._valueMin() + 1, closestHandle;
  170. var self = this, index;
  171. this.handles.each(function(i) {
  172. var thisDistance = Math.abs(normValue - self.values(i));
  173. if (distance > thisDistance) {
  174. distance = thisDistance;
  175. closestHandle = $(this);
  176. index = i;
  177. }
  178. });
  179. // workaround for bug #3736 (if both handles of a range are at 0,
  180. // the first is always used as the one with least distance,
  181. // and moving it is obviously prevented by preventing negative ranges)
  182. if(o.range == true && this.values(1) == o.min) {
  183. closestHandle = $(this.handles[++index]);
  184. }
  185. this._start(event, index);
  186. self._handleIndex = index;
  187. closestHandle
  188. .addClass("ui-state-active")
  189. .focus();
  190. var offset = closestHandle.offset();
  191. var mouseOverHandle = !$(event.target).parents().andSelf().is('.ui-slider-handle');
  192. this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
  193. left: event.pageX - offset.left - (closestHandle.width() / 2),
  194. top: event.pageY - offset.top
  195. - (closestHandle.height() / 2)
  196. - (parseInt(closestHandle.css('borderTopWidth'),10) || 0)
  197. - (parseInt(closestHandle.css('borderBottomWidth'),10) || 0)
  198. + (parseInt(closestHandle.css('marginTop'),10) || 0)
  199. };
  200. normValue = this._normValueFromMouse(position);
  201. this._slide(event, index, normValue);
  202. return true;
  203. },
  204. _mouseStart: function(event) {
  205. return true;
  206. },
  207. _mouseDrag: function(event) {
  208. var position = { x: event.pageX, y: event.pageY };
  209. var normValue = this._normValueFromMouse(position);
  210. this._slide(event, this._handleIndex, normValue);
  211. return false;
  212. },
  213. _mouseStop: function(event) {
  214. this.handles.removeClass("ui-state-active");
  215. this._stop(event, this._handleIndex);
  216. this._change(event, this._handleIndex);
  217. this._handleIndex = null;
  218. this._clickOffset = null;
  219. return false;
  220. },
  221. _detectOrientation: function() {
  222. this.orientation = this.options.orientation == 'vertical' ? 'vertical' : 'horizontal';
  223. },
  224. _normValueFromMouse: function(position) {
  225. var pixelTotal, pixelMouse;
  226. if ('horizontal' == this.orientation) {
  227. pixelTotal = this.elementSize.width;
  228. pixelMouse = position.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0);
  229. } else {
  230. pixelTotal = this.elementSize.height;
  231. pixelMouse = position.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0);
  232. }
  233. var percentMouse = (pixelMouse / pixelTotal);
  234. if (percentMouse > 1) percentMouse = 1;
  235. if (percentMouse < 0) percentMouse = 0;
  236. if ('vertical' == this.orientation)
  237. percentMouse = 1 - percentMouse;
  238. var valueTotal = this._valueMax() - this._valueMin(),
  239. valueMouse = percentMouse * valueTotal,
  240. valueMouseModStep = valueMouse % this.options.step,
  241. normValue = this._valueMin() + valueMouse - valueMouseModStep;
  242. if (valueMouseModStep > (this.options.step / 2))
  243. normValue += this.options.step;
  244. // Since JavaScript has problems with large floats, round
  245. // the final value to 5 digits after the decimal point (see #4124)
  246. return parseFloat(normValue.toFixed(5));
  247. },
  248. _start: function(event, index) {
  249. var uiHash = {
  250. handle: this.handles[index],
  251. value: this.value()
  252. };
  253. if (this.options.values && this.options.values.length) {
  254. uiHash.value = this.values(index);
  255. uiHash.values = this.values();
  256. }
  257. this._trigger("start", event, uiHash);
  258. },
  259. _slide: function(event, index, newVal) {
  260. var handle = this.handles[index];
  261. if (this.options.values && this.options.values.length) {
  262. var otherVal = this.values(index ? 0 : 1);
  263. if ((this.options.values.length == 2 && this.options.range === true) &&
  264. ((index == 0 && newVal > otherVal) || (index == 1 && newVal < otherVal))){
  265. newVal = otherVal;
  266. }
  267. if (newVal != this.values(index)) {
  268. var newValues = this.values();
  269. newValues[index] = newVal;
  270. // A slide can be canceled by returning false from the slide callback
  271. var allowed = this._trigger("slide", event, {
  272. handle: this.handles[index],
  273. value: newVal,
  274. values: newValues
  275. });
  276. var otherVal = this.values(index ? 0 : 1);
  277. if (allowed !== false) {
  278. this.values(index, newVal, ( event.type == 'mousedown' && this.options.animate ), true);
  279. }
  280. }
  281. } else {
  282. if (newVal != this.value()) {
  283. // A slide can be canceled by returning false from the slide callback
  284. var allowed = this._trigger("slide", event, {
  285. handle: this.handles[index],
  286. value: newVal
  287. });
  288. if (allowed !== false) {
  289. this._setData('value', newVal, ( event.type == 'mousedown' && this.options.animate ));
  290. }
  291. }
  292. }
  293. },
  294. _stop: function(event, index) {
  295. var uiHash = {
  296. handle: this.handles[index],
  297. value: this.value()
  298. };
  299. if (this.options.values && this.options.values.length) {
  300. uiHash.value = this.values(index);
  301. uiHash.values = this.values();
  302. }
  303. this._trigger("stop", event, uiHash);
  304. },
  305. _change: function(event, index) {
  306. var uiHash = {
  307. handle: this.handles[index],
  308. value: this.value()
  309. };
  310. if (this.options.values && this.options.values.length) {
  311. uiHash.value = this.values(index);
  312. uiHash.values = this.values();
  313. }
  314. this._trigger("change", event, uiHash);
  315. },
  316. value: function(newValue) {
  317. if (arguments.length) {
  318. this._setData("value", newValue);
  319. this._change(null, 0);
  320. }
  321. return this._value();
  322. },
  323. values: function(index, newValue, animated, noPropagation) {
  324. if (arguments.length > 1) {
  325. this.options.values[index] = newValue;
  326. this._refreshValue(animated);
  327. if(!noPropagation) this._change(null, index);
  328. }
  329. if (arguments.length) {
  330. if (this.options.values && this.options.values.length) {
  331. return this._values(index);
  332. } else {
  333. return this.value();
  334. }
  335. } else {
  336. return this._values();
  337. }
  338. },
  339. _setData: function(key, value, animated) {
  340. $.widget.prototype._setData.apply(this, arguments);
  341. switch (key) {
  342. case 'disabled':
  343. if (value) {
  344. this.handles.filter(".ui-state-focus").blur();
  345. this.handles.removeClass("ui-state-hover");
  346. this.handles.attr("disabled", "disabled");
  347. } else {
  348. this.handles.removeAttr("disabled");
  349. }
  350. case 'orientation':
  351. this._detectOrientation();
  352. this.element
  353. .removeClass("ui-slider-horizontal ui-slider-vertical")
  354. .addClass("ui-slider-" + this.orientation);
  355. this._refreshValue(animated);
  356. break;
  357. case 'value':
  358. this._refreshValue(animated);
  359. break;
  360. }
  361. },
  362. _step: function() {
  363. var step = this.options.step;
  364. return step;
  365. },
  366. _value: function() {
  367. var val = this.options.value;
  368. if (val < this._valueMin()) val = this._valueMin();
  369. if (val > this._valueMax()) val = this._valueMax();
  370. return val;
  371. },
  372. _values: function(index) {
  373. if (arguments.length) {
  374. var val = this.options.values[index];
  375. if (val < this._valueMin()) val = this._valueMin();
  376. if (val > this._valueMax()) val = this._valueMax();
  377. return val;
  378. } else {
  379. return this.options.values;
  380. }
  381. },
  382. _valueMin: function() {
  383. var valueMin = this.options.min;
  384. return valueMin;
  385. },
  386. _valueMax: function() {
  387. var valueMax = this.options.max;
  388. return valueMax;
  389. },
  390. _refreshValue: function(animate) {
  391. var oRange = this.options.range, o = this.options, self = this;
  392. if (this.options.values && this.options.values.length) {
  393. var vp0, vp1;
  394. this.handles.each(function(i, j) {
  395. var valPercent = (self.values(i) - self._valueMin()) / (self._valueMax() - self._valueMin()) * 100;
  396. var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
  397. $(this).stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
  398. if (self.options.range === true) {
  399. if (self.orientation == 'horizontal') {
  400. (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ left: valPercent + '%' }, o.animate);
  401. (i == 1) && self.range[animate ? 'animate' : 'css']({ width: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
  402. } else {
  403. (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ bottom: (valPercent) + '%' }, o.animate);
  404. (i == 1) && self.range[animate ? 'animate' : 'css']({ height: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
  405. }
  406. }
  407. lastValPercent = valPercent;
  408. });
  409. } else {
  410. var value = this.value(),
  411. valueMin = this._valueMin(),
  412. valueMax = this._valueMax(),
  413. valPercent = valueMax != valueMin
  414. ? (value - valueMin) / (valueMax - valueMin) * 100
  415. : 0;
  416. var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
  417. this.handle.stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
  418. (oRange == "min") && (this.orientation == "horizontal") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ width: valPercent + '%' }, o.animate);
  419. (oRange == "max") && (this.orientation == "horizontal") && this.range[animate ? 'animate' : 'css']({ width: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
  420. (oRange == "min") && (this.orientation == "vertical") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ height: valPercent + '%' }, o.animate);
  421. (oRange == "max") && (this.orientation == "vertical") && this.range[animate ? 'animate' : 'css']({ height: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
  422. }
  423. }
  424. }));
  425. $.extend($.ui.slider, {
  426. getter: "value values",
  427. version: "1.7.2",
  428. eventPrefix: "slide",
  429. defaults: {
  430. animate: false,
  431. delay: 0,
  432. distance: 0,
  433. max: 100,
  434. min: 0,
  435. orientation: 'horizontal',
  436. range: false,
  437. step: 1,
  438. value: 0,
  439. values: null
  440. }
  441. });
  442. })(jQuery);