/** * @class Ext.Slider * @extends Ext.BoxComponent * Slider which supports vertical or horizontal orientation, keyboard adjustments, * configurable snapping, axis clicking and animation. Can be added as an item to * any container. Example usage:

new Ext.Slider({
    renderTo: Ext.getBody(),
    width: 200,
    value: 50,
    increment: 10,
    minValue: 0,
    maxValue: 100
});
*/ Ext.Slider = Ext.extend(Ext.BoxComponent, {
/** * @cfg {Number} value The value to initialize the slider with. Defaults to minValue. */
/** * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false. */ vertical: false,
/** * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0. */ minValue: 0,
/** * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100. */ maxValue: 100,
/** * @cfg {Number/Boolean} decimalPrecision. *

The number of decimal places to which to round the Slider's value. Defaults to 0.

*

To disable rounding, configure as false.

*/ decimalPrecision: 0,
/** * @cfg {Number} keyIncrement How many units to change the Slider when adjusting with keyboard navigation. Defaults to 1. If the increment config is larger, it will be used instead. */ keyIncrement: 1,
/** * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'. */ increment: 0, // private clickRange: [5,15],
/** * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true */ clickToChange : true,
/** * @cfg {Boolean} animate Turn on or off animation. Defaults to true */ animate: true,
/** * True while the thumb is in a drag operation * @type boolean */ dragging: false, // private override initComponent : function(){ if(!Ext.isDefined(this.value)){ this.value = this.minValue; } Ext.Slider.superclass.initComponent.call(this); this.keyIncrement = Math.max(this.increment, this.keyIncrement); this.addEvents(
/** * @event beforechange * Fires before the slider value is changed. By returning false from an event handler, * you can cancel the event and prevent the slider from changing. * @param {Ext.Slider} slider The slider * @param {Number} newValue The new value which the slider is being changed to. * @param {Number} oldValue The old value which the slider was previously. */ 'beforechange',
/** * @event change * Fires when the slider value is changed. * @param {Ext.Slider} slider The slider * @param {Number} newValue The new value which the slider has been changed to. */ 'change',
/** * @event changecomplete * Fires when the slider value is changed by the user and any drag operations have completed. * @param {Ext.Slider} slider The slider * @param {Number} newValue The new value which the slider has been changed to. */ 'changecomplete',
/** * @event dragstart * Fires after a drag operation has started. * @param {Ext.Slider} slider The slider * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker */ 'dragstart',
/** * @event drag * Fires continuously during the drag operation while the mouse is moving. * @param {Ext.Slider} slider The slider * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker */ 'drag',
/** * @event dragend * Fires after the drag operation has completed. * @param {Ext.Slider} slider The slider * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker */ 'dragend' ); if(this.vertical){ Ext.apply(this, Ext.Slider.Vertical); } }, // private override onRender : function(){ this.autoEl = { cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'), cn:{cls:'x-slider-end',cn:{cls:'x-slider-inner',cn:[{cls:'x-slider-thumb'},{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]}} }; Ext.Slider.superclass.onRender.apply(this, arguments); this.endEl = this.el.first(); this.innerEl = this.endEl.first(); this.thumb = this.innerEl.first(); this.halfThumb = (this.vertical ? this.thumb.getHeight() : this.thumb.getWidth())/2; this.focusEl = this.thumb.next(); this.initEvents(); }, // private override initEvents : function(){ this.thumb.addClassOnOver('x-slider-thumb-over'); this.mon(this.el, { scope: this, mousedown: this.onMouseDown, keydown: this.onKeyDown }); this.focusEl.swallowEvent("click", true); this.tracker = new Ext.dd.DragTracker({ onBeforeStart: this.onBeforeDragStart.createDelegate(this), onStart: this.onDragStart.createDelegate(this), onDrag: this.onDrag.createDelegate(this), onEnd: this.onDragEnd.createDelegate(this), tolerance: 3, autoStart: 300 }); this.tracker.initEl(this.thumb); }, // private override onMouseDown : function(e){ if(this.disabled){ return; } if(this.clickToChange && e.target != this.thumb.dom){ var local = this.innerEl.translatePoints(e.getXY()); this.onClickChange(local); } this.focus(); }, // private onClickChange : function(local){ if(local.top > this.clickRange[0] && local.top < this.clickRange[1]){ this.setValue(Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true); } }, // private onKeyDown : function(e){ if(this.disabled){e.preventDefault();return;} var k = e.getKey(); switch(k){ case e.UP: case e.RIGHT: e.stopEvent(); if(e.ctrlKey){ this.setValue(this.maxValue, undefined, true); }else{ this.setValue(this.value+this.keyIncrement, undefined, true); } break; case e.DOWN: case e.LEFT: e.stopEvent(); if(e.ctrlKey){ this.setValue(this.minValue, undefined, true); }else{ this.setValue(this.value-this.keyIncrement, undefined, true); } break; default: e.preventDefault(); } }, // private doSnap : function(value){ if(!(this.increment && value)){ return value; } var newValue = value, inc = this.increment, m = value % inc; if(m != 0){ newValue -= m; if(m * 2 > inc){ newValue += inc; }else if(m * 2 < -inc){ newValue -= inc; } } return newValue.constrain(this.minValue, this.maxValue); }, // private afterRender : function(){ Ext.Slider.superclass.afterRender.apply(this, arguments); if(this.value !== undefined){ var v = this.normalizeValue(this.value); if(v !== this.value){ delete this.value; this.setValue(v, false); }else{ this.moveThumb(this.translateValue(v), false); } } }, // private getRatio : function(){ var w = this.innerEl.getWidth(), v = this.maxValue - this.minValue; return v == 0 ? w : (w/v); }, // private normalizeValue : function(v){ v = this.doSnap(v); v = Ext.util.Format.round(v, this.decimalPrecision); v = v.constrain(this.minValue, this.maxValue); return v; },
/** * Programmatically sets the value of the Slider. Ensures that the value is constrained within * the minValue and maxValue. * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue) * @param {Boolean} animate Turn on or off animation, defaults to true */ setValue : function(v, animate, changeComplete){ v = this.normalizeValue(v); if(v !== this.value && this.fireEvent('beforechange', this, v, this.value) !== false){ this.value = v; this.moveThumb(this.translateValue(v), animate !== false); this.fireEvent('change', this, v); if(changeComplete){ this.fireEvent('changecomplete', this, v); } } }, // private translateValue : function(v){ var ratio = this.getRatio(); return (v * ratio) - (this.minValue * ratio) - this.halfThumb; }, reverseValue : function(pos){ var ratio = this.getRatio(); return (pos + this.halfThumb + (this.minValue * ratio)) / ratio; }, // private moveThumb: function(v, animate){ if(!animate || this.animate === false){ this.thumb.setLeft(v); }else{ this.thumb.shift({left: v, stopFx: true, duration:.35}); } }, // private focus : function(){ this.focusEl.focus(10); }, // private onBeforeDragStart : function(e){ return !this.disabled; }, // private onDragStart: function(e){ this.thumb.addClass('x-slider-thumb-drag'); this.dragging = true; this.dragStartValue = this.value; this.fireEvent('dragstart', this, e); }, // private onDrag: function(e){ var pos = this.innerEl.translatePoints(this.tracker.getXY()); this.setValue(Ext.util.Format.round(this.reverseValue(pos.left), this.decimalPrecision), false); this.fireEvent('drag', this, e); }, // private onDragEnd: function(e){ this.thumb.removeClass('x-slider-thumb-drag'); this.dragging = false; this.fireEvent('dragend', this, e); if(this.dragStartValue != this.value){ this.fireEvent('changecomplete', this, this.value); } }, // private onResize : function(w, h){ this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r'))); this.syncThumb(); }, //private onDisable: function(){ Ext.Slider.superclass.onDisable.call(this); this.thumb.addClass(this.disabledClass); if(Ext.isIE){ //IE breaks when using overflow visible and opacity other than 1. //Create a place holder for the thumb and display it. var xy = this.thumb.getXY(); this.thumb.hide(); this.innerEl.addClass(this.disabledClass).dom.disabled = true; if (!this.thumbHolder){ this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass}); } this.thumbHolder.show().setXY(xy); } }, //private onEnable: function(){ Ext.Slider.superclass.onEnable.call(this); this.thumb.removeClass(this.disabledClass); if(Ext.isIE){ this.innerEl.removeClass(this.disabledClass).dom.disabled = false; if(this.thumbHolder){ this.thumbHolder.hide(); } this.thumb.show(); this.syncThumb(); } },
/** * Synchronizes the thumb position to the proper proportion of the total component width based * on the current slider {@link #value}. This will be called automatically when the Slider * is resized by a layout, but if it is rendered auto width, this method can be called from * another resize handler to sync the Slider if necessary. */ syncThumb : function(){ if(this.rendered){ this.moveThumb(this.translateValue(this.value)); } },
/** * Returns the current value of the slider * @return {Number} The current value of the slider */ getValue : function(){ return this.value; }, // private beforeDestroy : function(){ Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder'); Ext.Slider.superclass.beforeDestroy.call(this); } }); Ext.reg('slider', Ext.Slider); // private class to support vertical sliders Ext.Slider.Vertical = { onResize : function(w, h){ this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b'))); this.syncThumb(); }, getRatio : function(){ var h = this.innerEl.getHeight(), v = this.maxValue - this.minValue; return h/v; }, moveThumb: function(v, animate){ if(!animate || this.animate === false){ this.thumb.setBottom(v); }else{ this.thumb.shift({bottom: v, stopFx: true, duration:.35}); } }, onDrag: function(e){ var pos = this.innerEl.translatePoints(this.tracker.getXY()), bottom = this.innerEl.getHeight()-pos.top; this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), false); this.fireEvent('drag', this, e); }, onClickChange : function(local){ if(local.left > this.clickRange[0] && local.left < this.clickRange[1]){ var bottom = this.innerEl.getHeight() - local.top; this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), undefined, true); } } };