"use strict" ; interface CssClasses { target : string ; base : string ; origin : string ; handle : string ; handleLower : string ; handleUpper : string ; touchArea : string ; horizontal : string ; vertical : string ; background : string ; connect : string ; connects : string ; ltr : string ; rtl : string ; textDirectionLtr : string ; textDirectionRtl : string ; draggable : string ; drag : string ; tap : string ; active : string ; tooltip : string ; pips : string ; pipsHorizontal : string ; pipsVertical : string ; marker : string ; markerHorizontal : string ; markerVertical : string ; markerNormal : string ; markerLarge : string ; markerSub : string ; value : string ; valueHorizontal : string ; valueVertical : string ; valueNormal : string ; valueLarge : string ; valueSub : string ; } export interface PartialFormatter { to : ( value : number ) => string | number ; from ? : ( value : string ) => number | false ; } export interface Formatter extends PartialFormatter { from : ( value : string ) => number | false ; } export enum PipsMode { Range = "range" , Steps = "steps" , Positions = "positions" , Count = "count" , Values = "values" } export enum PipsType { None = - 1 , NoValue = 0 , LargeValue = 1 , SmallValue = 2 } type WrappedSubRange = [ number ] | [ number , number ] ; type SubRange = number | WrappedSubRange ; interface Range { min : SubRange ; max : SubRange ; [ key ] : SubRange ; } interface BasePips { mode : PipsMode ; density ? : number ; filter ? : PipsFilter ; format ? : PartialFormatter ; } interface PositionsPips extends BasePips { mode : PipsMode . Positions ; values : number [ ] ; stepped ? : boolean ; } interface ValuesPips extends BasePips { mode : PipsMode . Values ; values : number [ ] ; stepped ? : boolean ; } interface CountPips extends BasePips { mode : PipsMode . Count ; values : number ; stepped ? : boolean ; } interface StepsPips extends BasePips { mode : PipsMode . Steps ; } interface RangePips extends BasePips { mode : PipsMode . Range ; } type Pips = PositionsPips | ValuesPips | CountPips | StepsPips | RangePips ; type StartValues = string | number | ( string | number ) [ ] ; type HandleAttributes = { [ key ] : string ; } ; interface UpdatableOptions { range ? : Range ; start ? : StartValues ; margin ? : number ; limit ? : number ; padding ? : number | number [ ] ; snap ? : boolean ; step ? : number ; pips ? : Pips ; format ? : Formatter ; tooltips ? : boolean | PartialFormatter | ( boolean | PartialFormatter ) [ ] ; animate ? : boolean ; } export interface Options extends UpdatableOptions { range : Range ; connect ? : "lower" | "upper" | boolean | boolean [ ] ; orientation ? : "vertical" | "horizontal" ; direction ? : "ltr" | "rtl" ; behaviour ? : string ; keyboardSupport ? : boolean ; keyboardPageMultiplier ? : number ; keyboardMultiplier ? : number ; keyboardDefaultStep ? : number ; documentElement ? : HTMLElement ; cssPrefix ? : string ; cssClasses ? : CssClasses ; ariaFormat ? : PartialFormatter ; animationDuration ? : number ; handleAttributes ? : HandleAttributes [ ] ; } interface Behaviour { tap : boolean ; drag : boolean ; dragAll : boolean ; fixed : boolean ; snap : boolean ; hover : boolean ; unconstrained : boolean ; } interface ParsedOptions { animate : boolean ; connect : boolean [ ] ; start : number [ ] ; margin : number [ ] | null ; limit : number [ ] | null ; padding : number [ ] [ ] | null ; step ? : number ; orientation ? : "vertical" | "horizontal" ; direction ? : "ltr" | "rtl" ; tooltips ? : ( boolean | PartialFormatter ) [ ] ; keyboardSupport : boolean ; keyboardPageMultiplier : number ; keyboardMultiplier : number ; keyboardDefaultStep : number ; documentElement ? : HTMLElement ; cssPrefix ? : string | false ; cssClasses : CssClasses ; ariaFormat : PartialFormatter ; pips ? : Pips ; animationDuration : number ; snap ? : boolean ; format : Formatter ; handleAttributes ? : HandleAttributes [ ] ; range : Range ; singleStep : number ; transformRule : "transform" | "msTransform" | "webkitTransform" ; style : "left" | "top" | "right" | "bottom" ; ort : 0 | 1 ; handles : number ; events : Behaviour ; dir : 0 | 1 ; spectrum : Spectrum ; } export interface API { destroy : ( ) => void ; steps : ( ) => NextStepsForHandle [ ] ; on : ( eventName : string , callback : EventCallback ) => void ; off : ( eventName : string ) => void ; get : ( unencoded ? : boolean ) => GetResult ; set : ( input : number | string | ( number | string ) [ ] , fireSetEvent ? : boolean , exactInput ? : boolean ) => void ; setHandle : ( handleNumber : number , value : number | string , fireSetEvent ? : boolean , exactInput ? : boolean ) => void ; reset : ( fireSetEvent ? : boolean ) => void ; options : Options ; updateOptions : ( optionsToUpdate : UpdatableOptions , fireSetEvent : boolean ) => void ; target : HTMLElement ; removePips : ( ) => void ; removeTooltips : ( ) => void ; getTooltips : ( ) => { [ handleNumber ] : HTMLElement | false ; } ; getOrigins : ( ) => { [ handleNumber ] : HTMLElement ; } ; pips : ( grid : Pips ) => HTMLElement ; } interface TargetElement extends HTMLElement { noUiSlider ? : API ; } interface CSSStyleDeclarationIE10 extends CSSStyleDeclaration { msTransform ? : string ; } interface EventData { target ? : HTMLElement ; handles ? : HTMLElement [ ] ; handle ? : HTMLElement ; connect ? : HTMLElement ; listeners ? : [ string , EventHandler ] [ ] ; startCalcPoint ? : number ; baseSize ? : number ; pageOffset ? : PageOffset ; handleNumbers : number [ ] ; buttonsProperty ? : number ; locations ? : number [ ] ; doNotReject ? : boolean ; hover ? : boolean ; } interface MoveEventData extends EventData { listeners : [ string , EventHandler ] [ ] ; startCalcPoint : number ; baseSize : number ; locations : number [ ] ; } interface EndEventData extends EventData { listeners : [ string , EventHandler ] [ ] ; } interface NearByStep { startValue : number ; step : number | false ; highestStep : number ; } interface NearBySteps { stepBefore : NearByStep ; thisStep : NearByStep ; stepAfter : NearByStep ; } type EventHandler = ( event : BrowserEvent ) => false | undefined ; type GetResult = number | string | ( string | number ) [ ] ; type NextStepsForHandle = [ number | false | null , number | false | null ] ; type OptionKey = keyof Options & keyof ParsedOptions & keyof UpdatableOptions ; type PipsFilter = ( value : number , type : PipsType ) => PipsType ; type PageOffset = { x : number ; y : number ; } ; type BrowserEvent = MouseEvent & TouchEvent & { pageOffset : PageOffset ; points : [ number , number ] ; cursor : boolean ; calcPoint : number ; } ; type EventCallback = ( this : API , values : ( number | string ) [ ] , handleNumber : number , unencoded : number [ ] , tap : boolean , locations : number [ ] , slider : API ) => void ; function isValidFormatter ( entry ) { return isValidPartialFormatter ( entry ) && typeof ( < Formatter > entry ) . from === "function" ; } function isValidPartialFormatter ( entry ) { return typeof entry === "object" && typeof ( < Formatter > entry ) . to === "function" ; } function removeElement ( el ) { ( el . parentElement as HTMLElement ) . removeChild ( el ) ; } function isSet < T > ( value ) { return value !== null && value !== undefined ; } function preventDefault ( e ) { e . preventDefault ( ) ; } function unique < Type > ( array ) { return array . filter ( function ( a ) { return ! this [ a ] ? ( this [ a ] = true ) : false ; } , { } ) ; } function closest ( value , to ) { return Math . round ( value / to ) * to ; } function offset ( elem , orientation ) { const rect = elem . getBoundingClientRect ( ) ; const doc = elem . ownerDocument ; const docElem = doc . documentElement ; const pageOffset = getPageOffset ( doc ) ; if ( /webkit.*Chrome.*Mobile/i . test ( navigator . userAgent ) ) { pageOffset . x = 0 ; } return orientation ? rect . top + pageOffset . y - docElem . clientTop : rect . left + pageOffset . x - docElem . clientLeft ; } function isNumeric ( a ) { return typeof a === "number" && ! isNaN ( a ) && isFinite ( a ) ; } function addClassFor ( element , className , duration ) { if ( duration > 0 ) { addClass ( element , className ) ; setTimeout ( function ( ) { removeClass ( element , className ) ; } , duration ) ; } } function limit ( a ) { return Math . max ( Math . min ( a , 100 ) , 0 ) ; } function asArray < Type > ( a ) { return Array . isArray ( a ) ? a : [ a ] ; } function countDecimals ( numStr ) { numStr = String ( numStr ) ; const pieces = numStr . split ( "." ) ; return pieces . length > 1 ? pieces [ 1 ] . length : 0 ; } function addClass ( el , className ) { if ( el . classList && ! /\s/ . test ( className ) ) { el . classList . add ( className ) ; } else { el . className += " " + className ; } } function removeClass ( el , className ) { if ( el . classList && ! /\s/ . test ( className ) ) { el . classList . remove ( className ) ; } else { el . className = el . className . replace ( new RegExp ( "(^|\\b)" + className . split ( " " ) . join ( "|" ) + "(\\b|$)" , "gi" ) , " " ) ; } } function hasClass ( el , className ) { return el . classList ? el . classList . contains ( className ) : new RegExp ( "\\b" + className + "\\b" ) . test ( el . className ) ; } function getPageOffset ( doc ) { const supportPageOffset = window . pageXOffset !== undefined ; const isCSS1Compat = ( doc . compatMode || "" ) === "CSS1Compat" ; const x = supportPageOffset ? window . pageXOffset : isCSS1Compat ? doc . documentElement . scrollLeft : doc . body . scrollLeft ; const y = supportPageOffset ? window . pageYOffset : isCSS1Compat ? doc . documentElement . scrollTop : doc . body . scrollTop ; return { x : x , y : y } ; } function getActions ( ) { return ( window . navigator as any ) . pointerEnabled ? { start : "pointerdown" , move : "pointermove" , end : "pointerup" } : ( window . navigator as any ) . msPointerEnabled ? { start : "MSPointerDown" , move : "MSPointerMove" , end : "MSPointerUp" } : { start : "mousedown touchstart" , move : "mousemove touchmove" , end : "mouseup touchend" } ; } function getSupportsPassive ( ) { let supportsPassive = false ; try { const opts = Object . defineProperty ( { } , "passive" , { get : function ( ) { supportsPassive = true ; } } ) ; window . addEventListener ( "test" , null , opts ) ; } catch ( e ) { } return supportsPassive ; } function getSupportsTouchActionNone ( ) { return window . CSS && CSS . supports && CSS . supports ( "touch-action" , "none" ) ; } function subRangeRatio ( pa , pb ) { return 100 / ( pb - pa ) ; } function fromPercentage ( range , value , startRange ) { return ( value * 100 ) / ( range [ startRange + 1 ] - range [ startRange ] ) ; } function toPercentage ( range , value ) { return fromPercentage ( range , range [ 0 ] < 0 ? value + Math . abs ( range [ 0 ] ) : value - range [ 0 ] , 0 ) ; } function isPercentage ( range , value ) { return ( value * ( range [ 1 ] - range [ 0 ] ) ) / 100 + range [ 0 ] ; } function getJ ( value , arr ) { let j = 1 ; while ( value >= arr [ j ] ) { j += 1 ; } return j ; } function toStepping ( xVal , xPct , value ) { if ( value >= xVal . slice ( - 1 ) [ 0 ] ) { return 100 ; } const j = getJ ( value , xVal ) ; const va = xVal [ j - 1 ] ; const vb = xVal [ j ] ; const pa = xPct [ j - 1 ] ; const pb = xPct [ j ] ; return pa + toPercentage ( [ va , vb ] , value ) / subRangeRatio ( pa , pb ) ; } function fromStepping ( xVal , xPct , value ) { if ( value >= 100 ) { return xVal . slice ( - 1 ) [ 0 ] ; } const j = getJ ( value , xPct ) ; const va = xVal [ j - 1 ] ; const vb = xVal [ j ] ; const pa = xPct [ j - 1 ] ; const pb = xPct [ j ] ; return isPercentage ( [ va , vb ] , ( value - pa ) * subRangeRatio ( pa , pb ) ) ; } function getStep ( xPct , xSteps , snap , value ) { if ( value === 100 ) { return value ; } const j = getJ ( value , xPct ) ; const a = xPct [ j - 1 ] ; const b = xPct [ j ] ; if ( snap ) { if ( value - a > ( b - a ) / 2 ) { return b ; } return a ; } if ( ! xSteps [ j - 1 ] ) { return value ; } return xPct [ j - 1 ] + closest ( value - xPct [ j - 1 ] , xSteps [ j - 1 ] as number ) ; } class Spectrum { public xPct : number [ ] = [ ] ; public xVal : number [ ] = [ ] ; public xSteps : ( number | false ) [ ] = [ ] ; public xNumSteps : ( number | false ) [ ] = [ ] ; protected xHighestCompleteStep : number [ ] = [ ] ; protected snap : boolean ; constructor ( entry , snap , singleStep ) { this . xSteps = [ singleStep || false ] ; this . xNumSteps = [ false ] ; this . snap = snap ; let index ; const ordered = [ ] ; Object . keys ( entry ) . forEach ( index => { ordered . push ( [ asArray ( entry [ index ] ) as WrappedSubRange , index ] ) ; } ) ; ordered . sort ( function ( a , b ) { return a [ 0 ] [ 0 ] - b [ 0 ] [ 0 ] ; } ) ; for ( index = 0 ; index < ordered . length ; index ++ ) { this . handleEntryPoint ( ordered [ index ] [ 1 ] , ordered [ index ] [ 0 ] ) ; } this . xNumSteps = this . xSteps . slice ( 0 ) ; for ( index = 0 ; index < this . xNumSteps . length ; index ++ ) { this . handleStepPoint ( index , this . xNumSteps [ index ] ) ; } } public getDistance ( value ) { const distances = [ ] ; for ( let index = 0 ; index < this . xNumSteps . length - 1 ; index ++ ) { distances [ index ] = fromPercentage ( this . xVal , value , index ) ; } return distances ; } public getAbsoluteDistance ( value , distances , direction ) { let xPct_index = 0 ; if ( value < this . xPct [ this . xPct . length - 1 ] ) { while ( value > this . xPct [ xPct_index + 1 ] ) { xPct_index ++ ; } } else if ( value === this . xPct [ this . xPct . length - 1 ] ) { xPct_index = this . xPct . length - 2 ; } if ( ! direction && value === this . xPct [ xPct_index + 1 ] ) { xPct_index ++ ; } if ( distances === null ) { distances = [ ] ; } let start_factor ; let rest_factor = 1 ; let rest_rel_distance = distances [ xPct_index ] ; let range_pct = 0 ; let rel_range_distance = 0 ; let abs_distance_counter = 0 ; let range_counter = 0 ; if ( direction ) { start_factor = ( value - this . xPct [ xPct_index ] ) / ( this . xPct [ xPct_index + 1 ] - this . xPct [ xPct_index ] ) ; } else { start_factor = ( this . xPct [ xPct_index + 1 ] - value ) / ( this . xPct [ xPct_index + 1 ] - this . xPct [ xPct_index ] ) ; } while ( rest_rel_distance > 0 ) { range_pct = this . xPct [ xPct_index + 1 + range_counter ] - this . xPct [ xPct_index + range_counter ] ; if ( distances [ xPct_index + range_counter ] * rest_factor + 100 - start_factor * 100 > 100 ) { rel_range_distance = range_pct * start_factor ; rest_factor = ( rest_rel_distance - 100 * start_factor ) / distances [ xPct_index + range_counter ] ; start_factor = 1 ; } else { rel_range_distance = ( ( distances [ xPct_index + range_counter ] * range_pct ) / 100 ) * rest_factor ; rest_factor = 0 ; } if ( direction ) { abs_distance_counter = abs_distance_counter - rel_range_distance ; if ( this . xPct . length + range_counter >= 1 ) { range_counter -- ; } } else { abs_distance_counter = abs_distance_counter + rel_range_distance ; if ( this . xPct . length - range_counter >= 1 ) { range_counter ++ ; } } rest_rel_distance = distances [ xPct_index + range_counter ] * rest_factor ; } return value + abs_distance_counter ; } public toStepping ( value ) { value = toStepping ( this . xVal , this . xPct , value ) ; return value ; } public fromStepping ( value ) { return fromStepping ( this . xVal , this . xPct , value ) ; } public getStep ( value ) { value = getStep ( this . xPct , this . xSteps , this . snap , value ) ; return value ; } public getDefaultStep ( value , isDown , size ) { let j = getJ ( value , this . xPct ) ; if ( value === 100 || ( isDown && value === this . xPct [ j - 1 ] ) ) { j = Math . max ( j - 1 , 1 ) ; } return ( this . xVal [ j ] - this . xVal [ j - 1 ] ) / size ; } public getNearbySteps ( value ) { const j = getJ ( value , this . xPct ) ; return { stepBefore : { startValue : this . xVal [ j - 2 ] , step : this . xNumSteps [ j - 2 ] , highestStep : this . xHighestCompleteStep [ j - 2 ] } , thisStep : { startValue : this . xVal [ j - 1 ] , step : this . xNumSteps [ j - 1 ] , highestStep : this . xHighestCompleteStep [ j - 1 ] } , stepAfter : { startValue : this . xVal [ j ] , step : this . xNumSteps [ j ] , highestStep : this . xHighestCompleteStep [ j ] } } ; } public countStepDecimals ( ) { const stepDecimals = this . xNumSteps . map ( countDecimals ) ; return Math . max . apply ( null , stepDecimals ) ; } public hasNoSize ( ) { return this . xVal [ 0 ] === this . xVal [ this . xVal . length - 1 ] ; } public convert ( value ) { return this . getStep ( this . toStepping ( value ) ) ; } private handleEntryPoint ( index , value ) { let percentage ; if ( index === "min" ) { percentage = 0 ; } else if ( index === "max" ) { percentage = 100 ; } else { percentage = parseFloat ( index ) ; } if ( ! isNumeric ( percentage ) || ! isNumeric ( value [ 0 ] ) ) { throw new Error ( "noUiSlider: 'range' value isn't numeric." ) ; } this . xPct . push ( percentage ) ; this . xVal . push ( value [ 0 ] ) ; const value1 = Number ( value [ 1 ] ) ; if ( ! percentage ) { if ( ! isNaN ( value1 ) ) { this . xSteps [ 0 ] = value1 ; } } else { this . xSteps . push ( isNaN ( value1 ) ? false : value1 ) ; } this . xHighestCompleteStep . push ( 0 ) ; } private handleStepPoint ( i , n ) { if ( ! n ) { return ; } if ( this . xVal [ i ] === this . xVal [ i + 1 ] ) { this . xSteps [ i ] = this . xHighestCompleteStep [ i ] = this . xVal [ i ] ; return ; } this . xSteps [ i ] = fromPercentage ( [ this . xVal [ i ] , this . xVal [ i + 1 ] ] , n , 0 ) / subRangeRatio ( this . xPct [ i ] , this . xPct [ i + 1 ] ) ; const totalSteps = ( this . xVal [ i + 1 ] - this . xVal [ i ] ) / ( this . xNumSteps [ i ] as number ) ; const highestStep = Math . ceil ( Number ( totalSteps . toFixed ( 3 ) ) - 1 ) ; const step = this . xVal [ i ] + ( this . xNumSteps [ i ] as number ) * highestStep ; this . xHighestCompleteStep [ i ] = step ; } } const defaultFormatter = { to : function ( value ) { return value === undefined ? "" : value . toFixed ( 2 ) ; } , from : Number } ; const cssClasses = { target : "target" , base : "base" , origin : "origin" , handle : "handle" , handleLower : "handle-lower" , handleUpper : "handle-upper" , touchArea : "touch-area" , horizontal : "horizontal" , vertical : "vertical" , background : "background" , connect : "connect" , connects : "connects" , ltr : "ltr" , rtl : "rtl" , textDirectionLtr : "txt-dir-ltr" , textDirectionRtl : "txt-dir-rtl" , draggable : "draggable" , drag : "state-drag" , tap : "state-tap" , active : "active" , tooltip : "tooltip" , pips : "pips" , pipsHorizontal : "pips-horizontal" , pipsVertical : "pips-vertical" , marker : "marker" , markerHorizontal : "marker-horizontal" , markerVertical : "marker-vertical" , markerNormal : "marker-normal" , markerLarge : "marker-large" , markerSub : "marker-sub" , value : "value" , valueHorizontal : "value-horizontal" , valueVertical : "value-vertical" , valueNormal : "value-normal" , valueLarge : "value-large" , valueSub : "value-sub" } ; const INTERNAL_EVENT_NS = { tooltips : ".__tooltips" , aria : ".__aria" } ; function testStep ( parsed , entry ) { if ( ! isNumeric ( entry ) ) { throw new Error ( "noUiSlider: 'step' is not numeric." ) ; } parsed . singleStep = entry ; } function testKeyboardPageMultiplier ( parsed , entry ) { if ( ! isNumeric ( entry ) ) { throw new Error ( "noUiSlider: 'keyboardPageMultiplier' is not numeric." ) ; } parsed . keyboardPageMultiplier = entry ; } function testKeyboardMultiplier ( parsed , entry ) { if ( ! isNumeric ( entry ) ) { throw new Error ( "noUiSlider: 'keyboardMultiplier' is not numeric." ) ; } parsed . keyboardMultiplier = entry ; } function testKeyboardDefaultStep ( parsed , entry ) { if ( ! isNumeric ( entry ) ) { throw new Error ( "noUiSlider: 'keyboardDefaultStep' is not numeric." ) ; } parsed . keyboardDefaultStep = entry ; } function testRange ( parsed , entry ) { if ( typeof entry !== "object" || Array . isArray ( entry ) ) { throw new Error ( "noUiSlider: 'range' is not an object." ) ; } if ( entry . min === undefined || entry . max === undefined ) { throw new Error ( "noUiSlider: Missing 'min' or 'max' in 'range'." ) ; } parsed . spectrum = new Spectrum ( entry , parsed . snap || false , parsed . singleStep ) ; } function testStart ( parsed , entry ) { entry = asArray ( entry ) ; if ( ! Array . isArray ( entry ) || ! entry . length ) { throw new Error ( "noUiSlider: 'start' option is incorrect." ) ; } parsed . handles = entry . length ; parsed . start = entry ; } function testSnap ( parsed , entry ) { if ( typeof entry !== "boolean" ) { throw new Error ( "noUiSlider: 'snap' option must be a boolean." ) ; } parsed . snap = entry ; } function testAnimate ( parsed , entry ) { if ( typeof entry !== "boolean" ) { throw new Error ( "noUiSlider: 'animate' option must be a boolean." ) ; } parsed . animate = entry ; } function testAnimationDuration ( parsed , entry ) { if ( typeof entry !== "number" ) { throw new Error ( "noUiSlider: 'animationDuration' option must be a number." ) ; } parsed . animationDuration = entry ; } function testConnect ( parsed , entry ) { let connect = [ false ] ; let i ; if ( entry === "lower" ) { entry = [ true , false ] ; } else if ( entry === "upper" ) { entry = [ false , true ] ; } if ( entry === true || entry === false ) { for ( i = 1 ; i < parsed . handles ; i ++ ) { connect . push ( entry ) ; } connect . push ( false ) ; } else if ( ! Array . isArray ( entry ) || ! entry . length || entry . length !== parsed . handles + 1 ) { throw new Error ( "noUiSlider: 'connect' option doesn't match handle count." ) ; } else { connect = entry ; } parsed . connect = connect ; } function testOrientation ( parsed , entry ) { switch ( entry ) { case "horizontal" : parsed . ort = 0 ; break ; case "vertical" : parsed . ort = 1 ; break ; default : throw new Error ( "noUiSlider: 'orientation' option is invalid." ) ; } } function testMargin ( parsed , entry ) { if ( ! isNumeric ( entry ) ) { throw new Error ( "noUiSlider: 'margin' option must be numeric." ) ; } if ( entry === 0 ) { return ; } parsed . margin = parsed . spectrum . getDistance ( entry ) ; } function testLimit ( parsed , entry ) { if ( ! isNumeric ( entry ) ) { throw new Error ( "noUiSlider: 'limit' option must be numeric." ) ; } parsed . limit = parsed . spectrum . getDistance ( entry ) ; if ( ! parsed . limit || parsed . handles < 2 ) { throw new Error ( "noUiSlider: 'limit' option is only supported on linear sliders with 2 or more handles." ) ; } } function testPadding ( parsed , entry ) { let index ; if ( ! isNumeric ( entry ) && ! Array . isArray ( entry ) ) { throw new Error ( "noUiSlider: 'padding' option must be numeric or array of exactly 2 numbers." ) ; } if ( Array . isArray ( entry ) && ! ( entry . length === 2 || isNumeric ( entry [ 0 ] ) || isNumeric ( entry [ 1 ] ) ) ) { throw new Error ( "noUiSlider: 'padding' option must be numeric or array of exactly 2 numbers." ) ; } if ( entry === 0 ) { return ; } if ( ! Array . isArray ( entry ) ) { entry = [ entry , entry ] ; } parsed . padding = [ parsed . spectrum . getDistance ( entry [ 0 ] ) , parsed . spectrum . getDistance ( entry [ 1 ] ) ] ; for ( index = 0 ; index < parsed . spectrum . xNumSteps . length - 1 ; index ++ ) { if ( parsed . padding [ 0 ] [ index ] < 0 || parsed . padding [ 1 ] [ index ] < 0 ) { throw new Error ( "noUiSlider: 'padding' option must be a positive number(s)." ) ; } } const totalPadding = entry [ 0 ] + entry [ 1 ] ; const firstValue = parsed . spectrum . xVal [ 0 ] ; const lastValue = parsed . spectrum . xVal [ parsed . spectrum . xVal . length - 1 ] ; if ( totalPadding / ( lastValue - firstValue ) > 1 ) { throw new Error ( "noUiSlider: 'padding' option must not exceed 100% of the range." ) ; } } function testDirection ( parsed , entry ) { switch ( entry ) { case "ltr" : parsed . dir = 0 ; break ; case "rtl" : parsed . dir = 1 ; break ; default : throw new Error ( "noUiSlider: 'direction' option was not recognized." ) ; } } function testBehaviour ( parsed , entry ) { if ( typeof entry !== "string" ) { throw new Error ( "noUiSlider: 'behaviour' must be a string containing options." ) ; } const tap = entry . indexOf ( "tap" ) >= 0 ; const drag = entry . indexOf ( "drag" ) >= 0 ; const fixed = entry . indexOf ( "fixed" ) >= 0 ; const snap = entry . indexOf ( "snap" ) >= 0 ; const hover = entry . indexOf ( "hover" ) >= 0 ; const unconstrained = entry . indexOf ( "unconstrained" ) >= 0 ; const dragAll = entry . indexOf ( "drag-all" ) >= 0 ; if ( fixed ) { if ( parsed . handles !== 2 ) { throw new Error ( "noUiSlider: 'fixed' behaviour must be used with 2 handles" ) ; } testMargin ( parsed , parsed . start [ 1 ] - parsed . start [ 0 ] ) ; } if ( unconstrained && ( parsed . margin || parsed . limit ) ) { throw new Error ( "noUiSlider: 'unconstrained' behaviour cannot be used with margin or limit" ) ; } parsed . events = { tap : tap || snap , drag : drag , dragAll : dragAll , fixed : fixed , snap : snap , hover : hover , unconstrained : unconstrained } ; } function testTooltips ( parsed , entry ) { if ( entry === false ) { return ; } if ( entry === true || isValidPartialFormatter ( entry ) ) { parsed . tooltips = [ ] ; for ( let i = 0 ; i < parsed . handles ; i ++ ) { parsed . tooltips . push ( entry ) ; } } else { entry = asArray ( entry ) ; if ( entry . length !== parsed . handles ) { throw new Error ( "noUiSlider: must pass a formatter for all handles." ) ; } entry . forEach ( function ( formatter ) { if ( typeof formatter !== "boolean" && ! isValidPartialFormatter ( formatter ) ) { throw new Error ( "noUiSlider: 'tooltips' must be passed a formatter or 'false'." ) ; } } ) ; parsed . tooltips = entry ; } } function testHandleAttributes ( parsed , entry ) { if ( entry . length !== parsed . handles ) { throw new Error ( "noUiSlider: must pass a attributes for all handles." ) ; } parsed . handleAttributes = entry ; } function testAriaFormat ( parsed , entry ) { if ( ! isValidPartialFormatter ( entry ) ) { throw new Error ( "noUiSlider: 'ariaFormat' requires 'to' method." ) ; } parsed . ariaFormat = entry ; } function testFormat ( parsed , entry ) { if ( ! isValidFormatter ( entry ) ) { throw new Error ( "noUiSlider: 'format' requires 'to' and 'from' methods." ) ; } parsed . format = entry ; } function testKeyboardSupport ( parsed , entry ) { if ( typeof entry !== "boolean" ) { throw new Error ( "noUiSlider: 'keyboardSupport' option must be a boolean." ) ; } parsed . keyboardSupport = entry ; } function testDocumentElement ( parsed , entry ) { parsed . documentElement = entry ; } function testCssPrefix ( parsed , entry ) { if ( typeof entry !== "string" && entry !== false ) { throw new Error ( "noUiSlider: 'cssPrefix' must be a string or `false`." ) ; } parsed . cssPrefix = entry ; } function testCssClasses ( parsed , entry ) { if ( typeof entry !== "object" ) { throw new Error ( "noUiSlider: 'cssClasses' must be an object." ) ; } if ( typeof parsed . cssPrefix === "string" ) { parsed . cssClasses = { } as CssClasses ; Object . keys ( entry ) . forEach ( ( key ) => { parsed . cssClasses [ key ] = parsed . cssPrefix + entry [ key ] ; } ) ; } else { parsed . cssClasses = entry ; } } function testOptions ( options ) { const parsed = { margin : null , limit : null , padding : null , animate : true , animationDuration : 300 , ariaFormat : defaultFormatter , format : defaultFormatter } as ParsedOptions ; const tests = { step : { r : false , t : testStep } , keyboardPageMultiplier : { r : false , t : testKeyboardPageMultiplier } , keyboardMultiplier : { r : false , t : testKeyboardMultiplier } , keyboardDefaultStep : { r : false , t : testKeyboardDefaultStep } , start : { r : true , t : testStart } , connect : { r : true , t : testConnect } , direction : { r : true , t : testDirection } , snap : { r : false , t : testSnap } , animate : { r : false , t : testAnimate } , animationDuration : { r : false , t : testAnimationDuration } , range : { r : true , t : testRange } , orientation : { r : false , t : testOrientation } , margin : { r : false , t : testMargin } , limit : { r : false , t : testLimit } , padding : { r : false , t : testPadding } , behaviour : { r : true , t : testBehaviour } , ariaFormat : { r : false , t : testAriaFormat } , format : { r : false , t : testFormat } , tooltips : { r : false , t : testTooltips } , keyboardSupport : { r : true , t : testKeyboardSupport } , documentElement : { r : false , t : testDocumentElement } , cssPrefix : { r : true , t : testCssPrefix } , cssClasses : { r : true , t : testCssClasses } , handleAttributes : { r : false , t : testHandleAttributes } } ; const defaults = { connect : false , direction : "ltr" , behaviour : "tap" , orientation : "horizontal" , keyboardSupport : true , cssPrefix : "noUi-" , cssClasses : cssClasses , keyboardPageMultiplier : 5 , keyboardMultiplier : 1 , keyboardDefaultStep : 10 } as UpdatableOptions ; if ( options . format && ! options . ariaFormat ) { options . ariaFormat = options . format ; } Object . keys ( tests ) . forEach ( function ( name ) { if ( ! isSet ( options [ name ] ) && defaults [ name ] === undefined ) { if ( ( tests [ name ] as any ) . r ) { throw new Error ( "noUiSlider: '" + name + "' is required." ) ; } return ; } ( tests [ name ] as any ) . t ( parsed , ! isSet ( options [ name ] ) ? defaults [ name ] : options [ name ] ) ; } ) ; parsed . pips = options . pips ; const d = document . createElement ( "div" ) ; const msPrefix = ( d . style as CSSStyleDeclarationIE10 ) . msTransform !== undefined ; const noPrefix = d . style . transform !== undefined ; parsed . transformRule = noPrefix ? "transform" : msPrefix ? "msTransform" : "webkitTransform" ; const styles = [ [ "left" , "top" ] , [ "right" , "bottom" ] ] ; parsed . style = styles [ parsed . dir ] [ parsed . ort ] as "left" | "top" | "right" | "bottom" ; return parsed ; } function scope ( target , options , originalOptions ) { const actions = getActions ( ) ; const supportsTouchActionNone = getSupportsTouchActionNone ( ) ; const supportsPassive = supportsTouchActionNone && getSupportsPassive ( ) ; const scope_Target = target ; let scope_Base ; let scope_Handles ; let scope_Connects ; let scope_Pips ; let scope_Tooltips ; let scope_Spectrum = options . spectrum ; const scope_Values = [ ] ; let scope_Locations = [ ] ; const scope_HandleNumbers = [ ] ; let scope_ActiveHandlesCount = 0 ; const scope_Events = { } ; const scope_Document = target . ownerDocument ; const scope_DocumentElement = options . documentElement || scope_Document . documentElement ; const scope_Body = scope_Document . body ; const scope_DirOffset = scope_Document . dir === "rtl" || options . ort === 1 ? 0 : 100 ; function addNodeTo ( addTarget , className ) { const div = scope_Document . createElement ( "div" ) ; if ( className ) { addClass ( div , className ) ; } addTarget . appendChild ( div ) ; return div ; } function addOrigin ( base , handleNumber ) { const origin = addNodeTo ( base , options . cssClasses . origin ) ; const handle = addNodeTo ( origin , options . cssClasses . handle ) ; addNodeTo ( handle , options . cssClasses . touchArea ) ; handle . setAttribute ( "data-handle" , String ( handleNumber ) ) ; if ( options . keyboardSupport ) { handle . setAttribute ( "tabindex" , "0" ) ; handle . addEventListener ( "keydown" , function ( event ) { return eventKeydown ( event , handleNumber ) ; } ) ; } if ( options . handleAttributes !== undefined ) { const attributes = options . handleAttributes [ handleNumber ] ; Object . keys ( attributes ) . forEach ( function ( attribute ) { handle . setAttribute ( attribute , attributes [ attribute ] ) ; } ) ; } handle . setAttribute ( "role" , "slider" ) ; handle . setAttribute ( "aria-orientation" , options . ort ? "vertical" : "horizontal" ) ; if ( handleNumber === 0 ) { addClass ( handle , options . cssClasses . handleLower ) ; } else if ( handleNumber === options . handles - 1 ) { addClass ( handle , options . cssClasses . handleUpper ) ; } return origin ; } function addConnect ( base , add ) { if ( ! add ) { return false ; } return addNodeTo ( base , options . cssClasses . connect ) ; } function addElements ( connectOptions , base ) { const connectBase = addNodeTo ( base , options . cssClasses . connects ) ; scope_Handles = [ ] ; scope_Connects = [ ] ; scope_Connects . push ( addConnect ( connectBase , connectOptions [ 0 ] ) ) ; for ( let i = 0 ; i < options . handles ; i ++ ) { scope_Handles . push ( addOrigin ( base , i ) ) ; scope_HandleNumbers [ i ] = i ; scope_Connects . push ( addConnect ( connectBase , connectOptions [ i + 1 ] ) ) ; } } function addSlider ( addTarget ) { addClass ( addTarget , options . cssClasses . target ) ; if ( options . dir === 0 ) { addClass ( addTarget , options . cssClasses . ltr ) ; } else { addClass ( addTarget , options . cssClasses . rtl ) ; } if ( options . ort === 0 ) { addClass ( addTarget , options . cssClasses . horizontal ) ; } else { addClass ( addTarget , options . cssClasses . vertical ) ; } const textDirection = getComputedStyle ( addTarget ) . direction ; if ( textDirection === "rtl" ) { addClass ( addTarget , options . cssClasses . textDirectionRtl ) ; } else { addClass ( addTarget , options . cssClasses . textDirectionLtr ) ; } return addNodeTo ( addTarget , options . cssClasses . base ) ; } function addTooltip ( handle , handleNumber ) { if ( ! options . tooltips || ! options . tooltips [ handleNumber ] ) { return false ; } return addNodeTo ( handle . firstChild as HTMLElement , options . cssClasses . tooltip ) ; } function isSliderDisabled ( ) { return scope_Target . hasAttribute ( "disabled" ) ; } function isHandleDisabled ( handleNumber ) { const handleOrigin = scope_Handles [ handleNumber ] ; return handleOrigin . hasAttribute ( "disabled" ) ; } function removeTooltips ( ) { if ( scope_Tooltips ) { removeEvent ( "update" + INTERNAL_EVENT_NS . tooltips ) ; scope_Tooltips . forEach ( function ( tooltip ) { if ( tooltip ) { removeElement ( tooltip ) ; } } ) ; scope_Tooltips = null ; } } function tooltips ( ) { removeTooltips ( ) ; scope_Tooltips = scope_Handles . map ( addTooltip ) ; bindEvent ( "update" + INTERNAL_EVENT_NS . tooltips , function ( values , handleNumber , unencoded ) { if ( ! scope_Tooltips || ! options . tooltips ) { return ; } if ( scope_Tooltips [ handleNumber ] === false ) { return ; } let formattedValue = values [ handleNumber ] ; if ( options . tooltips [ handleNumber ] !== true ) { formattedValue = ( < Formatter > options . tooltips [ handleNumber ] ) . to ( unencoded [ handleNumber ] ) ; } ( < HTMLElement > scope_Tooltips [ handleNumber ] ) . innerHTML = < string > formattedValue ; } ) ; } function aria ( ) { removeEvent ( "update" + INTERNAL_EVENT_NS . aria ) ; bindEvent ( "update" + INTERNAL_EVENT_NS . aria , function ( values , handleNumber , unencoded , tap , positions ) { scope_HandleNumbers . forEach ( function ( index ) { const handle = scope_Handles [ index ] ; let min = < number > checkHandlePosition ( scope_Locations , index , 0 , true , true , true ) ; let max = < number > checkHandlePosition ( scope_Locations , index , 100 , true , true , true ) ; let now = positions [ index ] ; const text = String ( options . ariaFormat . to ( unencoded [ index ] ) ) ; min = < string > scope_Spectrum . fromStepping ( min ) . toFixed ( 1 ) ; max = < string > scope_Spectrum . fromStepping ( max ) . toFixed ( 1 ) ; now = < string > scope_Spectrum . fromStepping ( now ) . toFixed ( 1 ) ; handle . children [ 0 ] . setAttribute ( "aria-valuemin" , min ) ; handle . children [ 0 ] . setAttribute ( "aria-valuemax" , max ) ; handle . children [ 0 ] . setAttribute ( "aria-valuenow" , now ) ; handle . children [ 0 ] . setAttribute ( "aria-valuetext" , text ) ; } ) ; } ) ; } function getGroup ( pips ) { if ( pips . mode === PipsMode . Range || pips . mode === PipsMode . Steps ) { return scope_Spectrum . xVal ; } if ( pips . mode === PipsMode . Count ) { if ( pips . values < 2 ) { throw new Error ( "noUiSlider: 'values' (>= 2) required for mode 'count'." ) ; } let interval = pips . values - 1 ; const spread = 100 / interval ; const values = [ ] ; while ( interval -- ) { values [ interval ] = interval * spread ; } values . push ( 100 ) ; return mapToRange ( values , pips . stepped ) ; } if ( pips . mode === PipsMode . Positions ) { return mapToRange ( pips . values , pips . stepped ) ; } if ( pips . mode === PipsMode . Values ) { if ( pips . stepped ) { return pips . values . map ( function ( value ) { return scope_Spectrum . fromStepping ( scope_Spectrum . getStep ( scope_Spectrum . toStepping ( value ) ) ) ; } ) ; } return pips . values ; } return [ ] ; } function mapToRange ( values , stepped ) { return values . map ( function ( value ) { return scope_Spectrum . fromStepping ( stepped ? scope_Spectrum . getStep ( value ) : value ) ; } ) ; } function generateSpread ( pips ) { function safeIncrement ( value , increment ) { return Number ( ( value + increment ) . toFixed ( 7 ) ) ; } let group = getGroup ( pips ) ; const indexes = { } ; const firstInRange = scope_Spectrum . xVal [ 0 ] ; const lastInRange = scope_Spectrum . xVal [ scope_Spectrum . xVal . length - 1 ] ; let ignoreFirst = false ; let ignoreLast = false ; let prevPct = 0 ; group = unique ( group . slice ( ) . sort ( function ( a , b ) { return a - b ; } ) ) ; if ( group [ 0 ] !== firstInRange ) { group . unshift ( firstInRange ) ; ignoreFirst = true ; } if ( group [ group . length - 1 ] !== lastInRange ) { group . push ( lastInRange ) ; ignoreLast = true ; } group . forEach ( function ( current , index ) { let step ; let i ; let q ; const low = current ; let high = group [ index + 1 ] ; let newPct ; let pctDifference ; let pctPos ; let type ; let steps ; let realSteps ; let stepSize ; const isSteps = pips . mode === PipsMode . Steps ; if ( isSteps ) { step = scope_Spectrum . xNumSteps [ index ] ; } if ( ! step ) { step = high - low ; } if ( high === undefined ) { high = low ; } step = Math . max ( step , 0.0000001 ) ; for ( i = low ; i <= high ; i = safeIncrement ( i , step ) ) { newPct = scope_Spectrum . toStepping ( i ) ; pctDifference = newPct - prevPct ; steps = pctDifference / ( pips . density || 1 ) ; realSteps = Math . round ( steps ) ; stepSize = pctDifference / realSteps ; for ( q = 1 ; q <= realSteps ; q += 1 ) { pctPos = prevPct + q * stepSize ; indexes [ pctPos . toFixed ( 5 ) ] = [ scope_Spectrum . fromStepping ( pctPos ) , 0 ] ; } type = group . indexOf ( i ) > - 1 ? PipsType . LargeValue : isSteps ? PipsType . SmallValue : PipsType . NoValue ; if ( ! index && ignoreFirst && i !== high ) { type = 0 ; } if ( ! ( i === high && ignoreLast ) ) { indexes [ newPct . toFixed ( 5 ) ] = [ i , type ] ; } prevPct = newPct ; } } ) ; return indexes ; } function addMarking ( spread , filterFunc , formatter ) { const element = scope_Document . createElement ( "div" ) ; const valueSizeClasses = { [ PipsType . None ] : "" , [ PipsType . NoValue ] : options . cssClasses . valueNormal , [ PipsType . LargeValue ] : options . cssClasses . valueLarge , [ PipsType . SmallValue ] : options . cssClasses . valueSub } ; const markerSizeClasses = { [ PipsType . None ] : "" , [ PipsType . NoValue ] : options . cssClasses . markerNormal , [ PipsType . LargeValue ] : options . cssClasses . markerLarge , [ PipsType . SmallValue ] : options . cssClasses . markerSub } ; const valueOrientationClasses = [ options . cssClasses . valueHorizontal , options . cssClasses . valueVertical ] ; const markerOrientationClasses = [ options . cssClasses . markerHorizontal , options . cssClasses . markerVertical ] ; addClass ( element , options . cssClasses . pips ) ; addClass ( element , options . ort === 0 ? options . cssClasses . pipsHorizontal : options . cssClasses . pipsVertical ) ; function getClasses ( type , source ) { const a = source === options . cssClasses . value ; const orientationClasses = a ? valueOrientationClasses : markerOrientationClasses ; const sizeClasses = a ? valueSizeClasses : markerSizeClasses ; return source + " " + orientationClasses [ options . ort ] + " " + sizeClasses [ type ] ; } function addSpread ( offset , value , type ) { type = filterFunc ? filterFunc ( value , type ) : type ; if ( type === PipsType . None ) { return ; } let node = addNodeTo ( element , false ) ; node . className = getClasses ( type , options . cssClasses . marker ) ; node . style [ options . style ] = offset + "%" ; if ( type > PipsType . NoValue ) { node = addNodeTo ( element , false ) ; node . className = getClasses ( type , options . cssClasses . value ) ; node . setAttribute ( "data-value" , String ( value ) ) ; node . style [ options . style ] = offset + "%" ; node . innerHTML = String ( formatter . to ( value ) ) ; } } Object . keys ( spread ) . forEach ( function ( offset ) { addSpread ( offset , spread [ offset ] [ 0 ] , spread [ offset ] [ 1 ] ) ; } ) ; return element ; } function removePips ( ) { if ( scope_Pips ) { removeElement ( scope_Pips ) ; scope_Pips = null ; } } function pips ( pips ) { removePips ( ) ; const spread = generateSpread ( pips ) ; const filter = pips . filter ; const format = pips . format || { to : function ( value ) { return String ( Math . round ( value ) ) ; } } ; scope_Pips = scope_Target . appendChild ( addMarking ( spread , filter , format ) ) ; return scope_Pips ; } function baseSize ( ) { const rect = scope_Base . getBoundingClientRect ( ) ; const alt = ( "offset" + [ "Width" , "Height" ] [ options . ort ] ) as "offsetWidth" | "offsetHeight" ; return options . ort === 0 ? rect . width || scope_Base [ alt ] : rect . height || scope_Base [ alt ] ; } function attachEvent ( events , element , callback , data ) { const method = function ( event ) : false | undefined { const e = fixEvent ( event , data . pageOffset , data . target || element ) ; if ( ! e ) { return false ; } if ( isSliderDisabled ( ) && ! data . doNotReject ) { return false ; } if ( hasClass ( scope_Target , options . cssClasses . tap ) && ! data . doNotReject ) { return false ; } if ( events === actions . start && e . buttons !== undefined && e . buttons > 1 ) { return false ; } if ( data . hover && e . buttons ) { return false ; } if ( ! supportsPassive ) { e . preventDefault ( ) ; } e . calcPoint = e . points [ options . ort ] ; callback ( e , data ) ; return ; } ; const methods = [ ] ; events . split ( " " ) . forEach ( function ( eventName ) : void { element . addEventListener ( eventName , method , supportsPassive ? { passive : true } : false ) ; methods . push ( [ eventName , method ] ) ; } ) ; return methods ; } function fixEvent ( e , pageOffset , eventTarget ) { const touch = e . type . indexOf ( "touch" ) === 0 ; const mouse = e . type . indexOf ( "mouse" ) === 0 ; let pointer = e . type . indexOf ( "pointer" ) === 0 ; let x = 0 ; let y = 0 ; if ( e . type . indexOf ( "MSPointer" ) === 0 ) { pointer = true ; } if ( e . type === "mousedown" && ! e . buttons && ! e . touches ) { return false ; } if ( touch ) { const isTouchOnTarget = function ( checkTouch ) { const target = checkTouch . target as HTMLElement ; return ( target === eventTarget || eventTarget . contains ( target ) || ( e . composed && e . composedPath ( ) . shift ( ) === eventTarget ) ) ; } ; if ( e . type === "touchstart" ) { const targetTouches = Array . prototype . filter . call ( e . touches , isTouchOnTarget ) ; if ( targetTouches . length > 1 ) { return false ; } x = targetTouches [ 0 ] . pageX ; y = targetTouches [ 0 ] . pageY ; } else { const targetTouch = ( Array . prototype as any ) . find . call ( e . changedTouches , isTouchOnTarget ) ; if ( ! targetTouch ) { return false ; } x = targetTouch . pageX ; y = targetTouch . pageY ; } } pageOffset = pageOffset || getPageOffset ( scope_Document ) ; if ( mouse || pointer ) { x = e . clientX + pageOffset . x ; y = e . clientY + pageOffset . y ; } e . pageOffset = pageOffset ; e . points = [ x , y ] ; e . cursor = mouse || pointer ; return e ; } function calcPointToPercentage ( calcPoint ) { const location = calcPoint - offset ( scope_Base , options . ort ) ; let proposal = ( location * 100 ) / baseSize ( ) ; proposal = limit ( proposal ) ; return options . dir ? 100 - proposal : proposal ; } function getClosestHandle ( clickedPosition ) { let smallestDifference = 100 ; let handleNumber = false ; scope_Handles . forEach ( function ( handle , index ) { if ( isHandleDisabled ( index ) ) { return ; } const handlePosition = scope_Locations [ index ] ; const differenceWithThisHandle = Math . abs ( handlePosition - clickedPosition ) ; const clickAtEdge = differenceWithThisHandle === 100 && smallestDifference === 100 ; const isCloser = differenceWithThisHandle < smallestDifference ; const isCloserAfter = differenceWithThisHandle <= smallestDifference && clickedPosition > handlePosition ; if ( isCloser || isCloserAfter || clickAtEdge ) { handleNumber = index ; smallestDifference = differenceWithThisHandle ; } } ) ; return handleNumber ; } function documentLeave ( event , data ) { if ( event . type === "mouseout" && ( event . target as HTMLElement ) . nodeName === "HTML" && event . relatedTarget === null ) { eventEnd ( event , data ) ; } } function eventMove ( event , data ) { if ( navigator . appVersion . indexOf ( "MSIE 9" ) === - 1 && event . buttons === 0 && data . buttonsProperty !== 0 ) { return eventEnd ( event , data ) ; } const movement = ( options . dir ? - 1 : 1 ) * ( event . calcPoint - data . startCalcPoint ) ; const proposal = ( movement * 100 ) / data . baseSize ; moveHandles ( movement > 0 , proposal , data . locations , data . handleNumbers , data . connect ) ; } function eventEnd ( event , data ) { if ( data . handle ) { removeClass ( data . handle , options . cssClasses . active ) ; scope_ActiveHandlesCount -= 1 ; } data . listeners . forEach ( function ( c ) { scope_DocumentElement . removeEventListener ( c [ 0 ] , c [ 1 ] ) ; } ) ; if ( scope_ActiveHandlesCount === 0 ) { removeClass ( scope_Target , options . cssClasses . drag ) ; setZindex ( ) ; if ( event . cursor ) { scope_Body . style . cursor = "" ; scope_Body . removeEventListener ( "selectstart" , preventDefault ) ; } } data . handleNumbers . forEach ( function ( handleNumber ) { fireEvent ( "change" , handleNumber ) ; fireEvent ( "set" , handleNumber ) ; fireEvent ( "end" , handleNumber ) ; } ) ; } function eventStart ( event , data ) { if ( data . handleNumbers . some ( isHandleDisabled ) ) { return ; } let handle ; if ( data . handleNumbers . length === 1 ) { const handleOrigin = scope_Handles [ data . handleNumbers [ 0 ] ] ; handle = handleOrigin . children [ 0 ] as HTMLElement ; scope_ActiveHandlesCount += 1 ; addClass ( handle , options . cssClasses . active ) ; } event . stopPropagation ( ) ; const listeners = [ ] ; const moveEvent = attachEvent ( actions . move , scope_DocumentElement , eventMove , { target : event . target as HTMLElement , handle : handle , connect : data . connect , listeners : listeners , startCalcPoint : event . calcPoint , baseSize : baseSize ( ) , pageOffset : event . pageOffset , handleNumbers : data . handleNumbers , buttonsProperty : event . buttons , locations : scope_Locations . slice ( ) } ) ; const endEvent = attachEvent ( actions . end , scope_DocumentElement , eventEnd , { target : event . target as HTMLElement , handle : handle , listeners : listeners , doNotReject : true , handleNumbers : data . handleNumbers } ) ; const outEvent = attachEvent ( "mouseout" , scope_DocumentElement , documentLeave , { target : event . target as HTMLElement , handle : handle , listeners : listeners , doNotReject : true , handleNumbers : data . handleNumbers } ) ; listeners . push . apply ( listeners , moveEvent . concat ( endEvent , outEvent ) ) ; if ( event . cursor ) { scope_Body . style . cursor = getComputedStyle ( event . target as Element ) . cursor ; if ( scope_Handles . length > 1 ) { addClass ( scope_Target , options . cssClasses . drag ) ; } scope_Body . addEventListener ( "selectstart" , preventDefault , false ) ; } data . handleNumbers . forEach ( function ( handleNumber ) { fireEvent ( "start" , handleNumber ) ; } ) ; } function eventTap ( event ) { event . stopPropagation ( ) ; const proposal = calcPointToPercentage ( event . calcPoint ) ; const handleNumber = getClosestHandle ( proposal ) ; if ( handleNumber === false ) { return ; } if ( ! options . events . snap ) { addClassFor ( scope_Target , options . cssClasses . tap , options . animationDuration ) ; } setHandle ( handleNumber , proposal , true , true ) ; setZindex ( ) ; fireEvent ( "slide" , handleNumber , true ) ; fireEvent ( "update" , handleNumber , true ) ; if ( ! options . events . snap ) { fireEvent ( "change" , handleNumber , true ) ; fireEvent ( "set" , handleNumber , true ) ; } else { eventStart ( event , { handleNumbers : [ handleNumber ] } ) ; } } function eventHover ( event ) { const proposal = calcPointToPercentage ( event . calcPoint ) ; const to = scope_Spectrum . getStep ( proposal ) ; const value = scope_Spectrum . fromStepping ( to ) ; Object . keys ( scope_Events ) . forEach ( function ( targetEvent ) { if ( "hover" === targetEvent . split ( "." ) [ 0 ] ) { scope_Events [ targetEvent ] . forEach ( function ( callback ) { callback . call ( scope_Self , value ) ; } ) ; } } ) ; } function eventKeydown ( event , handleNumber ) { if ( isSliderDisabled ( ) || isHandleDisabled ( handleNumber ) ) { return false ; } const horizontalKeys = [ "Left" , "Right" ] ; const verticalKeys = [ "Down" , "Up" ] ; const largeStepKeys = [ "PageDown" , "PageUp" ] ; const edgeKeys = [ "Home" , "End" ] ; if ( options . dir && ! options . ort ) { horizontalKeys . reverse ( ) ; } else if ( options . ort && ! options . dir ) { verticalKeys . reverse ( ) ; largeStepKeys . reverse ( ) ; } const key = event . key . replace ( "Arrow" , "" ) ; const isLargeDown = key === largeStepKeys [ 0 ] ; const isLargeUp = key === largeStepKeys [ 1 ] ; const isDown = key === verticalKeys [ 0 ] || key === horizontalKeys [ 0 ] || isLargeDown ; const isUp = key === verticalKeys [ 1 ] || key === horizontalKeys [ 1 ] || isLargeUp ; const isMin = key === edgeKeys [ 0 ] ; const isMax = key === edgeKeys [ 1 ] ; if ( ! isDown && ! isUp && ! isMin && ! isMax ) { return true ; } event . preventDefault ( ) ; let to ; if ( isUp || isDown ) { const direction = isDown ? 0 : 1 ; const steps = getNextStepsForHandle ( handleNumber ) ; let step = steps [ direction ] ; if ( step === null ) { return false ; } if ( step === false ) { step = scope_Spectrum . getDefaultStep ( scope_Locations [ handleNumber ] , isDown , options . keyboardDefaultStep ) as number ; } if ( isLargeUp || isLargeDown ) { step *= options . keyboardPageMultiplier ; } else { step *= options . keyboardMultiplier ; } step = Math . max ( step , 0.0000001 ) ; step = ( isDown ? - 1 : 1 ) * step ; to = scope_Values [ handleNumber ] + step ; } else if ( isMax ) { to = options . spectrum . xVal [ options . spectrum . xVal . length - 1 ] ; } else { to = options . spectrum . xVal [ 0 ] ; } setHandle ( handleNumber , scope_Spectrum . toStepping ( to ) , true , true ) ; fireEvent ( "slide" , handleNumber ) ; fireEvent ( "update" , handleNumber ) ; fireEvent ( "change" , handleNumber ) ; fireEvent ( "set" , handleNumber ) ; return false ; } function bindSliderEvents ( behaviour ) { if ( ! behaviour . fixed ) { scope_Handles . forEach ( function ( handle , index ) { attachEvent ( actions . start , handle . children [ 0 ] as HTMLElement , eventStart , { handleNumbers : [ index ] } ) ; } ) ; } if ( behaviour . tap ) { attachEvent ( actions . start , scope_Base , eventTap , { } as EventData ) ; } if ( behaviour . hover ) { attachEvent ( actions . move , scope_Base , eventHover , { hover : true } as EventData ) ; } if ( behaviour . drag ) { scope_Connects . forEach ( function ( connect , index ) { if ( connect === false || index === 0 || index === scope_Connects . length - 1 ) { return ; } const handleBefore = scope_Handles [ index - 1 ] ; const handleAfter = scope_Handles [ index ] ; const eventHolders = [ connect ] ; let handlesToDrag = [ handleBefore , handleAfter ] ; let handleNumbersToDrag = [ index - 1 , index ] ; addClass ( connect , options . cssClasses . draggable ) ; if ( behaviour . fixed ) { eventHolders . push ( handleBefore . children [ 0 ] as HTMLElement ) ; eventHolders . push ( handleAfter . children [ 0 ] as HTMLElement ) ; } if ( behaviour . dragAll ) { handlesToDrag = scope_Handles ; handleNumbersToDrag = scope_HandleNumbers ; } eventHolders . forEach ( function ( eventHolder ) { attachEvent ( actions . start , eventHolder , eventStart , { handles : handlesToDrag , handleNumbers : handleNumbersToDrag , connect : connect } ) ; } ) ; } ) ; } } function bindEvent ( namespacedEvent , callback ) { scope_Events [ namespacedEvent ] = scope_Events [ namespacedEvent ] || [ ] ; scope_Events [ namespacedEvent ] . push ( callback ) ; if ( namespacedEvent . split ( "." ) [ 0 ] === "update" ) { scope_Handles . forEach ( function ( a , index ) { fireEvent ( "update" , index ) ; } ) ; } } function isInternalNamespace ( namespace ) { return namespace === INTERNAL_EVENT_NS . aria || namespace === INTERNAL_EVENT_NS . tooltips ; } function removeEvent ( namespacedEvent ) { const event = namespacedEvent && namespacedEvent . split ( "." ) [ 0 ] ; const namespace = event ? namespacedEvent . substring ( event . length ) : namespacedEvent ; Object . keys ( scope_Events ) . forEach ( function ( bind ) { const tEvent = bind . split ( "." ) [ 0 ] ; const tNamespace = bind . substring ( tEvent . length ) ; if ( ( ! event || event === tEvent ) && ( ! namespace || namespace === tNamespace ) ) { if ( ! isInternalNamespace ( tNamespace ) || namespace === tNamespace ) { delete scope_Events [ bind ] ; } } } ) ; } function fireEvent ( eventName , handleNumber , tap ? ) { Object . keys ( scope_Events ) . forEach ( function ( targetEvent ) : void { const eventType = targetEvent . split ( "." ) [ 0 ] ; if ( eventName === eventType ) { scope_Events [ targetEvent ] . forEach ( function ( callback ) { callback . call ( scope_Self , scope_Values . map ( options . format . to ) , handleNumber , scope_Values . slice ( ) , tap || false , scope_Locations . slice ( ) , scope_Self ) ; } ) ; } } ) ; } function checkHandlePosition ( reference , handleNumber , to , lookBackward , lookForward , getValue ) { let distance ; if ( scope_Handles . length > 1 && ! options . events . unconstrained ) { if ( lookBackward && handleNumber > 0 ) { distance = scope_Spectrum . getAbsoluteDistance ( reference [ handleNumber - 1 ] , options . margin , false ) ; to = Math . max ( to , distance ) ; } if ( lookForward && handleNumber < scope_Handles . length - 1 ) { distance = scope_Spectrum . getAbsoluteDistance ( reference [ handleNumber + 1 ] , options . margin , true ) ; to = Math . min ( to , distance ) ; } } if ( scope_Handles . length > 1 && options . limit ) { if ( lookBackward && handleNumber > 0 ) { distance = scope_Spectrum . getAbsoluteDistance ( reference [ handleNumber - 1 ] , options . limit , false ) ; to = Math . min ( to , distance ) ; } if ( lookForward && handleNumber < scope_Handles . length - 1 ) { distance = scope_Spectrum . getAbsoluteDistance ( reference [ handleNumber + 1 ] , options . limit , true ) ; to = Math . max ( to , distance ) ; } } if ( options . padding ) { if ( handleNumber === 0 ) { distance = scope_Spectrum . getAbsoluteDistance ( 0 , options . padding [ 0 ] , false ) ; to = Math . max ( to , distance ) ; } if ( handleNumber === scope_Handles . length - 1 ) { distance = scope_Spectrum . getAbsoluteDistance ( 100 , options . padding [ 1 ] , true ) ; to = Math . min ( to , distance ) ; } } to = scope_Spectrum . getStep ( to ) ; to = limit ( to ) ; if ( to === reference [ handleNumber ] && ! getValue ) { return false ; } return to ; } function inRuleOrder ( v , a ) { const o = options . ort ; return ( o ? a : v ) + ", " + ( o ? v : a ) ; } function moveHandles ( upward , proposal , locations , handleNumbers , connect ? ) { const proposals = locations . slice ( ) ; const firstHandle = handleNumbers [ 0 ] ; let b = [ ! upward , upward ] ; let f = [ upward , ! upward ] ; handleNumbers = handleNumbers . slice ( ) ; if ( upward ) { handleNumbers . reverse ( ) ; } if ( handleNumbers . length > 1 ) { handleNumbers . forEach ( function ( handleNumber , o ) { const to = checkHandlePosition ( proposals , handleNumber , proposals [ handleNumber ] + proposal , b [ o ] , f [ o ] , false ) ; if ( to === false ) { proposal = 0 ; } else { proposal = to - proposals [ handleNumber ] ; proposals [ handleNumber ] = to ; } } ) ; } else { b = f = [ true ] ; } let state = false ; handleNumbers . forEach ( function ( handleNumber , o ) { state = setHandle ( handleNumber , locations [ handleNumber ] + proposal , b [ o ] , f [ o ] ) || state ; } ) ; if ( state ) { handleNumbers . forEach ( function ( handleNumber ) { fireEvent ( "update" , handleNumber ) ; fireEvent ( "slide" , handleNumber ) ; } ) ; if ( connect != undefined ) { fireEvent ( "drag" , firstHandle ) ; } } } function transformDirection ( a , b ) { return options . dir ? 100 - a - b : a ; } function updateHandlePosition ( handleNumber , to ) { scope_Locations [ handleNumber ] = to ; scope_Values [ handleNumber ] = scope_Spectrum . fromStepping ( to ) ; const translation = transformDirection ( to , 0 ) - scope_DirOffset ; const translateRule = "translate(" + inRuleOrder ( translation + "%" , "0" ) + ")" ; ( scope_Handles [ handleNumber ] . style as CSSStyleDeclarationIE10 ) [ options . transformRule ] = translateRule ; updateConnect ( handleNumber ) ; updateConnect ( handleNumber + 1 ) ; } function setZindex ( ) { scope_HandleNumbers . forEach ( function ( handleNumber ) : void { const dir = scope_Locations [ handleNumber ] > 50 ? - 1 : 1 ; const zIndex = 3 + ( scope_Handles . length + dir * handleNumber ) ; scope_Handles [ handleNumber ] . style . zIndex = String ( zIndex ) ; } ) ; } function setHandle ( handleNumber , to , lookBackward , lookForward , exactInput ? ) { if ( ! exactInput ) { to = checkHandlePosition ( scope_Locations , handleNumber , < number > to , lookBackward , lookForward , false ) ; } if ( to === false ) { return false ; } updateHandlePosition ( handleNumber , to ) ; return true ; } function updateConnect ( index ) { if ( ! scope_Connects [ index ] ) { return ; } let l = 0 ; let h = 100 ; if ( index !== 0 ) { l = scope_Locations [ index - 1 ] ; } if ( index !== scope_Connects . length - 1 ) { h = scope_Locations [ index ] ; } const connectWidth = h - l ; const translateRule = "translate(" + inRuleOrder ( transformDirection ( l , connectWidth ) + "%" , "0" ) + ")" ; const scaleRule = "scale(" + inRuleOrder ( connectWidth / 100 , "1" ) + ")" ; ( ( < HTMLElement > scope_Connects [ index ] ) . style as CSSStyleDeclarationIE10 ) [ options . transformRule ] = translateRule + " " + scaleRule ; } function resolveToValue ( to , handleNumber ) { if ( to === null || to === false || to === undefined ) { return scope_Locations [ handleNumber ] ; } if ( typeof to === "number" ) { to = String ( to ) ; } to = options . format . from ( to ) ; if ( to !== false ) { to = scope_Spectrum . toStepping ( to ) ; } if ( to === false || isNaN ( to ) ) { return scope_Locations [ handleNumber ] ; } return to ; } function valueSet ( input , fireSetEvent ? , exactInput ? ) { const values = asArray ( input ) ; const isInit = scope_Locations [ 0 ] === undefined ; fireSetEvent = fireSetEvent === undefined ? true : fireSetEvent ; if ( options . animate && ! isInit ) { addClassFor ( scope_Target , options . cssClasses . tap , options . animationDuration ) ; } scope_HandleNumbers . forEach ( function ( handleNumber ) { setHandle ( handleNumber , resolveToValue ( values [ handleNumber ] , handleNumber ) , true , false , exactInput ) ; } ) ; let i = scope_HandleNumbers . length === 1 ? 0 : 1 ; if ( isInit && scope_Spectrum . hasNoSize ( ) ) { exactInput = true ; scope_Locations [ 0 ] = 0 ; if ( scope_HandleNumbers . length > 1 ) { const space = 100 / ( scope_HandleNumbers . length - 1 ) ; scope_HandleNumbers . forEach ( function ( handleNumber ) { scope_Locations [ handleNumber ] = handleNumber * space ; } ) ; } } for ( ; i < scope_HandleNumbers . length ; ++ i ) { scope_HandleNumbers . forEach ( function ( handleNumber ) { setHandle ( handleNumber , scope_Locations [ handleNumber ] , true , true , exactInput ) ; } ) ; } setZindex ( ) ; scope_HandleNumbers . forEach ( function ( handleNumber ) { fireEvent ( "update" , handleNumber ) ; if ( values [ handleNumber ] !== null && fireSetEvent ) { fireEvent ( "set" , handleNumber ) ; } } ) ; } function valueReset ( fireSetEvent ? ) { valueSet ( options . start , fireSetEvent ) ; } function valueSetHandle ( handleNumber , value , fireSetEvent ? , exactInput ? ) { handleNumber = Number ( handleNumber ) ; if ( ! ( handleNumber >= 0 && handleNumber < scope_HandleNumbers . length ) ) { throw new Error ( "noUiSlider: invalid handle number, got: " + handleNumber ) ; } setHandle ( handleNumber , resolveToValue ( value , handleNumber ) , true , true , exactInput ) ; fireEvent ( "update" , handleNumber ) ; if ( fireSetEvent ) { fireEvent ( "set" , handleNumber ) ; } } function valueGet ( unencoded = false ) { if ( unencoded ) { return scope_Values . length === 1 ? scope_Values [ 0 ] : scope_Values . slice ( 0 ) ; } const values = scope_Values . map ( options . format . to ) ; if ( values . length === 1 ) { return values [ 0 ] ; } return values ; } function destroy ( ) { removeEvent ( INTERNAL_EVENT_NS . aria ) ; removeEvent ( INTERNAL_EVENT_NS . tooltips ) ; Object . keys ( options . cssClasses ) . forEach ( ( key ) => { removeClass ( scope_Target , options . cssClasses [ key ] ) ; } ) ; while ( scope_Target . firstChild ) { scope_Target . removeChild ( scope_Target . firstChild ) ; } delete scope_Target . noUiSlider ; } function getNextStepsForHandle ( handleNumber ) { const location = scope_Locations [ handleNumber ] ; const nearbySteps = scope_Spectrum . getNearbySteps ( location ) ; const value = scope_Values [ handleNumber ] ; let increment = nearbySteps . thisStep . step ; let decrement = null ; if ( options . snap ) { return [ value - nearbySteps . stepBefore . startValue || null , nearbySteps . stepAfter . startValue - value || null ] ; } if ( increment !== false ) { if ( value + increment > nearbySteps . stepAfter . startValue ) { increment = nearbySteps . stepAfter . startValue - value ; } } if ( value > nearbySteps . thisStep . startValue ) { decrement = nearbySteps . thisStep . step ; } else if ( nearbySteps . stepBefore . step === false ) { decrement = false ; } else { decrement = value - nearbySteps . stepBefore . highestStep ; } if ( location === 100 ) { increment = null ; } else if ( location === 0 ) { decrement = null ; } const stepDecimals = scope_Spectrum . countStepDecimals ( ) ; if ( increment !== null && increment !== false ) { increment = Number ( increment . toFixed ( stepDecimals ) ) ; } if ( decrement !== null && decrement !== false ) { decrement = Number ( decrement . toFixed ( stepDecimals ) ) ; } return [ decrement , increment ] ; } function getNextSteps ( ) { return scope_HandleNumbers . map ( getNextStepsForHandle ) ; } function updateOptions ( optionsToUpdate , fireSetEvent ) { const v = valueGet ( ) ; const updateAble = [ "margin" , "limit" , "padding" , "range" , "animate" , "snap" , "step" , "format" , "pips" , "tooltips" ] ; updateAble . forEach ( function ( name ) { if ( optionsToUpdate [ name ] !== undefined ) { ( originalOptions [ name ] as any ) = optionsToUpdate [ name ] ; } } ) ; const newOptions = testOptions ( originalOptions ) ; updateAble . forEach ( function ( name ) { if ( optionsToUpdate [ name ] !== undefined ) { ( options [ name ] as any ) = newOptions [ name ] ; } } ) ; scope_Spectrum = newOptions . spectrum ; options . margin = newOptions . margin ; options . limit = newOptions . limit ; options . padding = newOptions . padding ; if ( options . pips ) { pips ( options . pips ) ; } else { removePips ( ) ; } if ( options . tooltips ) { tooltips ( ) ; } else { removeTooltips ( ) ; } scope_Locations = [ ] ; valueSet ( isSet ( optionsToUpdate . start ) ? optionsToUpdate . start : v , fireSetEvent ) ; } function setupSlider ( ) { scope_Base = addSlider ( scope_Target ) ; addElements ( options . connect , scope_Base ) ; bindSliderEvents ( options . events ) ; valueSet ( options . start ) ; if ( options . pips ) { pips ( options . pips ) ; } if ( options . tooltips ) { tooltips ( ) ; } aria ( ) ; } setupSlider ( ) ; const scope_Self = { destroy : destroy , steps : getNextSteps , on : bindEvent , off : removeEvent , get : valueGet , set : valueSet , setHandle : valueSetHandle , reset : valueReset , __moveHandles : function ( upward , proposal , handleNumbers ) { moveHandles ( upward , proposal , scope_Locations , handleNumbers ) ; } , options : originalOptions , updateOptions : updateOptions , target : scope_Target , removePips : removePips , removeTooltips : removeTooltips , getPositions : function ( ) { return scope_Locations . slice ( ) ; } , getTooltips : function ( ) { return scope_Tooltips ; } , getOrigins : function ( ) { return scope_Handles ; } , pips : pips } as API ; return scope_Self ; } function initialize ( target , originalOptions ) { if ( ! target || ! target . nodeName ) { throw new Error ( "noUiSlider: create requires a single element, got: " + target ) ; } if ( target . noUiSlider ) { throw new Error ( "noUiSlider: Slider was already initialized." ) ; } const options = testOptions ( originalOptions ) ; const api = scope ( target , options , originalOptions ) ; target . noUiSlider = api ; return api ; } export { TargetElement as target } ; export { initialize as create } ; export { cssClasses } ; export default { __spectrum : Spectrum , cssClasses : cssClasses , create : initialize } ;