michael@0: // script.aculo.us slider.js v1.7.1_beta2, Tue May 15 15:15:45 EDT 2007 michael@0: michael@0: // Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs michael@0: // michael@0: // script.aculo.us is freely distributable under the terms of an MIT-style license. michael@0: // For details, see the script.aculo.us web site: http://script.aculo.us/ michael@0: michael@0: if(!Control) var Control = {}; michael@0: Control.Slider = Class.create(); michael@0: michael@0: // options: michael@0: // axis: 'vertical', or 'horizontal' (default) michael@0: // michael@0: // callbacks: michael@0: // onChange(value) michael@0: // onSlide(value) michael@0: Control.Slider.prototype = { michael@0: initialize: function(handle, track, options) { michael@0: var slider = this; michael@0: michael@0: if(handle instanceof Array) { michael@0: this.handles = handle.collect( function(e) { return $(e) }); michael@0: } else { michael@0: this.handles = [$(handle)]; michael@0: } michael@0: michael@0: this.track = $(track); michael@0: this.options = options || {}; michael@0: michael@0: this.axis = this.options.axis || 'horizontal'; michael@0: this.increment = this.options.increment || 1; michael@0: this.step = parseInt(this.options.step || '1'); michael@0: this.range = this.options.range || $R(0,1); michael@0: michael@0: this.value = 0; // assure backwards compat michael@0: this.values = this.handles.map( function() { return 0 }); michael@0: this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false; michael@0: this.options.startSpan = $(this.options.startSpan || null); michael@0: this.options.endSpan = $(this.options.endSpan || null); michael@0: michael@0: this.restricted = this.options.restricted || false; michael@0: michael@0: this.maximum = this.options.maximum || this.range.end; michael@0: this.minimum = this.options.minimum || this.range.start; michael@0: michael@0: // Will be used to align the handle onto the track, if necessary michael@0: this.alignX = parseInt(this.options.alignX || '0'); michael@0: this.alignY = parseInt(this.options.alignY || '0'); michael@0: michael@0: this.trackLength = this.maximumOffset() - this.minimumOffset(); michael@0: michael@0: this.handleLength = this.isVertical() ? michael@0: (this.handles[0].offsetHeight != 0 ? michael@0: this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : michael@0: (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : michael@0: this.handles[0].style.width.replace(/px$/,"")); michael@0: michael@0: this.active = false; michael@0: this.dragging = false; michael@0: this.disabled = false; michael@0: michael@0: if(this.options.disabled) this.setDisabled(); michael@0: michael@0: // Allowed values array michael@0: this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false; michael@0: if(this.allowedValues) { michael@0: this.minimum = this.allowedValues.min(); michael@0: this.maximum = this.allowedValues.max(); michael@0: } michael@0: michael@0: this.eventMouseDown = this.startDrag.bindAsEventListener(this); michael@0: this.eventMouseUp = this.endDrag.bindAsEventListener(this); michael@0: this.eventMouseMove = this.update.bindAsEventListener(this); michael@0: michael@0: // Initialize handles in reverse (make sure first handle is active) michael@0: this.handles.each( function(h,i) { michael@0: i = slider.handles.length-1-i; michael@0: slider.setValue(parseFloat( michael@0: (slider.options.sliderValue instanceof Array ? michael@0: slider.options.sliderValue[i] : slider.options.sliderValue) || michael@0: slider.range.start), i); michael@0: Element.makePositioned(h); // fix IE michael@0: Event.observe(h, "mousedown", slider.eventMouseDown); michael@0: }); michael@0: michael@0: Event.observe(this.track, "mousedown", this.eventMouseDown); michael@0: Event.observe(document, "mouseup", this.eventMouseUp); michael@0: Event.observe(document, "mousemove", this.eventMouseMove); michael@0: michael@0: this.initialized = true; michael@0: }, michael@0: dispose: function() { michael@0: var slider = this; michael@0: Event.stopObserving(this.track, "mousedown", this.eventMouseDown); michael@0: Event.stopObserving(document, "mouseup", this.eventMouseUp); michael@0: Event.stopObserving(document, "mousemove", this.eventMouseMove); michael@0: this.handles.each( function(h) { michael@0: Event.stopObserving(h, "mousedown", slider.eventMouseDown); michael@0: }); michael@0: }, michael@0: setDisabled: function(){ michael@0: this.disabled = true; michael@0: }, michael@0: setEnabled: function(){ michael@0: this.disabled = false; michael@0: }, michael@0: getNearestValue: function(value){ michael@0: if(this.allowedValues){ michael@0: if(value >= this.allowedValues.max()) return(this.allowedValues.max()); michael@0: if(value <= this.allowedValues.min()) return(this.allowedValues.min()); michael@0: michael@0: var offset = Math.abs(this.allowedValues[0] - value); michael@0: var newValue = this.allowedValues[0]; michael@0: this.allowedValues.each( function(v) { michael@0: var currentOffset = Math.abs(v - value); michael@0: if(currentOffset <= offset){ michael@0: newValue = v; michael@0: offset = currentOffset; michael@0: } michael@0: }); michael@0: return newValue; michael@0: } michael@0: if(value > this.range.end) return this.range.end; michael@0: if(value < this.range.start) return this.range.start; michael@0: return value; michael@0: }, michael@0: setValue: function(sliderValue, handleIdx){ michael@0: if(!this.active) { michael@0: this.activeHandleIdx = handleIdx || 0; michael@0: this.activeHandle = this.handles[this.activeHandleIdx]; michael@0: this.updateStyles(); michael@0: } michael@0: handleIdx = handleIdx || this.activeHandleIdx || 0; michael@0: if(this.initialized && this.restricted) { michael@0: if((handleIdx>0) && (sliderValuethis.values[handleIdx+1])) michael@0: sliderValue = this.values[handleIdx+1]; michael@0: } michael@0: sliderValue = this.getNearestValue(sliderValue); michael@0: this.values[handleIdx] = sliderValue; michael@0: this.value = this.values[0]; // assure backwards compat michael@0: michael@0: this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = michael@0: this.translateToPx(sliderValue); michael@0: michael@0: this.drawSpans(); michael@0: if(!this.dragging || !this.event) this.updateFinished(); michael@0: }, michael@0: setValueBy: function(delta, handleIdx) { michael@0: this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, michael@0: handleIdx || this.activeHandleIdx || 0); michael@0: }, michael@0: translateToPx: function(value) { michael@0: return Math.round( michael@0: ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * michael@0: (value - this.range.start)) + "px"; michael@0: }, michael@0: translateToValue: function(offset) { michael@0: return ((offset/(this.trackLength-this.handleLength) * michael@0: (this.range.end-this.range.start)) + this.range.start); michael@0: }, michael@0: getRange: function(range) { michael@0: var v = this.values.sortBy(Prototype.K); michael@0: range = range || 0; michael@0: return $R(v[range],v[range+1]); michael@0: }, michael@0: minimumOffset: function(){ michael@0: return(this.isVertical() ? this.alignY : this.alignX); michael@0: }, michael@0: maximumOffset: function(){ michael@0: return(this.isVertical() ? michael@0: (this.track.offsetHeight != 0 ? this.track.offsetHeight : michael@0: this.track.style.height.replace(/px$/,"")) - this.alignY : michael@0: (this.track.offsetWidth != 0 ? this.track.offsetWidth : michael@0: this.track.style.width.replace(/px$/,"")) - this.alignY); michael@0: }, michael@0: isVertical: function(){ michael@0: return (this.axis == 'vertical'); michael@0: }, michael@0: drawSpans: function() { michael@0: var slider = this; michael@0: if(this.spans) michael@0: $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); michael@0: if(this.options.startSpan) michael@0: this.setSpan(this.options.startSpan, michael@0: $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); michael@0: if(this.options.endSpan) michael@0: this.setSpan(this.options.endSpan, michael@0: $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); michael@0: }, michael@0: setSpan: function(span, range) { michael@0: if(this.isVertical()) { michael@0: span.style.top = this.translateToPx(range.start); michael@0: span.style.height = this.translateToPx(range.end - range.start + this.range.start); michael@0: } else { michael@0: span.style.left = this.translateToPx(range.start); michael@0: span.style.width = this.translateToPx(range.end - range.start + this.range.start); michael@0: } michael@0: }, michael@0: updateStyles: function() { michael@0: this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); michael@0: Element.addClassName(this.activeHandle, 'selected'); michael@0: }, michael@0: startDrag: function(event) { michael@0: if(Event.isLeftClick(event)) { michael@0: if(!this.disabled){ michael@0: this.active = true; michael@0: michael@0: var handle = Event.element(event); michael@0: var pointer = [Event.pointerX(event), Event.pointerY(event)]; michael@0: var track = handle; michael@0: if(track==this.track) { michael@0: var offsets = Position.cumulativeOffset(this.track); michael@0: this.event = event; michael@0: this.setValue(this.translateToValue( michael@0: (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) michael@0: )); michael@0: var offsets = Position.cumulativeOffset(this.activeHandle); michael@0: this.offsetX = (pointer[0] - offsets[0]); michael@0: this.offsetY = (pointer[1] - offsets[1]); michael@0: } else { michael@0: // find the handle (prevents issues with Safari) michael@0: while((this.handles.indexOf(handle) == -1) && handle.parentNode) michael@0: handle = handle.parentNode; michael@0: michael@0: if(this.handles.indexOf(handle)!=-1) { michael@0: this.activeHandle = handle; michael@0: this.activeHandleIdx = this.handles.indexOf(this.activeHandle); michael@0: this.updateStyles(); michael@0: michael@0: var offsets = Position.cumulativeOffset(this.activeHandle); michael@0: this.offsetX = (pointer[0] - offsets[0]); michael@0: this.offsetY = (pointer[1] - offsets[1]); michael@0: } michael@0: } michael@0: } michael@0: Event.stop(event); michael@0: } michael@0: }, michael@0: update: function(event) { michael@0: if(this.active) { michael@0: if(!this.dragging) this.dragging = true; michael@0: this.draw(event); michael@0: if(Prototype.Browser.WebKit) window.scrollBy(0,0); michael@0: Event.stop(event); michael@0: } michael@0: }, michael@0: draw: function(event) { michael@0: var pointer = [Event.pointerX(event), Event.pointerY(event)]; michael@0: var offsets = Position.cumulativeOffset(this.track); michael@0: pointer[0] -= this.offsetX + offsets[0]; michael@0: pointer[1] -= this.offsetY + offsets[1]; michael@0: this.event = event; michael@0: this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); michael@0: if(this.initialized && this.options.onSlide) michael@0: this.options.onSlide(this.values.length>1 ? this.values : this.value, this); michael@0: }, michael@0: endDrag: function(event) { michael@0: if(this.active && this.dragging) { michael@0: this.finishDrag(event, true); michael@0: Event.stop(event); michael@0: } michael@0: this.active = false; michael@0: this.dragging = false; michael@0: }, michael@0: finishDrag: function(event, success) { michael@0: this.active = false; michael@0: this.dragging = false; michael@0: this.updateFinished(); michael@0: }, michael@0: updateFinished: function() { michael@0: if(this.initialized && this.options.onChange) michael@0: this.options.onChange(this.values.length>1 ? this.values : this.value, this); michael@0: this.event = null; michael@0: } michael@0: }