slider.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // script.aculo.us slider.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
  2. // Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs
  3. //
  4. // script.aculo.us is freely distributable under the terms of an MIT-style license.
  5. // For details, see the script.aculo.us web site: http://script.aculo.us/
  6. if (!Control) var Control = { };
  7. // options:
  8. // axis: 'vertical', or 'horizontal' (default)
  9. //
  10. // callbacks:
  11. // onChange(value)
  12. // onSlide(value)
  13. Control.Slider = Class.create({
  14. initialize: function(handle, track, options) {
  15. var slider = this;
  16. if (Object.isArray(handle)) {
  17. this.handles = handle.collect( function(e) { return $(e) });
  18. } else {
  19. this.handles = [$(handle)];
  20. }
  21. this.track = $(track);
  22. this.options = options || { };
  23. this.axis = this.options.axis || 'horizontal';
  24. this.increment = this.options.increment || 1;
  25. this.step = parseInt(this.options.step || '1');
  26. this.range = this.options.range || $R(0,1);
  27. this.value = 0; // assure backwards compat
  28. this.values = this.handles.map( function() { return 0 });
  29. this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
  30. this.options.startSpan = $(this.options.startSpan || null);
  31. this.options.endSpan = $(this.options.endSpan || null);
  32. this.restricted = this.options.restricted || false;
  33. this.maximum = this.options.maximum || this.range.end;
  34. this.minimum = this.options.minimum || this.range.start;
  35. // Will be used to align the handle onto the track, if necessary
  36. this.alignX = parseInt(this.options.alignX || '0');
  37. this.alignY = parseInt(this.options.alignY || '0');
  38. this.trackLength = this.maximumOffset() - this.minimumOffset();
  39. this.handleLength = this.isVertical() ?
  40. (this.handles[0].offsetHeight != 0 ?
  41. this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
  42. (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
  43. this.handles[0].style.width.replace(/px$/,""));
  44. this.active = false;
  45. this.dragging = false;
  46. this.disabled = false;
  47. if (this.options.disabled) this.setDisabled();
  48. // Allowed values array
  49. this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
  50. if (this.allowedValues) {
  51. this.minimum = this.allowedValues.min();
  52. this.maximum = this.allowedValues.max();
  53. }
  54. this.eventMouseDown = this.startDrag.bindAsEventListener(this);
  55. this.eventMouseUp = this.endDrag.bindAsEventListener(this);
  56. this.eventMouseMove = this.update.bindAsEventListener(this);
  57. // Initialize handles in reverse (make sure first handle is active)
  58. this.handles.each( function(h,i) {
  59. i = slider.handles.length-1-i;
  60. slider.setValue(parseFloat(
  61. (Object.isArray(slider.options.sliderValue) ?
  62. slider.options.sliderValue[i] : slider.options.sliderValue) ||
  63. slider.range.start), i);
  64. h.makePositioned().observe("mousedown", slider.eventMouseDown);
  65. });
  66. this.track.observe("mousedown", this.eventMouseDown);
  67. document.observe("mouseup", this.eventMouseUp);
  68. document.observe("mousemove", this.eventMouseMove);
  69. this.initialized = true;
  70. },
  71. dispose: function() {
  72. var slider = this;
  73. Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
  74. Event.stopObserving(document, "mouseup", this.eventMouseUp);
  75. Event.stopObserving(document, "mousemove", this.eventMouseMove);
  76. this.handles.each( function(h) {
  77. Event.stopObserving(h, "mousedown", slider.eventMouseDown);
  78. });
  79. },
  80. setDisabled: function(){
  81. this.disabled = true;
  82. },
  83. setEnabled: function(){
  84. this.disabled = false;
  85. },
  86. getNearestValue: function(value){
  87. if (this.allowedValues){
  88. if (value >= this.allowedValues.max()) return(this.allowedValues.max());
  89. if (value <= this.allowedValues.min()) return(this.allowedValues.min());
  90. var offset = Math.abs(this.allowedValues[0] - value);
  91. var newValue = this.allowedValues[0];
  92. this.allowedValues.each( function(v) {
  93. var currentOffset = Math.abs(v - value);
  94. if (currentOffset <= offset){
  95. newValue = v;
  96. offset = currentOffset;
  97. }
  98. });
  99. return newValue;
  100. }
  101. if (value > this.range.end) return this.range.end;
  102. if (value < this.range.start) return this.range.start;
  103. return value;
  104. },
  105. setValue: function(sliderValue, handleIdx){
  106. if (!this.active) {
  107. this.activeHandleIdx = handleIdx || 0;
  108. this.activeHandle = this.handles[this.activeHandleIdx];
  109. this.updateStyles();
  110. }
  111. handleIdx = handleIdx || this.activeHandleIdx || 0;
  112. if (this.initialized && this.restricted) {
  113. if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
  114. sliderValue = this.values[handleIdx-1];
  115. if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
  116. sliderValue = this.values[handleIdx+1];
  117. }
  118. sliderValue = this.getNearestValue(sliderValue);
  119. this.values[handleIdx] = sliderValue;
  120. this.value = this.values[0]; // assure backwards compat
  121. this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
  122. this.translateToPx(sliderValue);
  123. this.drawSpans();
  124. if (!this.dragging || !this.event) this.updateFinished();
  125. },
  126. setValueBy: function(delta, handleIdx) {
  127. this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
  128. handleIdx || this.activeHandleIdx || 0);
  129. },
  130. translateToPx: function(value) {
  131. return Math.round(
  132. ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
  133. (value - this.range.start)) + "px";
  134. },
  135. translateToValue: function(offset) {
  136. return ((offset/(this.trackLength-this.handleLength) *
  137. (this.range.end-this.range.start)) + this.range.start);
  138. },
  139. getRange: function(range) {
  140. var v = this.values.sortBy(Prototype.K);
  141. range = range || 0;
  142. return $R(v[range],v[range+1]);
  143. },
  144. minimumOffset: function(){
  145. return(this.isVertical() ? this.alignY : this.alignX);
  146. },
  147. maximumOffset: function(){
  148. return(this.isVertical() ?
  149. (this.track.offsetHeight != 0 ? this.track.offsetHeight :
  150. this.track.style.height.replace(/px$/,"")) - this.alignY :
  151. (this.track.offsetWidth != 0 ? this.track.offsetWidth :
  152. this.track.style.width.replace(/px$/,"")) - this.alignX);
  153. },
  154. isVertical: function(){
  155. return (this.axis == 'vertical');
  156. },
  157. drawSpans: function() {
  158. var slider = this;
  159. if (this.spans)
  160. $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
  161. if (this.options.startSpan)
  162. this.setSpan(this.options.startSpan,
  163. $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
  164. if (this.options.endSpan)
  165. this.setSpan(this.options.endSpan,
  166. $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
  167. },
  168. setSpan: function(span, range) {
  169. if (this.isVertical()) {
  170. span.style.top = this.translateToPx(range.start);
  171. span.style.height = this.translateToPx(range.end - range.start + this.range.start);
  172. } else {
  173. span.style.left = this.translateToPx(range.start);
  174. span.style.width = this.translateToPx(range.end - range.start + this.range.start);
  175. }
  176. },
  177. updateStyles: function() {
  178. this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
  179. Element.addClassName(this.activeHandle, 'selected');
  180. },
  181. startDrag: function(event) {
  182. if (Event.isLeftClick(event)) {
  183. if (!this.disabled){
  184. this.active = true;
  185. var handle = Event.element(event);
  186. var pointer = [Event.pointerX(event), Event.pointerY(event)];
  187. var track = handle;
  188. if (track==this.track) {
  189. var offsets = Position.cumulativeOffset(this.track);
  190. this.event = event;
  191. this.setValue(this.translateToValue(
  192. (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
  193. ));
  194. var offsets = Position.cumulativeOffset(this.activeHandle);
  195. this.offsetX = (pointer[0] - offsets[0]);
  196. this.offsetY = (pointer[1] - offsets[1]);
  197. } else {
  198. // find the handle (prevents issues with Safari)
  199. while((this.handles.indexOf(handle) == -1) && handle.parentNode)
  200. handle = handle.parentNode;
  201. if (this.handles.indexOf(handle)!=-1) {
  202. this.activeHandle = handle;
  203. this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
  204. this.updateStyles();
  205. var offsets = Position.cumulativeOffset(this.activeHandle);
  206. this.offsetX = (pointer[0] - offsets[0]);
  207. this.offsetY = (pointer[1] - offsets[1]);
  208. }
  209. }
  210. }
  211. Event.stop(event);
  212. }
  213. },
  214. update: function(event) {
  215. if (this.active) {
  216. if (!this.dragging) this.dragging = true;
  217. this.draw(event);
  218. if (Prototype.Browser.WebKit) window.scrollBy(0,0);
  219. Event.stop(event);
  220. }
  221. },
  222. draw: function(event) {
  223. var pointer = [Event.pointerX(event), Event.pointerY(event)];
  224. var offsets = Position.cumulativeOffset(this.track);
  225. pointer[0] -= this.offsetX + offsets[0];
  226. pointer[1] -= this.offsetY + offsets[1];
  227. this.event = event;
  228. this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
  229. if (this.initialized && this.options.onSlide)
  230. this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
  231. },
  232. endDrag: function(event) {
  233. if (this.active && this.dragging) {
  234. this.finishDrag(event, true);
  235. Event.stop(event);
  236. }
  237. this.active = false;
  238. this.dragging = false;
  239. },
  240. finishDrag: function(event, success) {
  241. this.active = false;
  242. this.dragging = false;
  243. this.updateFinished();
  244. },
  245. updateFinished: function() {
  246. if (this.initialized && this.options.onChange)
  247. this.options.onChange(this.values.length>1 ? this.values : this.value, this);
  248. this.event = null;
  249. }
  250. });