PageSourceSearch

https://www.lens-labo.com/js/nouislider.js?20261006035824

js lens-labo.com collected 2026-10-06 06:59:32 UTC 67,915 bytes, 2,347 lines download raw bytes

1/*! nouislider - 11.1.0 - 2018-04-02 11:18:13 */
2
3(function (factory) {
4
5    if ( typeof define === 'function' && define.amd ) {
6
7        // AMD. Register as an anonymous module.
8        define([], factory);
9
10    } else if ( typeof exports === 'object' ) {
11
12        // Node/CommonJS
13        module.exports = factory();
14
15    } else {
16
17        // Browser globals
18        window.noUiSlider = factory();
19    }
20
21}(function( ){
22
23	'use strict';
24
25	var VERSION = '11.1.0';
26
27
28	function isValidFormatter ( entry ) {
29		return typeof entry === 'object' && typeof entry.to === 'function' && typeof entry.from === 'function';
30	}
31
32	function removeElement ( el ) {
33		el.parentElement.removeChild(el);
34	}
35
36	function isSet ( value ) {
37		return value !== null && value !== undefined;
38	}
39
40	// Bindable version
41	function preventDefault ( e ) {
42		e.preventDefault();
43	}
44
45	// Removes duplicates from an array.
46	function unique ( array ) {
47		return array.filter(function(a){
48			return !this[a] ? this[a] = true : false;
49		}, {});
50	}
51
52	// Round a value to the closest 'to'.
53	function closest ( value, to ) {
54		return Math.round(value / to) * to;
55	}
56
57	// Current position of an element relative to the document.
58	function offset ( elem, orientation ) {
59
60		var rect = elem.getBoundingClientRect();
61		var doc = elem.ownerDocument;
62		var docElem = doc.documentElement;
63		var pageOffset = getPageOffset(doc);
64
65		// getBoundingClientRect contains left scroll in Chrome on Android.
66		// I haven't found a feature detection that proves this. Worst case
67		// scenario on mis-match: the 'tap' feature on horizontal sliders breaks.
68		if ( /webkit.*Chrome.*Mobile/i.test(navigator.userAgent) ) {
69			pageOffset.x = 0;
70		}
71
72		return orientation ? (rect.top + pageOffset.y - docElem.clientTop) : (rect.left + pageOffset.x - docElem.clientLeft);
73	}
74
75	// Checks whether a value is numerical.
76	function isNumeric ( a ) {
77		return typeof a === 'number' && !isNaN( a ) && isFinite( a );
78	}
79
80	// Sets a class and removes it after [duration] ms.
81	function addClassFor ( element, className, duration ) {
82		if (duration > 0) {
83		addClass(element, className);
84			setTimeout(function(){
85				removeClass(element, className);
86			}, duration);
87		}
88	}
89
90	// Limits a value to 0 - 100
91	function limit ( a ) {
92		return Math.max(Math.min(a, 100), 0);
93	}
94
95	// Wraps a variable as an array, if it isn't one yet.
96	// Note that an input array is returned by reference!
97	function asArray ( a ) {
98		return Array.isArray(a) ? a : [a];
99	}
100
101	// Counts decimals
102	function countDecimals ( numStr ) {
103		numStr = String(numStr);
104		var pieces = numStr.split(".");
105		return pieces.length > 1 ? pieces[1].length : 0;
106	}
107
108	// http://youmightnotneedjquery.com/#add_class
109	function addClass ( el, className ) {
110		if ( el.classList ) {
111			el.classList.add(className);
112		} else {
113			el.className += ' ' + className;
114		}
115	}
116
117	// http://youmightnotneedjquery.com/#remove_class
118	function removeClass ( el, className ) {
119		if ( el.classList ) {
120			el.classList.remove(className);
121		} else {
122			el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
123		}
124	}
125
126	// https://plainjs.com/javascript/attributes/adding-removing-and-testing-for-classes-9/
127	function hasClass ( el, className ) {
128		return el.classList ? el.classList.contains(className) : new RegExp('\\b' + className + '\\b').test(el.className);
129	}
130
131	// https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes
132	function getPageOffset ( doc ) {
133
134		var supportPageOffset = window.pageXOffset !== undefined;
135		var isCSS1Compat = ((doc.compatMode || "") === "CSS1Compat");
136		var x = supportPageOffset ? window.pageXOffset : isCSS1Compat ? doc.documentElement.scrollLeft : doc.body.scrollLeft;
137		var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? doc.documentElement.scrollTop : doc.body.scrollTop;
138
139		return {
140			x: x,
141			y: y
142		};
143	}
144
145	// we provide a function to compute constants instead
146	// of accessing window.* as soon as the module needs it
147	// so that we do not compute anything if not needed
148	function getActions ( ) {
149
150		// Determine the events to bind. IE11 implements pointerEvents without
151		// a prefix, which breaks compatibility with the IE10 implementation.
152		return window.navigator.pointerEnabled ? {
153			start: 'pointerdown',
154			move: 'pointermove',
155			end: 'pointerup'
156		} : window.navigator.msPointerEnabled ? {
157			start: 'MSPointerDown',
158			move: 'MSPointerMove',
159			end: 'MSPointerUp'
160		} : {
161			start: 'mousedown touchstart',
162			move: 'mousemove touchmove',
163			end: 'mouseup touchend'
164		};
165	}
166
167	// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
168	// Issue #785
169	function getSupportsPassive ( ) {
170
171		var supportsPassive = false;
172
173		try {
174
175			var opts = Object.defineProperty({}, 'passive', {
176				get: function() {
177					supportsPassive = true;
178				}
179			});
180
181			window.addEventListener('test', null, opts);
182
183		} catch (e) {}
184
185		return supportsPassive;
186	}
187
188	function getSupportsTouchActionNone ( ) {
189		return window.CSS && CSS.supports && CSS.supports('touch-action', 'none');
190	}
191
192
193// Value calculation
194
195	// Determine the size of a sub-range in relation to a full range.
196	function subRangeRatio ( pa, pb ) {
197		return (100 / (pb - pa));
198	}
199
200	// (percentage) How many percent is this value of this range?
201	function fromPercentage ( range, value ) {
202		return (value * 100) / ( range[1] - range[0] );
203	}
204
205	// (percentage) Where is this value on this range?
206	function toPercentage ( range, value ) {
207		return fromPercentage( range, range[0] < 0 ?
208			value + Math.abs(range[0]) :
209				value - range[0] );
210	}
211
212	// (value) How much is this percentage on this range?
213	function isPercentage ( range, value ) {
214		return ((value * ( range[1] - range[0] )) / 100) + range[0];
215	}
216
217
218// Range conversion
219
220	function getJ ( value, arr ) {
221
222		var j = 1;
223
224		while ( value >= arr[j] ){
225			j += 1;
226		}
227
228		return j;
229	}
230
231	// (percentage) Input a value, find where, on a scale of 0-100, it applies.
232	function toStepping ( xVal, xPct, value ) {
233
234		if ( value >= xVal.slice(-1)[0] ){
235			return 100;
236		}
237
238		var j = getJ( value, xVal );
239		var va = xVal[j-1];
240		var vb = xVal[j];
241		var pa = xPct[j-1];
242		var pb = xPct[j];
243
244		return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb));
245	}
246
247	// (value) Input a percentage, find where it is on the specified range.
248	function fromStepping ( xVal, xPct, value ) {
249
250		// There is no range group that fits 100
251		if ( value >= 100 ){
252			return xVal.slice(-1)[0];
253		}
254
255		var j = getJ( value, xPct );
256		var va = xVal[j-1];
257		var vb = xVal[j];
258		var pa = xPct[j-1];
259		var pb = xPct[j];
260
261		return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb));
262	}
263
264	// (percentage) Get the step that applies at a certain value.
265	function getStep ( xPct, xSteps, snap, value ) {
266
267		if ( value === 100 ) {
268			return value;
269		}
270
271		var j = getJ( value, xPct );
272		var a = xPct[j-1];
273		var b = xPct[j];
274
275		// If 'snap' is set, steps are used as fixed points on the slider.
276		if ( snap ) {
277
278			// Find the closest position, a or b.
279			if ((value - a) > ((b-a)/2)){
280				return b;
281			}
282
283			return a;
284		}
285
286		if ( !xSteps[j-1] ){
287			return value;
288		}
289
290		return xPct[j-1] + closest(
291			value - xPct[j-1],
292			xSteps[j-1]
293		);
294	}
295
296
297// Entry parsing
298
299	function handleEntryPoint ( index, value, that ) {
300
301		var percentage;
302
303		// Wrap numerical input in an array.
304		if ( typeof value === "number" ) {
305			value = [value];
306		}
307
308		// Reject any invalid input, by testing whether value is an array.
309		if ( !Array.isArray(value) ){
310			throw new Error("noUiSlider (" + VERSION + "): 'range' contains invalid value.");
311		}
312
313		// Covert min/max syntax to 0 and 100.
314		if ( index === 'min' ) {
315			percentage = 0;
316		} else if ( index === 'max' ) {
317			percentage = 100;
318		} else {
319			percentage = parseFloat( index );
320		}
321
322		// Check for correct input.
323		if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) {
324			throw new Error("noUiSlider (" + VERSION + "): 'range' value isn't numeric.");
325		}
326
327		// Store values.
328		that.xPct.push( percentage );
329		that.xVal.push( value[0] );
330
331		// NaN will evaluate to false too, but to keep
332		// logging clear, set step explicitly. Make sure
333		// not to override the 'step' setting with false.
334		if ( !percentage ) {
335			if ( !isNaN( value[1] ) ) {
336				that.xSteps[0] = value[1];
337			}
338		} else {
339			that.xSteps.push( isNaN(value[1]) ? false : value[1] );
340		}
341
342		that.xHighestCompleteStep.push(0);
343	}
344
345	function handleStepPoint ( i, n, that ) {
346
347		// Ignore 'false' stepping.
348		if ( !n ) {
349			return true;
350		}
351
352		// Factor to range ratio
353		that.xSteps[i] = fromPercentage([that.xVal[i], that.xVal[i+1]], n) / subRangeRatio(that.xPct[i], that.xPct[i+1]);
354
355		var totalSteps = (that.xVal[i+1] - that.xVal[i]) / that.xNumSteps[i];
356		var highestStep = Math.ceil(Number(totalSteps.toFixed(3)) - 1);
357		var step = that.xVal[i] + (that.xNumSteps[i] * highestStep);
358
359		that.xHighestCompleteStep[i] = step;
360	}
361
362
363// Interface
364
365	function Spectrum ( entry, snap, singleStep ) {
366
367		this.xPct = [];
368		this.xVal = [];
369		this.xSteps = [ singleStep || false ];
370		this.xNumSteps = [ false ];
371		this.xHighestCompleteStep = [];
372
373		this.snap = snap;
374
375		var index;
376		var ordered = []; // [0, 'min'], [1, '50%'], [2, 'max']
377
378		// Map the object keys to an array.
379		for ( index in entry ) {
380			if ( entry.hasOwnProperty(index) ) {
381				ordered.push([entry[index], index]);
382			}
383		}
384
385		// Sort all entries by value (numeric sort).
386		if ( ordered.length && typeof ordered[0][0] === "object" ) {
387			ordered.sort(function(a, b) { return a[0][0] - b[0][0]; });
388		} else {
389			ordered.sort(function(a, b) { return a[0] - b[0]; });
390		}
391
392
393		// Convert all entries to subranges.
394		for ( index = 0; index < ordered.length; index++ ) {
395			handleEntryPoint(ordered[index][1], ordered[index][0], this);
396		}
397
398		// Store the actual step values.
399		// xSteps is sorted in the same order as xPct and xVal.
400		this.xNumSteps = this.xSteps.slice(0);
401
402		// Convert all numeric steps to the percentage of the subrange they represent.
403		for ( index = 0; index < this.xNumSteps.length; index++ ) {
404			handleStepPoint(index, this.xNumSteps[index], this);
405		}
406	}
407
408	Spectrum.prototype.getMargin = function ( value ) {
409
410		var step = this.xNumSteps[0];
411
412		if ( step && ((value / step) % 1) !== 0 ) {
413			throw new Error("noUiSlider (" + VERSION + "): 'limit', 'margin' and 'padding' must be divisible by step.");
414		}
415
416		return this.xPct.length === 2 ? fromPercentage(this.xVal, value) : false;
417	};
418
419	Spectrum.prototype.toStepping = function ( value ) {
420
421		value = toStepping( this.xVal, this.xPct, value );
422
423		return value;
424	};
425
426	Spectrum.prototype.fromStepping = function ( value ) {
427
428		return fromStepping( this.xVal, this.xPct, value );
429	};
430
431	Spectrum.prototype.getStep = function ( value ) {
432
433		value = getStep(this.xPct, this.xSteps, this.snap, value );
434
435		return value;
436	};
437
438	Spectrum.prototype.getNearbySteps = function ( value ) {
439
440		var j = getJ(value, this.xPct);
441
442		return {
443			stepBefore: { startValue: this.xVal[j-2], step: this.xNumSteps[j-2], highestStep: this.xHighestCompleteStep[j-2] },
444			thisStep: { startValue: this.xVal[j-1], step: this.xNumSteps[j-1], highestStep: this.xHighestCompleteStep[j-1] },
445			stepAfter: { startValue: this.xVal[j-0], step: this.xNumSteps[j-0], highestStep: this.xHighestCompleteStep[j-0] }
446		};
447	};
448
449	Spectrum.prototype.countStepDecimals = function () {
450		var stepDecimals = this.xNumSteps.map(countDecimals);
451		return Math.max.apply(null, stepDecimals);
452	};
453
454	// Outside testing
455	Spectrum.prototype.convert = function ( value ) {
456		return this.getStep(this.toStepping(value));
457	};
458
459/*	Every input option is tested and parsed. This'll prevent
460	endless validation in internal methods. These tests are
461	structured with an item for every option available. An
462	option can be marked as required by setting the 'r' flag.
463	The testing function is provided with three arguments:
464		- The provided value for the option;
465		- A reference to the options object;
466		- The name for the option;
467
468	The testing function returns false when an error is detected,
469	or true when everything is OK. It can also modify the option
470	object, to make sure all values can be correctly looped elsewhere. */
471
472	var defaultFormatter = { 'to': function( value ){
473		return value !== undefined && value.toFixed(2);
474	}, 'from': Number };
475
476	function validateFormat ( entry ) {
477
478		// Any object with a to and from method is supported.
479		if ( isValidFormatter(entry) ) {
480			return true;
481		}
482
483		throw new Error("noUiSlider (" + VERSION + "): 'format' requires 'to' and 'from' methods.");
484	}
485
486	function testStep ( parsed, entry ) {
487
488		if ( !isNumeric( entry ) ) {
489			throw new Error("noUiSlider (" + VERSION + "): 'step' is not numeric.");
490		}
491
492		// The step option can still be used to set stepping
493		// for linear sliders. Overwritten if set in 'range'.
494		parsed.singleStep = entry;
495	}
496
497	function testRange ( parsed, entry ) {
498
499		// Filter incorrect input.
500		if ( typeof entry !== 'object' || Array.isArray(entry) ) {
501			throw new Error("noUiSlider (" + VERSION + "): 'range' is not an object.");
502		}
503
504		// Catch missing start or end.
505		if ( entry.min === undefined || entry.max === undefined ) {
506			throw new Error("noUiSlider (" + VERSION + "): Missing 'min' or 'max' in 'range'.");
507		}
508
509		// Catch equal start or end.
510		if ( entry.min === entry.max ) {
511			throw new Error("noUiSlider (" + VERSION + "): 'range' 'min' and 'max' cannot be equal.");
512		}
513
514		parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.singleStep);
515	}
516
517	function testStart ( parsed, entry ) {
518
519		entry = asArray(entry);
520
521		// Validate input. Values aren't tested, as the public .val method
522		// will always provide a valid location.
523		if ( !Array.isArray( entry ) || !entry.length ) {
524			throw new Error("noUiSlider (" + VERSION + "): 'start' option is incorrect.");
525		}
526
527		// Store the number of handles.
528		parsed.handles = entry.length;
529
530		// When the slider is initialized, the .val method will
531		// be called with the start options.
532		parsed.start = entry;
533	}
534
535	function testSnap ( parsed, entry ) {
536
537		// Enforce 100% stepping within subranges.
538		parsed.snap = entry;
539
540		if ( typeof entry !== 'boolean' ){
541			throw new Error("noUiSlider (" + VERSION + "): 'snap' option must be a boolean.");
542		}
543	}
544
545	function testAnimate ( parsed, entry ) {
546
547		// Enforce 100% stepping within subranges.
548		parsed.animate = entry;
549
550		if ( typeof entry !== 'boolean' ){
551			throw new Error("noUiSlider (" + VERSION + "): 'animate' option must be a boolean.");
552		}
553	}
554
555	function testAnimationDuration ( parsed, entry ) {
556
557		parsed.animationDuration = entry;
558
559		if ( typeof entry !== 'number' ){
560			throw new Error("noUiSlider (" + VERSION + "): 'animationDuration' option must be a number.");
561		}
562	}
563
564	function testConnect ( parsed, entry ) {
565
566		var connect = [false];
567		var i;
568
569		// Map legacy options
570		if ( entry === 'lower' ) {
571			entry = [true, false];
572		}
573
574		else if ( entry === 'upper' ) {
575			entry = [false, true];
576		}
577
578		// Handle boolean options
579		if ( entry === true || entry === false ) {
580
581			for ( i = 1; i < parsed.handles; i++ ) {
582				connect.push(entry);
583			}
584
585			connect.push(false);
586		}
587
588		// Reject invalid input
589		else if ( !Array.isArray( entry ) || !entry.length || entry.length !== parsed.handles + 1 ) {
590			throw new Error("noUiSlider (" + VERSION + "): 'connect' option doesn't match handle count.");
591		}
592
593		else {
594			connect = entry;
595		}
596
597		parsed.connect = connect;
598	}
599
600	function testOrientation ( parsed, entry ) {
601
602		// Set orientation to an a numerical value for easy
603		// array selection.
604		switch ( entry ){
605			case 'horizontal':
606				parsed.ort = 0;
607				break;
608			case 'vertical':
609				parsed.ort = 1;
610				break;
611			default:
612				throw new Error("noUiSlider (" + VERSION + "): 'orientation' option is invalid.");
613		}
614	}
615
616	function testMargin ( parsed, entry ) {
617
618		if ( !isNumeric(entry) ){
619			throw new Error("noUiSlider (" + VERSION + "): 'margin' option must be numeric.");
620		}
621
622		// Issue #582
623		if ( entry === 0 ) {
624			return;
625		}
626
627		parsed.margin = parsed.spectrum.getMargin(entry);
628
629		if ( !parsed.margin ) {
630			throw new Error("noUiSlider (" + VERSION + "): 'margin' option is only supported on linear sliders.");
631		}
632	}
633
634	function testLimit ( parsed, entry ) {
635
636		if ( !isNumeric(entry) ){
637			throw new Error("noUiSlider (" + VERSION + "): 'limit' option must be numeric.");
638		}
639
640		parsed.limit = parsed.spectrum.getMargin(entry);
641
642		if ( !parsed.limit || parsed.handles < 2 ) {
643			throw new Error("noUiSlider (" + VERSION + "): 'limit' option is only supported on linear sliders with 2 or more handles.");
644		}
645	}
646
647	function testPadding ( parsed, entry ) {
648
649		if ( !isNumeric(entry) && !Array.isArray(entry) ){
650			throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers.");
651		}
652
653		if ( Array.isArray(entry) && !(entry.length === 2 || isNumeric(entry[0]) || isNumeric(entry[1])) ) {
654			throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers.");
655		}
656
657		if ( entry === 0 ) {
658			return;
659		}
660
661		if ( !Array.isArray(entry) ) {
662			entry = [entry, entry];
663		}
664
665		// 'getMargin' returns false for invalid values.
666		parsed.padding = [parsed.spectrum.getMargin(entry[0]), parsed.spectrum.getMargin(entry[1])];
667
668		if ( parsed.padding[0] === false || parsed.padding[1] === false ) {
669			throw new Error("noUiSlider (" + VERSION + "): 'padding' option is only supported on linear sliders.");
670		}
671
672		if ( parsed.padding[0] < 0 || parsed.padding[1] < 0 ) {
673			throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be a positive number(s).");
674		}
675
676		if ( parsed.padding[0] + parsed.padding[1] >= 100 ) {
677			throw new Error("noUiSlider (" + VERSION + "): 'padding' option must not exceed 100% of the range.");
678		}
679	}
680
681	function testDirection ( parsed, entry ) {
682
683		// Set direction as a numerical value for easy parsing.
684		// Invert connection for RTL sliders, so that the proper
685		// handles get the connect/background classes.
686		switch ( entry ) {
687			case 'ltr':
688				parsed.dir = 0;
689				break;
690			case 'rtl':
691				parsed.dir = 1;
692				break;
693			default:
694				throw new Error("noUiSlider (" + VERSION + "): 'direction' option was not recognized.");
695		}
696	}
697
698	function testBehaviour ( parsed, entry ) {
699
700		// Make sure the input is a string.
701		if ( typeof entry !== 'string' ) {
702			throw new Error("noUiSlider (" + VERSION + "): 'behaviour' must be a string containing options.");
703		}
704
705		// Check if the string contains any keywords.
706		// None are required.
707		var tap = entry.indexOf('tap') >= 0;
708		var drag = entry.indexOf('drag') >= 0;
709		var fixed = entry.indexOf('fixed') >= 0;
710		var snap = entry.indexOf('snap') >= 0;
711		var hover = entry.indexOf('hover') >= 0;
712
713		if ( fixed ) {
714
715			if ( parsed.handles !== 2 ) {
716				throw new Error("noUiSlider (" + VERSION + "): 'fixed' behaviour must be used with 2 handles");
717			}
718
719			// Use margin to enforce fixed state
720			testMargin(parsed, parsed.start[1] - parsed.start[0]);
721		}
722
723		parsed.events = {
724			tap: tap || snap,
725			drag: drag,
726			fixed: fixed,
727			snap: snap,
728			hover: hover
729		};
730	}
731
732	function testTooltips ( parsed, entry ) {
733
734		if ( entry === false ) {
735			return;
736		}
737
738		else if ( entry === true ) {
739
740			parsed.tooltips = [];
741
742			for ( var i = 0; i < parsed.handles; i++ ) {
743				parsed.tooltips.push(true);
744			}
745		}
746
747		else {
748
749			parsed.tooltips = asArray(entry);
750
751			if ( parsed.tooltips.length !== parsed.handles ) {
752				throw new Error("noUiSlider (" + VERSION + "): must pass a formatter for all handles.");
753			}
754
755			parsed.tooltips.forEach(function(formatter){
756				if ( typeof formatter !== 'boolean' && (typeof formatter !== 'object' || typeof formatter.to !== 'function') ) {
757					throw new Error("noUiSlider (" + VERSION + "): 'tooltips' must be passed a formatter or 'false'.");
758				}
759			});
760		}
761	}
762
763	function testAriaFormat ( parsed, entry ) {
764		parsed.ariaFormat = entry;
765		validateFormat(entry);
766	}
767
768	function testFormat ( parsed, entry ) {
769		parsed.format = entry;
770		validateFormat(entry);
771	}
772
773	function testCssPrefix ( parsed, entry ) {
774
775		if ( typeof entry !== 'string' && entry !== false ) {
776			throw new Error("noUiSlider (" + VERSION + "): 'cssPrefix' must be a string or `false`.");
777		}
778
779		parsed.cssPrefix = entry;
780	}
781
782	function testCssClasses ( parsed, entry ) {
783
784		if ( typeof entry !== 'object' ) {
785			throw new Error("noUiSlider (" + VERSION + "): 'cssClasses' must be an object.");
786		}
787
788		if ( typeof parsed.cssPrefix === 'string' ) {
789			parsed.cssClasses = {};
790
791			for ( var key in entry ) {
792				if ( !entry.hasOwnProperty(key) ) { continue; }
793
794				parsed.cssClasses[key] = parsed.cssPrefix + entry[key];
795			}
796		} else {
797			parsed.cssClasses = entry;
798		}
799	}
800
801	// Test all developer settings and parse to assumption-safe values.
802	function testOptions ( options ) {
803
804		// To prove a fix for #537, freeze options here.
805		// If the object is modified, an error will be thrown.
806		// Object.freeze(options);
807
808		var parsed = {
809			margin: 0,
810			limit: 0,
811			padding: 0,
812			animate: true,
813			animationDuration: 300,
814			ariaFormat: defaultFormatter,
815			format: defaultFormatter
816		};
817
818		// Tests are executed in the order they are presented here.
819		var tests = {
820			'step': { r: false, t: testStep },
821			'start': { r: true, t: testStart },
822			'connect': { r: true, t: testConnect },
823			'direction': { r: true, t: testDirection },
824			'snap': { r: false, t: testSnap },
825			'animate': { r: false, t: testAnimate },
826			'animationDuration': { r: false, t: testAnimationDuration },
827			'range': { r: true, t: testRange },
828			'orientation': { r: false, t: testOrientation },
829			'margin': { r: false, t: testMargin },
830			'limit': { r: false, t: testLimit },
831			'padding': { r: false, t: testPadding },
832			'behaviour': { r: true, t: testBehaviour },
833			'ariaFormat': { r: false, t: testAriaFormat },
834			'format': { r: false, t: testFormat },
835			'tooltips': { r: false, t: testTooltips },
836			'cssPrefix': { r: true, t: testCssPrefix },
837			'cssClasses': { r: true, t: testCssClasses }
838		};
839
840		var defaults = {
841			'connect': false,
842			'direction': 'ltr',
843			'behaviour': 'tap',
844			'orientation': 'horizontal',
845			'cssPrefix' : 'noUi-',
846			'cssClasses': {
847				target: 'target',
848				base: 'base',
849				origin: 'origin',
850				handle: 'handle',
851				handleLower: 'handle-lower',
852				handleUpper: 'handle-upper',
853				horizontal: 'horizontal',
854				vertical: 'vertical',
855				background: 'background',
856				connect: 'connect',
857				connects: 'connects',
858				ltr: 'ltr',
859				rtl: 'rtl',
860				draggable: 'draggable',
861				drag: 'state-drag',
862				tap: 'state-tap',
863				active: 'active',
864				tooltip: 'tooltip',
865				pips: 'pips',
866				pipsHorizontal: 'pips-horizontal',
867				pipsVertical: 'pips-vertical',
868				marker: 'marker',
869				markerHorizontal: 'marker-horizontal',
870				markerVertical: 'marker-vertical',
871				markerNormal: 'marker-normal',
872				markerLarge: 'marker-large',
873				markerSub: 'marker-sub',
874				value: 'value',
875				valueHorizontal: 'value-horizontal',
876				valueVertical: 'value-vertical',
877				valueNormal: 'value-normal',
878				valueLarge: 'value-large',
879				valueSub: 'value-sub'
880			}
881		};
882
883		// AriaFormat defaults to regular format, if any.
884		if ( options.format && !options.ariaFormat ) {
885			options.ariaFormat = options.format;
886		}
887
888		// Run all options through a testing mechanism to ensure correct
889		// input. It should be noted that options might get modified to
890		// be handled properly. E.g. wrapping integers in arrays.
891		Object.keys(tests).forEach(function( name ){
892
893			// If the option isn't set, but it is required, throw an error.
894			if ( !isSet(options[name]) && defaults[name] === undefined ) {
895
896				if ( tests[name].r ) {
897					throw new Error("noUiSlider (" + VERSION + "): '" + name + "' is required.");
898				}
899
900				return true;
901			}
902
903			tests[name].t( parsed, !isSet(options[name]) ? defaults[name] : options[name] );
904		});
905
906		// Forward pips options
907		parsed.pips = options.pips;
908
909		// All recent browsers accept unprefixed transform.
910		// We need -ms- for IE9 and -webkit- for older Android;
911		// Assume use of -webkit- if unprefixed and -ms- are not supported.
912		// https://caniuse.com/#feat=transforms2d
913		var d = document.createElement("div");
914		var msPrefix = d.style.msTransform !== undefined;
915		var noPrefix = d.style.transform !== undefined;
916
917		parsed.transformRule = noPrefix ? 'transform' : (msPrefix ? 'msTransform' : 'webkitTransform');
918
919		// Pips don't move, so we can place them using left/top.
920		var styles = [['left', 'top'], ['right', 'bottom']];
921
922		parsed.style = styles[parsed.dir][parsed.ort];
923
924		return parsed;
925	}
926
927
928function scope ( target, options, originalOptions ){
929
930	var actions = getActions();
931	var supportsTouchActionNone = getSupportsTouchActionNone();
932	var supportsPassive = supportsTouchActionNone && getSupportsPassive();
933
934	// All variables local to 'scope' are prefixed with 'scope_'
935	var scope_Target = target;
936	var scope_Locations = [];
937	var scope_Base;
938	var scope_Handles;
939	var scope_HandleNumbers = [];
940	var scope_ActiveHandlesCount = 0;
941	var scope_Connects;
942	var scope_Spectrum = options.spectrum;
943	var scope_Values = [];
944	var scope_Events = {};
945	var scope_Self;
946	var scope_Pips;
947	var scope_Document = target.ownerDocument;
948	var scope_DocumentElement = scope_Document.documentElement;
949	var scope_Body = scope_Document.body;
950
951
952	// For horizontal sliders in standard ltr documents,
953	// make .noUi-origin overflow to the left so the document doesn't scroll.
954	var scope_DirOffset = (scope_Document.dir === 'rtl') || (options.ort === 1) ? 0 : 100;
955
956/*! In this file: Construction of DOM elements; */
957
958	// Creates a node, adds it to target, returns the new node.
959	function addNodeTo ( addTarget, className ) {
960
961		var div = scope_Document.createElement('div');
962
963		if ( className ) {
964			addClass(div, className);
965		}
966
967		addTarget.appendChild(div);
968
969		return div;
970	}
971
972	// Append a origin to the base
973	function addOrigin ( base, handleNumber ) {
974
975		var origin = addNodeTo(base, options.cssClasses.origin);
976		var handle = addNodeTo(origin, options.cssClasses.handle);
977
978		handle.setAttribute('data-handle', handleNumber);
979
980		// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
981		// 0 = focusable and reachable
982		handle.setAttribute('tabindex', '0');
983		handle.setAttribute('role', 'slider');
984		handle.setAttribute('aria-orientation', options.ort ? 'vertical' : 'horizontal');
985
986		if ( handleNumber === 0 ) {
987			addClass(handle, options.cssClasses.handleLower);
988		}
989
990		else if ( handleNumber === options.handles - 1 ) {
991			addClass(handle, options.cssClasses.handleUpper);
992		}
993
994		return origin;
995	}
996
997	// Insert nodes for connect elements
998	function addConnect ( base, add ) {
999
1000		if ( !add ) {
1001			return false;
1002		}
1003
1004		return addNodeTo(base, options.cssClasses.connect);
1005	}
1006
1007	// Add handles to the slider base.
1008	function addElements ( connectOptions, base ) {
1009
1010		var connectBase = addNodeTo(base, options.cssClasses.connects);
1011
1012		scope_Handles = [];
1013		scope_Connects = [];
1014
1015		scope_Connects.push(addConnect(connectBase, connectOptions[0]));
1016
1017		// [::::O====O====O====]
1018		// connectOptions = [0, 1, 1, 1]
1019
1020		for ( var i = 0; i < options.handles; i++ ) {
1021			// Keep a list of all added handles.
1022			scope_Handles.push(addOrigin(base, i));
1023			scope_HandleNumbers[i] = i;
1024			scope_Connects.push(addConnect(connectBase, connectOptions[i + 1]));
1025		}
1026	}
1027
1028	// Initialize a single slider.
1029	function addSlider ( addTarget ) {
1030
1031		// Apply classes and data to the target.
1032		addClass(addTarget, options.cssClasses.target);
1033
1034		if ( options.dir === 0 ) {
1035			addClass(addTarget, options.cssClasses.ltr);
1036		} else {
1037			addClass(addTarget, options.cssClasses.rtl);
1038		}
1039
1040		if ( options.ort === 0 ) {
1041			addClass(addTarget, options.cssClasses.horizontal);
1042		} else {
1043			addClass(addTarget, options.cssClasses.vertical);
1044		}
1045
1046		scope_Base = addNodeTo(addTarget, options.cssClasses.base);
1047	}
1048
1049
1050	function addTooltip ( handle, handleNumber ) {
1051
1052		if ( !options.tooltips[handleNumber] ) {
1053			return false;
1054		}
1055
1056		return addNodeTo(handle.firstChild, options.cssClasses.tooltip);
1057	}
1058
1059	// The tooltips option is a shorthand for using the 'update' event.
1060	function tooltips ( ) {
1061
1062		// Tooltips are added with options.tooltips in original order.
1063		var tips = scope_Handles.map(addTooltip);
1064
1065		bindEvent('update', function(values, handleNumber, unencoded) {
1066
1067			if ( !tips[handleNumber] ) {
1068				return;
1069			}
1070
1071			var formattedValue = values[handleNumber];
1072
1073			if ( options.tooltips[handleNumber] !== true ) {
1074				formattedValue = options.tooltips[handleNumber].to(unencoded[handleNumber]);
1075			}
1076
1077			tips[handleNumber].innerHTML = formattedValue;
1078		});
1079	}
1080
1081
1082	function aria ( ) {
1083
1084		bindEvent('update', function ( values, handleNumber, unencoded, tap, positions ) {
1085
1086			// Update Aria Values for all handles, as a change in one changes min and max values for the next.
1087			scope_HandleNumbers.forEach(function( index ){
1088
1089				var handle = scope_Handles[index];
1090
1091				var min = checkHandlePosition(scope_Locations, index, 0, true, true, true);
1092				var max = checkHandlePosition(scope_Locations, index, 100, true, true, true);
1093
1094				var now = positions[index];
1095				var text = options.ariaFormat.to(unencoded[index]);
1096
1097				handle.children[0].setAttribute('aria-valuemin', min.toFixed(1));
1098				handle.children[0].setAttribute('aria-valuemax', max.toFixed(1));
1099				handle.children[0].setAttribute('aria-valuenow', now.toFixed(1));
1100				handle.children[0].setAttribute('aria-valuetext', text);
1101			});
1102		});
1103	}
1104
1105
1106	function getGroup ( mode, values, stepped ) {
1107
1108		// Use the range.
1109		if ( mode === 'range' || mode === 'steps' ) {
1110			return scope_Spectrum.xVal;
1111		}
1112
1113		if ( mode === 'count' ) {
1114
1115			if ( values < 2 ) {
1116				throw new Error("noUiSlider (" + VERSION + "): 'values' (>= 2) required for mode 'count'.");
1117			}
1118
1119			// Divide 0 - 100 in 'count' parts.
1120			var interval = values - 1;
1121			var spread = ( 100 / interval );
1122
1123			values = [];
1124
1125			// List these parts and have them handled as 'positions'.
1126			while ( interval-- ) {
1127				values[ interval ] = ( interval * spread );
1128			}
1129
1130			values.push(100);
1131
1132			mode = 'positions';
1133		}
1134
1135		if ( mode === 'positions' ) {
1136
1137			// Map all percentages to on-range values.
1138			return values.map(function( value ){
1139				return scope_Spectrum.fromStepping( stepped ? scope_Spectrum.getStep( value ) : value );
1140			});
1141		}
1142
1143		if ( mode === 'values' ) {
1144
1145			// If the value must be stepped, it needs to be converted to a percentage first.
1146			if ( stepped ) {
1147
1148				return values.map(function( value ){
1149
1150					// Convert to percentage, apply step, return to value.
1151					return scope_Spectrum.fromStepping( scope_Spectrum.getStep( scope_Spectrum.toStepping( value ) ) );
1152				});
1153
1154			}
1155
1156			// Otherwise, we can simply use the values.
1157			return values;
1158		}
1159	}
1160
1161	function generateSpread ( density, mode, group ) {
1162
1163		function safeIncrement(value, increment) {
1164			// Avoid floating point variance by dropping the smallest decimal places.
1165			return (value + increment).toFixed(7) / 1;
1166		}
1167
1168		var indexes = {};
1169		var firstInRange = scope_Spectrum.xVal[0];
1170		var lastInRange = scope_Spectrum.xVal[scope_Spectrum.xVal.length-1];
1171		var ignoreFirst = false;
1172		var ignoreLast = false;
1173		var prevPct = 0;
1174
1175		// Create a copy of the group, sort it and filter away all duplicates.
1176		group = unique(group.slice().sort(function(a, b){ return a - b; }));
1177
1178		// Make sure the range starts with the first element.
1179		if ( group[0] !== firstInRange ) {
1180			group.unshift(firstInRange);
1181			ignoreFirst = true;
1182		}
1183
1184		// Likewise for the last one.
1185		if ( group[group.length - 1] !== lastInRange ) {
1186			group.push(lastInRange);
1187			ignoreLast = true;
1188		}
1189
1190		group.forEach(function ( current, index ) {
1191
1192			// Get the current step and the lower + upper positions.
1193			var step;
1194			var i;
1195			var q;
1196			var low = current;
1197			var high = group[index+1];
1198			var newPct;
1199			var pctDifference;
1200			var pctPos;
1201			var type;
1202			var steps;
1203			var realSteps;
1204			var stepsize;
1205
1206			// When using 'steps' mode, use the provided steps.
1207			// Otherwise, we'll step on to the next subrange.
1208			if ( mode === 'steps' ) {
1209				step = scope_Spectrum.xNumSteps[ index ];
1210			}
1211
1212			// Default to a 'full' step.
1213			if ( !step ) {
1214				step = high-low;
1215			}
1216
1217			// Low can be 0, so test for false. If high is undefined,
1218			// we are at the last subrange. Index 0 is already handled.
1219			if ( low === false || high === undefined ) {
1220				return;
1221			}
1222
1223			// Make sure step isn't 0, which would cause an infinite loop (#654)
1224			step = Math.max(step, 0.0000001);
1225
1226			// Find all steps in the subrange.
1227			for ( i = low; i <= high; i = safeIncrement(i, step) ) {
1228
1229				// Get the percentage value for the current step,
1230				// calculate the size for the subrange.
1231				newPct = scope_Spectrum.toStepping( i );
1232				pctDifference = newPct - prevPct;
1233
1234				steps = pctDifference / density;
1235				realSteps = Math.round(steps);
1236
1237				// This ratio represents the amount of percentage-space a point indicates.
1238				// For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided.
1239				// Round the percentage offset to an even number, then divide by two
1240				// to spread the offset on both sides of the range.
1241				stepsize = pctDifference/realSteps;
1242
1243				// Divide all points evenly, adding the correct number to this subrange.
1244				// Run up to <= so that 100% gets a point, event if ignoreLast is set.
1245				for ( q = 1; q <= realSteps; q += 1 ) {
1246
1247					// The ratio between the rounded value and the actual size might be ~1% off.
1248					// Correct the percentage offset by the number of points
1249					// per subrange. density = 1 will result in 100 points on the
1250					// full range, 2 for 50, 4 for 25, etc.
1251					pctPos = prevPct + ( q * stepsize );
1252					indexes[pctPos.toFixed(5)] = ['x', 0];
1253				}
1254
1255				// Determine the point type.
1256				type = (group.indexOf(i) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 );
1257
1258				// Enforce the 'ignoreFirst' option by overwriting the type for 0.
1259				if ( !index && ignoreFirst ) {
1260					type = 0;
1261				}
1262
1263				if ( !(i === high && ignoreLast)) {
1264					// Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value.
1265					indexes[newPct.toFixed(5)] = [i, type];
1266				}
1267
1268				// Update the percentage count.
1269				prevPct = newPct;
1270			}
1271		});
1272
1273		return indexes;
1274	}
1275
1276	function addMarking ( spread, filterFunc, formatter ) {
1277
1278		var element = scope_Document.createElement('div');
1279
1280		var valueSizeClasses = [
1281			options.cssClasses.valueNormal,
1282			options.cssClasses.valueLarge,
1283			options.cssClasses.valueSub
1284		];
1285		var markerSizeClasses = [
1286			options.cssClasses.markerNormal,
1287			options.cssClasses.markerLarge,
1288			options.cssClasses.markerSub
1289		];
1290		var valueOrientationClasses = [
1291			options.cssClasses.valueHorizontal,
1292			options.cssClasses.valueVertical
1293		];
1294		var markerOrientationClasses = [
1295			options.cssClasses.markerHorizontal,
1296			options.cssClasses.markerVertical
1297		];
1298
1299		addClass(element, options.cssClasses.pips);
1300		addClass(element, options.ort === 0 ? options.cssClasses.pipsHorizontal : options.cssClasses.pipsVertical);
1301
1302		function getClasses( type, source ){
1303			var a = source === options.cssClasses.value;
1304			var orientationClasses = a ? valueOrientationClasses : markerOrientationClasses;
1305			var sizeClasses = a ? valueSizeClasses : markerSizeClasses;
1306
1307			return source + ' ' + orientationClasses[options.ort] + ' ' + sizeClasses[type];
1308		}
1309
1310		function addSpread ( offset, values ){
1311
1312			// Apply the filter function, if it is set.
1313			values[1] = (values[1] && filterFunc) ? filterFunc(values[0], values[1]) : values[1];
1314
1315			// Add a marker for every point
1316			var node = addNodeTo(element, false);
1317				node.className = getClasses(values[1], options.cssClasses.marker);
1318				node.style[options.style] = offset + '%';
1319
1320			// Values are only appended for points marked '1' or '2'.
1321			if ( values[1] ) {
1322				node = addNodeTo(element, false);
1323				node.className = getClasses(values[1], options.cssClasses.value);
1324				node.setAttribute('data-value', values[0]);
1325				node.style[options.style] = offset + '%';
1326				node.innerText = formatter.to(values[0]);
1327			}
1328		}
1329
1330		// Append all points.
1331		Object.keys(spread).forEach(function(a){
1332			addSpread(a, spread[a]);
1333		});
1334
1335		return element;
1336	}
1337
1338	function removePips ( ) {
1339		if ( scope_Pips ) {
1340			removeElement(scope_Pips);
1341			scope_Pips = null;
1342		}
1343	}
1344
1345	function pips ( grid ) {
1346
1347		// Fix #669
1348		removePips();
1349
1350		var mode = grid.mode;
1351		var density = grid.density || 1;
1352		var filter = grid.filter || false;
1353		var values = grid.values || false;
1354		var stepped = grid.stepped || false;
1355		var group = getGroup( mode, values, stepped );
1356		var spread = generateSpread( density, mode, group );
1357		var format = grid.format || {
1358			to: Math.round
1359		};
1360
1361		scope_Pips = scope_Target.appendChild(addMarking(
1362			spread,
1363			filter,
1364			format
1365		));
1366
1367		return scope_Pips;
1368	}
1369
1370/*! In this file: Browser events (not slider events like slide, change); */
1371
1372	// Shorthand for base dimensions.
1373	function baseSize ( ) {
1374		var rect = scope_Base.getBoundingClientRect();
1375		var alt = 'offset' + ['Width', 'Height'][options.ort];
1376		return options.ort === 0 ? (rect.width||scope_Base[alt]) : (rect.height||scope_Base[alt]);
1377	}
1378
1379	// Handler for attaching events trough a proxy.
1380	function attachEvent ( events, element, callback, data ) {
1381
1382		// This function can be used to 'filter' events to the slider.
1383		// element is a node, not a nodeList
1384
1385		var method = function ( e ){
1386
1387			e = fixEvent(e, data.pageOffset, data.target || element);
1388
1389			// fixEvent returns false if this event has a different target
1390			// when handling (multi-) touch events;
1391			if ( !e ) {
1392				return false;
1393			}
1394
1395			// doNotReject is passed by all end events to make sure released touches
1396			// are not rejected, leaving the slider "stuck" to the cursor;
1397			if ( scope_Target.hasAttribute('disabled') && !data.doNotReject ) {
1398				return false;
1399			}
1400
1401			// Stop if an active 'tap' transition is taking place.
1402			if ( hasClass(scope_Target, options.cssClasses.tap) && !data.doNotReject ) {
1403				return false;
1404			}
1405
1406			// Ignore right or middle clicks on start #454
1407			if ( events === actions.start && e.buttons !== undefined && e.buttons > 1 ) {
1408				return false;
1409			}
1410
1411			// Ignore right or middle clicks on start #454
1412			if ( data.hover && e.buttons ) {
1413				return false;
1414			}
1415
1416			// 'supportsPassive' is only true if a browser also supports touch-action: none in CSS.
1417			// iOS safari does not, so it doesn't get to benefit from passive scrolling. iOS does support
1418			// touch-action: manipulation, but that allows panning, which breaks
1419			// sliders after zooming/on non-responsive pages.
1420			// See: https://bugs.webkit.org/show_bug.cgi?id=133112
1421			if ( !supportsPassive ) {
1422				e.preventDefault();
1423			}
1424
1425			e.calcPoint = e.points[ options.ort ];
1426
1427			// Call the event handler with the event [ and additional data ].
1428			callback ( e, data );
1429		};
1430
1431		var methods = [];
1432
1433		// Bind a closure on the target for every event type.
1434		events.split(' ').forEach(function( eventName ){
1435			element.addEventListener(eventName, method, supportsPassive ? { passive: true } : false);
1436			methods.push([eventName, method]);
1437		});
1438
1439		return methods;
1440	}
1441
1442	// Provide a clean event with standardized offset values.
1443	function fixEvent ( e, pageOffset, eventTarget ) {
1444
1445		// Filter the event to register the type, which can be
1446		// touch, mouse or pointer. Offset changes need to be
1447		// made on an event specific basis.
1448		var touch = e.type.indexOf('touch') === 0;
1449		var mouse = e.type.indexOf('mouse') === 0;
1450		var pointer = e.type.indexOf('pointer') === 0;
1451
1452		var x;
1453		var y;
1454
1455		// IE10 implemented pointer events with a prefix;
1456		if ( e.type.indexOf('MSPointer') === 0 ) {
1457			pointer = true;
1458		}
1459
1460		// In the event that multitouch is activated, the only thing one handle should be concerned
1461		// about is the touches that originated on top of it.
1462		if ( touch ) {
1463
1464			// Returns true if a touch originated on the target.
1465			var isTouchOnTarget = function (checkTouch) {
1466				return checkTouch.target === eventTarget || eventTarget.contains(checkTouch.target);
1467			};
1468
1469			// In the case of touchstart events, we need to make sure there is still no more than one
1470			// touch on the target so we look amongst all touches.
1471			if (e.type === 'touchstart') {
1472
1473				var targetTouches = Array.prototype.filter.call(e.touches, isTouchOnTarget);
1474
vendor: 3,286 bytes, lines 1475-1583
1475				// Do not support more than one touch per handle.
1476				if ( targetTouches.length > 1 ) {
1477					return false;
1478				}
1479
1480				x = targetTouches[0].pageX;
1481				y = targetTouches[0].pageY;
1482
1483			} else {
1484
1485				// In the other cases, find on changedTouches is enough.
1486				var targetTouch = Array.prototype.find.call(e.changedTouches, isTouchOnTarget);
1487
1488				// Cancel if the target touch has not moved.
1489				if ( !targetTouch ) {
1490					return false;
1491				}
1492
1493				x = targetTouch.pageX;
1494				y = targetTouch.pageY;
1495			}
1496		}
1497
1498		pageOffset = pageOffset || getPageOffset(scope_Document);
1499
1500		if ( mouse || pointer ) {
1501			x = e.clientX + pageOffset.x;
1502			y = e.clientY + pageOffset.y;
1503		}
1504
1505		e.pageOffset = pageOffset;
1506		e.points = [x, y];
1507		e.cursor = mouse || pointer; // Fix #435
1508
1509		return e;
1510	}
1511
1512	// Translate a coordinate in the document to a percentage on the slider
1513	function calcPointToPercentage ( calcPoint ) {
1514		var location = calcPoint - offset(scope_Base, options.ort);
1515		var proposal = ( location * 100 ) / baseSize();
1516
1517		// Clamp proposal between 0% and 100%
1518		// Out-of-bound coordinates may occur when .noUi-base pseudo-elements
1519		// are used (e.g. contained handles feature)
1520		proposal = limit(proposal);
1521
1522		return options.dir ? 100 - proposal : proposal;
1523	}
1524
1525	// Find handle closest to a certain percentage on the slider
1526	function getClosestHandle ( proposal ) {
1527
1528		var closest = 100;
1529		var handleNumber = false;
1530
1531		scope_Handles.forEach(function(handle, index){
1532
1533			// Disabled handles are ignored
1534			if ( handle.hasAttribute('disabled') ) {
1535				return;
1536			}
1537
1538			var pos = Math.abs(scope_Locations[index] - proposal);
1539
1540			if ( pos < closest || (pos === 100 && closest === 100) ) {
1541				handleNumber = index;
1542				closest = pos;
1543			}
1544		});
1545
1546		return handleNumber;
1547	}
1548
1549	// Fire 'end' when a mouse or pen leaves the document.
1550	function documentLeave ( event, data ) {
1551		if ( event.type === "mouseout" && event.target.nodeName === "HTML" && event.relatedTarget === null ){
1552			eventEnd (event, data);
1553		}
1554	}
1555
1556	// Handle movement on document for handle and range drag.
1557	function eventMove ( event, data ) {
1558
1559		// Fix #498
1560		// Check value of .buttons in 'start' to work around a bug in IE10 mobile (data.buttonsProperty).
1561		// https://connect.microsoft.com/IE/feedback/details/927005/mobile-ie10-windows-phone-buttons-property-of-pointermove-event-always-zero
1562		// IE9 has .buttons and .which zero on mousemove.
1563		// Firefox breaks the spec MDN defines.
1564		if ( navigator.appVersion.indexOf("MSIE 9") === -1 && event.buttons === 0 && data.buttonsProperty !== 0 ) {
1565			return eventEnd(event, data);
1566		}
1567
1568		// Check if we are moving up or down
1569		var movement = (options.dir ? -1 : 1) * (event.calcPoint - data.startCalcPoint);
1570
1571		// Convert the movement into a percentage of the slider width/height
1572		var proposal = (movement * 100) / data.baseSize;
1573
1574		moveHandles(movement > 0, proposal, data.locations, data.handleNumbers);
1575	}
1576
1577	// Unbind move events on document, call callbacks.
1578	function eventEnd ( event, data ) {
1579
1580		// The handle is no longer active, so remove the class.
1581		if ( data.handle ) {
1582			removeClass(data.handle, options.cssClasses.active);
1583			scope_ActiveHandlesCount -= 1;
1584		}
1585
1586		// Unbind the move and end events, which are added on 'start'.
1587		data.listeners.forEach(function( c ) {
1588			scope_DocumentElement.removeEventListener(c[0], c[1]);
1589		});
1590
1591		if ( scope_ActiveHandlesCount === 0 ) {
1592			// Remove dragging class.
1593			removeClass(scope_Target, options.cssClasses.drag);
1594			setZindex();
1595
1596			// Remove cursor styles and text-selection events bound to the body.
1597			if ( event.cursor ) {
1598				scope_Body.style.cursor = '';
1599				scope_Body.removeEventListener('selectstart', preventDefault);
1600			}
1601		}
1602
1603		data.handleNumbers.forEach(function(handleNumber){
1604			fireEvent('change', handleNumber);
1605			fireEvent('set', handleNumber);
1606			fireEvent('end', handleNumber);
1607		});
1608	}
1609
1610	// Bind move events on document.
1611	function eventStart ( event, data ) {
1612
1613		var handle;
1614		if ( data.handleNumbers.length === 1 ) {
1615
1616			var handleOrigin = scope_Handles[data.handleNumbers[0]];
1617
1618			// Ignore 'disabled' handles
1619			if ( handleOrigin.hasAttribute('disabled') ) {
1620				return false;
1621			}
1622
1623			handle = handleOrigin.children[0];
1624			scope_ActiveHandlesCount += 1;
1625
1626			// Mark the handle as 'active' so it can be styled.
1627			addClass(handle, options.cssClasses.active);
1628		}
1629
1630		// A drag should never propagate up to the 'tap' event.
1631		event.stopPropagation();
1632
1633		// Record the event listeners.
1634		var listeners = [];
1635
1636		// Attach the move and end events.
1637		var moveEvent = attachEvent(actions.move, scope_DocumentElement, eventMove, {
1638			// The event target has changed so we need to propagate the original one so that we keep
1639			// relying on it to extract target touches.
1640			target: event.target,
1641			handle: handle,
1642			listeners: listeners,
1643			startCalcPoint: event.calcPoint,
1644			baseSize: baseSize(),
1645			pageOffset: event.pageOffset,
1646			handleNumbers: data.handleNumbers,
1647			buttonsProperty: event.buttons,
1648			locations: scope_Locations.slice()
1649		});
1650
1651		var endEvent = attachEvent(actions.end, scope_DocumentElement, eventEnd, {
1652			target: event.target,
1653			handle: handle,
1654			listeners: listeners,
1655			doNotReject: true,
1656			handleNumbers: data.handleNumbers
1657		});
1658
1659		var outEvent = attachEvent("mouseout", scope_DocumentElement, documentLeave, {
1660			target: event.target,
1661			handle: handle,
1662			listeners: listeners,
1663			doNotReject: true,
1664			handleNumbers: data.handleNumbers
1665		});
1666
1667		// We want to make sure we pushed the listeners in the listener list rather than creating
1668		// a new one as it has already been passed to the event handlers.
1669		listeners.push.apply(listeners, moveEvent.concat(endEvent, outEvent));
1670
1671		// Text selection isn't an issue on touch devices,
1672		// so adding cursor styles can be skipped.
1673		if ( event.cursor ) {
1674
1675			// Prevent the 'I' cursor and extend the range-drag cursor.
1676			scope_Body.style.cursor = getComputedStyle(event.target).cursor;
1677
1678			// Mark the target with a dragging state.
1679			if ( scope_Handles.length > 1 ) {
1680				addClass(scope_Target, options.cssClasses.drag);
1681			}
1682
1683			// Prevent text selection when dragging the handles.
1684			// In noUiSlider <= 9.2.0, this was handled by calling preventDefault on mouse/touch start/move,
1685			// which is scroll blocking. The selectstart event is supported by FireFox starting from version 52,
1686			// meaning the only holdout is iOS Safari. This doesn't matter: text selection isn't triggered there.
1687			// The 'cursor' flag is false.
1688			// See: http://caniuse.com/#search=selectstart
1689			scope_Body.addEventListener('selectstart', preventDefault, false);
1690		}
1691
1692		data.handleNumbers.forEach(function(handleNumber){
1693			fireEvent('start', handleNumber);
1694		});
1695	}
1696
1697	// Move closest handle to tapped location.
1698	function eventTap ( event ) {
1699
1700		// The tap event shouldn't propagate up
1701		event.stopPropagation();
1702
1703		var proposal = calcPointToPercentage(event.calcPoint);
1704		var handleNumber = getClosestHandle(proposal);
1705
1706		// Tackle the case that all handles are 'disabled'.
1707		if ( handleNumber === false ) {
1708			return false;
1709		}
1710
1711		// Flag the slider as it is now in a transitional state.
1712		// Transition takes a configurable amount of ms (default 300). Re-enable the slider after that.
1713		if ( !options.events.snap ) {
1714			addClassFor(scope_Target, options.cssClasses.tap, options.animationDuration);
1715		}
1716
1717		setHandle(handleNumber, proposal, true, true);
1718
1719		setZindex();
1720
1721		fireEvent('slide', handleNumber, true);
1722		fireEvent('update', handleNumber, true);
1723		fireEvent('change', handleNumber, true);
1724		fireEvent('set', handleNumber, true);
1725
1726		if ( options.events.snap ) {
1727			eventStart(event, { handleNumbers: [handleNumber] });
1728		}
1729	}
1730
1731	// Fires a 'hover' event for a hovered mouse/pen position.
1732	function eventHover ( event ) {
1733
1734		var proposal = calcPointToPercentage(event.calcPoint);
1735
1736		var to = scope_Spectrum.getStep(proposal);
1737		var value = scope_Spectrum.fromStepping(to);
1738
1739		Object.keys(scope_Events).forEach(function( targetEvent ) {
1740			if ( 'hover' === targetEvent.split('.')[0] ) {
1741				scope_Events[targetEvent].forEach(function( callback ) {
1742					callback.call( scope_Self, value );
1743				});
1744			}
1745		});
1746	}
1747
1748	// Attach events to several slider parts.
1749	function bindSliderEvents ( behaviour ) {
1750
1751		// Attach the standard drag event to the handles.
1752		if ( !behaviour.fixed ) {
1753
1754			scope_Handles.forEach(function( handle, index ){
1755
1756				// These events are only bound to the visual handle
1757				// element, not the 'real' origin element.
1758				attachEvent ( actions.start, handle.children[0], eventStart, {
1759					handleNumbers: [index]
1760				});
1761			});
1762		}
1763
1764		// Attach the tap event to the slider base.
1765		if ( behaviour.tap ) {
1766			attachEvent (actions.start, scope_Base, eventTap, {});
1767		}
1768
1769		// Fire hover events
1770		if ( behaviour.hover ) {
1771			attachEvent (actions.move, scope_Base, eventHover, { hover: true });
1772		}
1773
1774		// Make the range draggable.
1775		if ( behaviour.drag ){
1776
1777			scope_Connects.forEach(function( connect, index ){
1778
1779				if ( connect === false || index === 0 || index === scope_Connects.length - 1 ) {
1780					return;
1781				}
1782
1783				var handleBefore = scope_Handles[index - 1];
1784				var handleAfter = scope_Handles[index];
1785				var eventHolders = [connect];
1786
1787				addClass(connect, options.cssClasses.draggable);
1788
1789				// When the range is fixed, the entire range can
1790				// be dragged by the handles. The handle in the first
1791				// origin will propagate the start event upward,
1792				// but it needs to be bound manually on the other.
1793				if ( behaviour.fixed ) {
1794					eventHolders.push(handleBefore.children[0]);
1795					eventHolders.push(handleAfter.children[0]);
1796				}
1797
1798				eventHolders.forEach(function( eventHolder ) {
1799					attachEvent ( actions.start, eventHolder, eventStart, {
1800						handles: [handleBefore, handleAfter],
1801						handleNumbers: [index - 1, index]
1802					});
1803				});
1804			});
1805		}
1806	}
1807
1808/*! In this file: Slider events (not browser events); */
1809
1810	// Attach an event to this slider, possibly including a namespace
1811	function bindEvent ( namespacedEvent, callback ) {
1812		scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || [];
1813		scope_Events[namespacedEvent].push(callback);
1814
1815		// If the event bound is 'update,' fire it immediately for all handles.
1816		if ( namespacedEvent.split('.')[0] === 'update' ) {
1817			scope_Handles.forEach(function(a, index){
1818				fireEvent('update', index);
1819			});
1820		}
1821	}
1822
1823	// Undo attachment of event
1824	function removeEvent ( namespacedEvent ) {
1825
1826		var event = namespacedEvent && namespacedEvent.split('.')[0];
1827		var namespace = event && namespacedEvent.substring(event.length);
1828
1829		Object.keys(scope_Events).forEach(function( bind ){
1830
1831			var tEvent = bind.split('.')[0];
1832			var tNamespace = bind.substring(tEvent.length);
1833
1834			if ( (!event || event === tEvent) && (!namespace || namespace === tNamespace) ) {
1835				delete scope_Events[bind];
1836			}
1837		});
1838	}
1839
1840	// External event handling
1841	function fireEvent ( eventName, handleNumber, tap ) {
1842
1843		Object.keys(scope_Events).forEach(function( targetEvent ) {
1844
1845			var eventType = targetEvent.split('.')[0];
1846
1847			if ( eventName === eventType ) {
1848				scope_Events[targetEvent].forEach(function( callback ) {
1849
1850					callback.call(
1851						// Use the slider public API as the scope ('this')
1852						scope_Self,
1853						// Return values as array, so arg_1[arg_2] is always valid.
1854						scope_Values.map(options.format.to),
1855						// Handle index, 0 or 1
1856						handleNumber,
1857						// Unformatted slider values
1858						scope_Values.slice(),
1859						// Event is fired by tap, true or false
1860						tap || false,
1861						// Left offset of the handle, in relation to the slider
1862						scope_Locations.slice()
1863					);
1864				});
1865			}
1866		});
1867	}
1868
1869/*! In this file: Mechanics for slider operation */
1870
1871	function toPct ( pct ) {
1872		return pct + '%';
1873	}
1874
1875	// Split out the handle positioning logic so the Move event can use it, too
1876	function checkHandlePosition ( reference, handleNumber, to, lookBackward, lookForward, getValue ) {
1877
1878		// For sliders with multiple handles, limit movement to the other handle.
1879		// Apply the margin option by adding it to the handle positions.
1880		if ( scope_Handles.length > 1 ) {
1881
1882			if ( lookBackward && handleNumber > 0 ) {
1883				to = Math.max(to, reference[handleNumber - 1] + options.margin);
1884			}
1885
1886			if ( lookForward && handleNumber < scope_Handles.length - 1 ) {
1887				to = Math.min(to, reference[handleNumber + 1] - options.margin);
1888			}
1889		}
1890
1891		// The limit option has the opposite effect, limiting handles to a
1892		// maximum distance from another. Limit must be > 0, as otherwise
1893		// handles would be unmoveable.
1894		if ( scope_Handles.length > 1 && options.limit ) {
1895
1896			if ( lookBackward && handleNumber > 0 ) {
1897				to = Math.min(to, reference[handleNumber - 1] + options.limit);
1898			}
1899
1900			if ( lookForward && handleNumber < scope_Handles.length - 1 ) {
1901				to = Math.max(to, reference[handleNumber + 1] - options.limit);
1902			}
1903		}
1904
1905		// The padding option keeps the handles a certain distance from the
1906		// edges of the slider. Padding must be > 0.
1907		if ( options.padding ) {
1908
1909			if ( handleNumber === 0 ) {
1910				to = Math.max(to, options.padding[0]);
1911			}
1912
1913			if ( handleNumber === scope_Handles.length - 1 ) {
1914				to = Math.min(to, 100 - options.padding[1]);
1915			}
1916		}
1917
1918		to = scope_Spectrum.getStep(to);
1919
1920		// Limit percentage to the 0 - 100 range
1921		to = limit(to);
1922
1923		// Return false if handle can't move
1924		if ( to === reference[handleNumber] && !getValue ) {
1925			return false;
1926		}
1927
1928		return to;
1929	}
1930
1931	// Uses slider orientation to create CSS rules. a = base value;
1932	function inRuleOrder ( v, a ) {
1933		var o = options.ort;
1934		return (o?a:v) + ', ' + (o?v:a);
1935	}
1936
1937	// Moves handle(s) by a percentage
1938	// (bool, % to move, [% where handle started, ...], [index in scope_Handles, ...])
1939	function moveHandles ( upward, proposal, locations, handleNumbers ) {
1940
1941		var proposals = locations.slice();
1942
1943		var b = [!upward, upward];
1944		var f = [upward, !upward];
1945
1946		// Copy handleNumbers so we don't change the dataset
1947		handleNumbers = handleNumbers.slice();
1948
1949		// Check to see which handle is 'leading'.
1950		// If that one can't move the second can't either.
1951		if ( upward ) {
1952			handleNumbers.reverse();
1953		}
1954
1955		// Step 1: get the maximum percentage that any of the handles can move
1956		if ( handleNumbers.length > 1 ) {
1957
1958			handleNumbers.forEach(function(handleNumber, o) {
1959
1960				var to = checkHandlePosition(proposals, handleNumber, proposals[handleNumber] + proposal, b[o], f[o], false);
1961
1962				// Stop if one of the handles can't move.
1963				if ( to === false ) {
1964					proposal = 0;
1965				} else {
1966					proposal = to - proposals[handleNumber];
1967					proposals[handleNumber] = to;
1968				}
1969			});
1970		}
1971
1972		// If using one handle, check backward AND forward
1973		else {
1974			b = f = [true];
1975		}
1976
1977		var state = false;
1978
1979		// Step 2: Try to set the handles with the found percentage
1980		handleNumbers.forEach(function(handleNumber, o) {
1981			state = setHandle(handleNumber, locations[handleNumber] + proposal, b[o], f[o]) || state;
1982		});
1983
1984		// Step 3: If a handle moved, fire events
1985		if ( state ) {
1986			handleNumbers.forEach(function(handleNumber){
1987				fireEvent('update', handleNumber);
1988				fireEvent('slide', handleNumber);
1989			});
1990		}
1991	}
1992
1993	// Takes a base value and an offset. This offset is used for the connect bar size.
1994	// In the initial design for this feature, the origin element was 1% wide.
1995	// Unfortunately, a rounding bug in Chrome makes it impossible to implement this feature
1996	// in this manner: https://bugs.chromium.org/p/chromium/issues/detail?id=798223
1997	function transformDirection ( a, b ) {
1998		return options.dir ? 100 - a - b : a;
1999	}
2000
2001	// Updates scope_Locations and scope_Values, updates visual state
2002	function updateHandlePosition ( handleNumber, to ) {
2003
2004		// Update locations.
2005		scope_Locations[handleNumber] = to;
2006
2007		// Convert the value to the slider stepping/range.
2008		scope_Values[handleNumber] = scope_Spectrum.fromStepping(to);
2009
2010		var rule = 'translate(' + inRuleOrder(toPct(transformDirection(to, 0) - scope_DirOffset), '0') + ')';
2011		scope_Handles[handleNumber].style[options.transformRule] = rule;
2012
2013		updateConnect(handleNumber);
2014		updateConnect(handleNumber + 1);
2015	}
2016
2017	// Handles before the slider middle are stacked later = higher,
2018	// Handles after the middle later is lower
2019	// [[7] [8] .......... | .......... [5] [4]
2020	function setZindex ( ) {
2021
2022		scope_HandleNumbers.forEach(function(handleNumber){
2023			var dir = (scope_Locations[handleNumber] > 50 ? -1 : 1);
2024			var zIndex = 3 + (scope_Handles.length + (dir * handleNumber));
2025			scope_Handles[handleNumber].style.zIndex = zIndex;
2026		});
2027	}
2028
2029	// Test suggested values and apply margin, step.
2030	function setHandle ( handleNumber, to, lookBackward, lookForward ) {
2031
2032		to = checkHandlePosition(scope_Locations, handleNumber, to, lookBackward, lookForward, false);
2033
2034		if ( to === false ) {
2035			return false;
2036		}
2037
2038		updateHandlePosition(handleNumber, to);
2039
2040		return true;
2041	}
2042
2043	// Updates style attribute for connect nodes
2044	function updateConnect ( index ) {
2045
2046		// Skip connects set to false
2047		if ( !scope_Connects[index] ) {
2048			return;
2049		}
2050
2051		var l = 0;
2052		var h = 100;
2053
2054		if ( index !== 0 ) {
2055			l = scope_Locations[index - 1];
2056		}
2057
2058		if ( index !== scope_Connects.length - 1 ) {
2059			h = scope_Locations[index];
2060		}
2061
2062		// We use two rules:
2063		// 'translate' to change the left/top offset;
2064		// 'scale' to change the width of the element;
2065		// As the element has a width of 100%, a translation of 100% is equal to 100% of the parent (.noUi-base)
2066		var connectWidth = h - l;
2067		var translateRule = 'translate(' + inRuleOrder(toPct(transformDirection(l, connectWidth)), '0') + ')';
2068		var scaleRule = 'scale(' + inRuleOrder(connectWidth / 100, '1') + ')';
2069
2070		scope_Connects[index].style[options.transformRule] = translateRule + ' ' + scaleRule;
2071	}
2072
2073/*! In this file: All methods eventually exposed in slider.noUiSlider... */
2074
2075	// Parses value passed to .set method. Returns current value if not parse-able.
2076	function resolveToValue ( to, handleNumber ) {
2077
2078		// Setting with null indicates an 'ignore'.
2079		// Inputting 'false' is invalid.
2080		if ( to === null || to === false || to === undefined ) {
2081			return scope_Locations[handleNumber];
2082		}
2083
2084		// If a formatted number was passed, attempt to decode it.
2085		if ( typeof to === 'number' ) {
2086			to = String(to);
2087		}
2088
2089		to = options.format.from(to);
2090		to = scope_Spectrum.toStepping(to);
2091
2092		// If parsing the number failed, use the current value.
2093		if ( to === false || isNaN(to) ) {
2094			return scope_Locations[handleNumber];
2095		}
2096
2097		return to;
2098	}
2099
2100	// Set the slider value.
2101	function valueSet ( input, fireSetEvent ) {
2102
2103		var values = asArray(input);
2104		var isInit = scope_Locations[0] === undefined;
2105
2106		// Event fires by default
2107		fireSetEvent = (fireSetEvent === undefined ? true : !!fireSetEvent);
2108
2109		// Animation is optional.
2110		// Make sure the initial values were set before using animated placement.
2111		if ( options.animate && !isInit ) {
2112			addClassFor(scope_Target, options.cssClasses.tap, options.animationDuration);
2113		}
2114
2115		// First pass, without lookAhead but with lookBackward. Values are set from left to right.
2116		scope_HandleNumbers.forEach(function(handleNumber){
2117			setHandle(handleNumber, resolveToValue(values[handleNumber], handleNumber), true, false);
2118		});
2119
2120		// Second pass. Now that all base values are set, apply constraints
2121		scope_HandleNumbers.forEach(function(handleNumber){
2122			setHandle(handleNumber, scope_Locations[handleNumber], true, true);
2123		});
2124
2125		setZindex();
2126
2127		scope_HandleNumbers.forEach(function(handleNumber){
2128
2129			fireEvent('update', handleNumber);
2130
2131			// Fire the event only for handles that received a new value, as per #579
2132			if ( values[handleNumber] !== null && fireSetEvent ) {
2133				fireEvent('set', handleNumber);
2134			}
2135		});
2136	}
2137
2138	// Reset slider to initial values
2139	function valueReset ( fireSetEvent ) {
2140		valueSet(options.start, fireSetEvent);
2141	}
2142
2143	// Get the slider value.
2144	function valueGet ( ) {
2145
2146		var values = scope_Values.map(options.format.to);
2147
2148		// If only one handle is used, return a single value.
2149		if ( values.length === 1 ){
2150			return values[0];
2151		}
2152
2153		return values;
2154	}
2155
2156	// Removes classes from the root and empties it.
2157	function destroy ( ) {
2158
2159		for ( var key in options.cssClasses ) {
2160			if ( !options.cssClasses.hasOwnProperty(key) ) { continue; }
2161			removeClass(scope_Target, options.cssClasses[key]);
2162		}
2163
2164		while (scope_Target.firstChild) {
2165			scope_Target.removeChild(scope_Target.firstChild);
2166		}
2167
2168		delete scope_Target.noUiSlider;
2169	}
2170
2171	// Get the current step size for the slider.
2172	function getCurrentStep ( ) {
2173
2174		// Check all locations, map them to their stepping point.
2175		// Get the step point, then find it in the input list.
2176		return scope_Locations.map(function( location, index ){
2177
2178			var nearbySteps = scope_Spectrum.getNearbySteps( location );
2179			var value = scope_Values[index];
2180			var increment = nearbySteps.thisStep.step;
2181			var decrement = null;
2182
2183			// If the next value in this step moves into the next step,
2184			// the increment is the start of the next step - the current value
2185			if ( increment !== false ) {
2186				if ( value + increment > nearbySteps.stepAfter.startValue ) {
2187					increment = nearbySteps.stepAfter.startValue - value;
2188				}
2189			}
2190
2191
2192			// If the value is beyond the starting point
2193			if ( value > nearbySteps.thisStep.startValue ) {
2194				decrement = nearbySteps.thisStep.step;
2195			}
2196
2197			else if ( nearbySteps.stepBefore.step === false ) {
2198				decrement = false;
2199			}
2200
2201			// If a handle is at the start of a step, it always steps back into the previous step first
2202			else {
2203				decrement = value - nearbySteps.stepBefore.highestStep;
2204			}
2205
2206
2207			// Now, if at the slider edges, there is not in/decrement
2208			if ( location === 100 ) {
2209				increment = null;
2210			}
2211
2212			else if ( location === 0 ) {
2213				decrement = null;
2214			}
2215
2216			// As per #391, the comparison for the decrement step can have some rounding issues.
2217			var stepDecimals = scope_Spectrum.countStepDecimals();
2218
2219			// Round per #391
2220			if ( increment !== null && increment !== false ) {
2221				increment = Number(increment.toFixed(stepDecimals));
2222			}
2223
2224			if ( decrement !== null && decrement !== false ) {
2225				decrement = Number(decrement.toFixed(stepDecimals));
2226			}
2227
2228			return [decrement, increment];
2229		});
2230	}
2231
2232	// Updateable: margin, limit, padding, step, range, animate, snap
2233	function updateOptions ( optionsToUpdate, fireSetEvent ) {
2234
2235		// Spectrum is created using the range, snap, direction and step options.
2236		// 'snap' and 'step' can be updated.
2237		// If 'snap' and 'step' are not passed, they should remain unchanged.
2238		var v = valueGet();
2239
2240		var updateAble = ['margin', 'limit', 'padding', 'range', 'animate', 'snap', 'step', 'format'];
2241
2242		// Only change options that we're actually passed to update.
2243		updateAble.forEach(function(name){
2244			if ( optionsToUpdate[name] !== undefined ) {
2245				originalOptions[name] = optionsToUpdate[name];
2246			}
2247		});
2248
2249		var newOptions = testOptions(originalOptions);
2250
2251		// Load new options into the slider state
2252		updateAble.forEach(function(name){
2253			if ( optionsToUpdate[name] !== undefined ) {
2254				options[name] = newOptions[name];
2255			}
2256		});
2257
2258		scope_Spectrum = newOptions.spectrum;
2259
2260		// Limit, margin and padding depend on the spectrum but are stored outside of it. (#677)
2261		options.margin = newOptions.margin;
2262		options.limit = newOptions.limit;
2263		options.padding = newOptions.padding;
2264
2265		// Update pips, removes existing.
2266		if ( options.pips ) {
2267			pips(options.pips);
2268		}
2269
2270		// Invalidate the current positioning so valueSet forces an update.
2271		scope_Locations = [];
2272		valueSet(optionsToUpdate.start || v, fireSetEvent);
2273	}
2274
2275/*! In this file: Calls to functions. All other scope_ files define functions only; */
2276
2277	// Create the base element, initialize HTML and set classes.
2278	// Add handles and connect elements.
2279	addSlider(scope_Target);
2280	addElements(options.connect, scope_Base);
2281
2282	// Attach user events.
2283	bindSliderEvents(options.events);
2284
2285	// Use the public value method to set the start values.
2286	valueSet(options.start);
2287
2288	scope_Self = {
2289		destroy: destroy,
2290		steps: getCurrentStep,
2291		on: bindEvent,
2292		off: removeEvent,
2293		get: valueGet,
2294		set: valueSet,
2295		reset: valueReset,
2296		// Exposed for unit testing, don't use this in your application.
2297		__moveHandles: function(a, b, c) { moveHandles(a, b, scope_Locations, c); },
2298		options: originalOptions, // Issue #600, #678
2299		updateOptions: updateOptions,
2300		target: scope_Target, // Issue #597
2301		removePips: removePips,
2302		pips: pips // Issue #594
2303	};
2304
2305	if ( options.pips ) {
2306		pips(options.pips);
2307	}
2308
2309	if ( options.tooltips ) {
2310		tooltips();
2311	}
2312
2313	aria();
2314
2315	return scope_Self;
2316
2317}
2318
2319
2320	// Run the standard initializer
2321	function initialize ( target, originalOptions ) {
2322
2323		if ( !target || !target.nodeName ) {
2324			throw new Error("noUiSlider (" + VERSION + "): create requires a single element, got: " + target);
2325		}
2326
2327		// Throw an error if the slider was already initialized.
2328		if ( target.noUiSlider ) {
2329			throw new Error("noUiSlider (" + VERSION + "): Slider was already initialized.");
2330		}
2331
2332		// Test the options and create the slider environment;
2333		var options = testOptions( originalOptions, target );
2334		var api = scope( target, options, originalOptions );
2335
2336		target.noUiSlider = api;
2337
2338		return api;
2339	}
2340
2341	// Use an object instead of a function for future expandability;
2342	return {
2343		version: VERSION,
2344		create: initialize
2345	};
2346
2347}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.