* jQuery UI Slider 1.12.1
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
//>>description: Displays a flexible slider with ranges and accessibility via keyboard.
//>>docs: http://api.jqueryui.com/slider/
//>>demos: http://jqueryui.com/slider/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/slider.css
//>>css.theme: ../../themes/base/theme.css
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
return $.widget( "ui.slider", $.ui.mouse, {
widgetEventPrefix: "slide",
"ui-slider": "ui-corner-all",
"ui-slider-handle": "ui-corner-all",
// Note: ui-widget-header isn't the most fittingly semantic framework class for this
// element, but worked best visually with a variety of themes
"ui-slider-range": "ui-corner-all ui-widget-header"
orientation: "horizontal",
// Number of pages in a slider
// (how many times can you page up/down to go through the whole range)
this._keySliding = false;
this._mouseSliding = false;
this._handleIndex = null;
this._detectOrientation();
this._addClass( "ui-slider ui-slider-" + this.orientation,
"ui-widget ui-widget-content" );
this._animateOff = false;
_createHandles: function() {
existingHandles = this.element.find( ".ui-slider-handle" ),
handle = "<span tabindex='0'></span>",
handleCount = ( options.values && options.values.length ) || 1;
if ( existingHandles.length > handleCount ) {
existingHandles.slice( handleCount ).remove();
existingHandles = existingHandles.slice( 0, handleCount );
for ( i = existingHandles.length; i < handleCount; i++ ) {
this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
this._addClass( this.handles, "ui-slider-handle", "ui-state-default" );
this.handle = this.handles.eq( 0 );
this.handles.each( function( i ) {
.data( "ui-slider-handle-index", i )
_createRange: function() {
var options = this.options;
if ( options.range === true ) {
options.values = [ this._valueMin(), this._valueMin() ];
} else if ( options.values.length && options.values.length !== 2 ) {
options.values = [ options.values[ 0 ], options.values[ 0 ] ];
} else if ( $.isArray( options.values ) ) {
options.values = options.values.slice( 0 );
if ( !this.range || !this.range.length ) {
this.range = $( "<div>" )
.appendTo( this.element );
this._addClass( this.range, "ui-slider-range" );
this._removeClass( this.range, "ui-slider-range-min ui-slider-range-max" );
// Handle range switching from true to min/max
if ( options.range === "min" || options.range === "max" ) {
this._addClass( this.range, "ui-slider-range-" + options.range );
_setupEvents: function() {
this._off( this.handles );
this._on( this.handles, this._handleEvents );
this._hoverable( this.handles );
this._focusable( this.handles );
_mouseCapture: function( event ) {
var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
width: this.element.outerWidth(),
height: this.element.outerHeight()
this.elementOffset = this.element.offset();
position = { x: event.pageX, y: event.pageY };
normValue = this._normValueFromMouse( position );
distance = this._valueMax() - this._valueMin() + 1;
this.handles.each( function( i ) {
var thisDistance = Math.abs( normValue - that.values( i ) );
if ( ( distance > thisDistance ) ||
( distance === thisDistance &&
( i === that._lastChangedValue || that.values( i ) === o.min ) ) ) {
closestHandle = $( this );
allowed = this._start( event, index );
if ( allowed === false ) {
this._mouseSliding = true;
this._handleIndex = index;
this._addClass( closestHandle, null, "ui-state-active" );
closestHandle.trigger( "focus" );
offset = closestHandle.offset();
mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
top: event.pageY - offset.top -
( closestHandle.height() / 2 ) -
( parseInt( closestHandle.css( "borderTopWidth" ), 10 ) || 0 ) -
( parseInt( closestHandle.css( "borderBottomWidth" ), 10 ) || 0 ) +
( parseInt( closestHandle.css( "marginTop" ), 10 ) || 0 )
if ( !this.handles.hasClass( "ui-state-hover" ) ) {
this._slide( event, index, normValue );
_mouseStart: function() {
_mouseDrag: function( event ) {
var position = { x: event.pageX, y: event.pageY },
normValue = this._normValueFromMouse( position );
this._slide( event, this._handleIndex, normValue );
_mouseStop: function( event ) {
this._removeClass( this.handles, null, "ui-state-active" );
this._mouseSliding = false;
this._stop( event, this._handleIndex );
this._change( event, this._handleIndex );
this._handleIndex = null;
this._clickOffset = null;
this._animateOff = false;
_detectOrientation: function() {
this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
_normValueFromMouse: function( position ) {
if ( this.orientation === "horizontal" ) {
pixelTotal = this.elementSize.width;
pixelMouse = position.x - this.elementOffset.left -
( this._clickOffset ? this._clickOffset.left : 0 );
pixelTotal = this.elementSize.height;
pixelMouse = position.y - this.elementOffset.top -
( this._clickOffset ? this._clickOffset.top : 0 );
percentMouse = ( pixelMouse / pixelTotal );
if ( percentMouse > 1 ) {
if ( percentMouse < 0 ) {
if ( this.orientation === "vertical" ) {
percentMouse = 1 - percentMouse;
valueTotal = this._valueMax() - this._valueMin();
valueMouse = this._valueMin() + percentMouse * valueTotal;
return this._trimAlignValue( valueMouse );
_uiHash: function( index, value, values ) {
handle: this.handles[ index ],
value: value !== undefined ? value : this.value()
if ( this._hasMultipleValues() ) {
uiHash.value = value !== undefined ? value : this.values( index );
uiHash.values = values || this.values();
_hasMultipleValues: function() {
return this.options.values && this.options.values.length;
_start: function( event, index ) {
return this._trigger( "start", event, this._uiHash( index ) );
_slide: function( event, index, newVal ) {
currentValue = this.value(),
newValues = this.values();
if ( this._hasMultipleValues() ) {
otherVal = this.values( index ? 0 : 1 );
currentValue = this.values( index );
if ( this.options.values.length === 2 && this.options.range === true ) {
newVal = index === 0 ? Math.min( otherVal, newVal ) : Math.max( otherVal, newVal );
newValues[ index ] = newVal;
if ( newVal === currentValue ) {
allowed = this._trigger( "slide", event, this._uiHash( index, newVal, newValues ) );
// A slide can be canceled by returning false from the slide callback
if ( allowed === false ) {
if ( this._hasMultipleValues() ) {
this.values( index, newVal );
_stop: function( event, index ) {
this._trigger( "stop", event, this._uiHash( index ) );
_change: function( event, index ) {
if ( !this._keySliding && !this._mouseSliding ) {
//store the last changed value index for reference when handles overlap
this._lastChangedValue = index;
this._trigger( "change", event, this._uiHash( index ) );
value: function( newValue ) {
if ( arguments.length ) {
this.options.value = this._trimAlignValue( newValue );
values: function( index, newValue ) {
if ( arguments.length > 1 ) {
this.options.values[ index ] = this._trimAlignValue( newValue );
this._change( null, index );
if ( arguments.length ) {
if ( $.isArray( arguments[ 0 ] ) ) {
vals = this.options.values;
newValues = arguments[ 0 ];
for ( i = 0; i < vals.length; i += 1 ) {
vals[ i ] = this._trimAlignValue( newValues[ i ] );
if ( this._hasMultipleValues() ) {
return this._values( index );
_setOption: function( key, value ) {
if ( key === "range" && this.options.range === true ) {
this.options.value = this._values( 0 );
this.options.values = null;
} else if ( value === "max" ) {
this.options.value = this._values( this.options.values.length - 1 );
this.options.values = null;
if ( $.isArray( this.options.values ) ) {
valsLength = this.options.values.length;
this._super( key, value );
this._detectOrientation();
this._removeClass( "ui-slider-horizontal ui-slider-vertical" )
._addClass( "ui-slider-" + this.orientation );
if ( this.options.range ) {
this._refreshRange( value );
// Reset positioning from previous orientation
this.handles.css( value === "horizontal" ? "bottom" : "left", "" );
this._animateOff = false;
// Start from the last handle to prevent unreachable handles (#9046)
for ( i = valsLength - 1; i >= 0; i-- ) {
this._animateOff = false;
this._animateOff = false;
this._animateOff = false;
_setOptionDisabled: function( value ) {
this._toggleClass( null, "ui-state-disabled", !!value );
// _value() returns value trimmed by min and max, aligned by step
var val = this.options.value;
val = this._trimAlignValue( val );
// _values() returns array of values trimmed by min and max, aligned by step
// _values( index ) returns single value trimmed by min and max, aligned by step
_values: function( index ) {