PageSourceSearch

https://www.gruppopaker.it/wp-content/themes/suprek/assets/js/vendors-libs/nouislider.js?ver=8.5.1

js gruppopaker.it collected 2026-10-10 00:04:07 UTC 55,242 bytes, 1,959 lines download raw bytes

1/*! nouislider - 8.5.1 - 2016-04-24 16:00:29 */
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
26	// Removes duplicates from an array.
27	function unique(array) {
28		return array.filter(function(a){
29			return !this[a] ? this[a] = true : false;
30		}, {});
31	}
32
33	// Round a value to the closest 'to'.
34	function closest ( value, to ) {
35		return Math.round(value / to) * to;
36	}
37
38	// Current position of an element relative to the document.
39	function offset ( elem ) {
40
41	var rect = elem.getBoundingClientRect(),
42		doc = elem.ownerDocument,
43		docElem = doc.documentElement,
44		pageOffset = getPageOffset();
45
46		// getBoundingClientRect contains left scroll in Chrome on Android.
47		// I haven't found a feature detection that proves this. Worst case
48		// scenario on mis-match: the 'tap' feature on horizontal sliders breaks.
49		if ( /webkit.*Chrome.*Mobile/i.test(navigator.userAgent) ) {
50			pageOffset.x = 0;
51		}
52
53		return {
54			top: rect.top + pageOffset.y - docElem.clientTop,
55			left: rect.left + pageOffset.x - docElem.clientLeft
56		};
57	}
58
59	// Checks whether a value is numerical.
60	function isNumeric ( a ) {
61		return typeof a === 'number' && !isNaN( a ) && isFinite( a );
62	}
63
64	// Sets a class and removes it after [duration] ms.
65	function addClassFor ( element, className, duration ) {
66		addClass(element, className);
67		setTimeout(function(){
68			removeClass(element, className);
69		}, duration);
70	}
71
72	// Limits a value to 0 - 100
73	function limit ( a ) {
74		return Math.max(Math.min(a, 100), 0);
75	}
76
77	// Wraps a variable as an array, if it isn't one yet.
78	function asArray ( a ) {
79		return Array.isArray(a) ? a : [a];
80	}
81
82	// Counts decimals
83	function countDecimals ( numStr ) {
84		var pieces = numStr.split(".");
85		return pieces.length > 1 ? pieces[1].length : 0;
86	}
87
88	// http://youmightnotneedjquery.com/#add_class
89	function addClass ( el, className ) {
90		if ( el.classList ) {
91			el.classList.add(className);
92		} else {
93			el.className += ' ' + className;
94		}
95	}
96
97	// http://youmightnotneedjquery.com/#remove_class
98	function removeClass ( el, className ) {
99		if ( el.classList ) {
100			el.classList.remove(className);
101		} else {
102			el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
103		}
104	}
105
106	// https://plainjs.com/javascript/attributes/adding-removing-and-testing-for-classes-9/
107	function hasClass ( el, className ) {
108		return el.classList ? el.classList.contains(className) : new RegExp('\\b' + className + '\\b').test(el.className);
109	}
110
111	// https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes
112	function getPageOffset ( ) {
113
114		var supportPageOffset = window.pageXOffset !== undefined,
115			isCSS1Compat = ((document.compatMode || "") === "CSS1Compat"),
116			x = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft,
117			y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;
118
119		return {
120			x: x,
121			y: y
122		};
123	}
124
125	// we provide a function to compute constants instead
126	// of accessing window.* as soon as the module needs it
127	// so that we do not compute anything if not needed
128	function getActions ( ) {
129
130		// Determine the events to bind. IE11 implements pointerEvents without
131		// a prefix, which breaks compatibility with the IE10 implementation.
132		return window.navigator.pointerEnabled ? {
133			start: 'pointerdown',
134			move: 'pointermove',
135			end: 'pointerup'
136		} : window.navigator.msPointerEnabled ? {
137			start: 'MSPointerDown',
138			move: 'MSPointerMove',
139			end: 'MSPointerUp'
140		} : {
141			start: 'mousedown touchstart',
142			move: 'mousemove touchmove',
143			end: 'mouseup touchend'
144		};
145	}
146
147
148// Value calculation
149
150	// Determine the size of a sub-range in relation to a full range.
151	function subRangeRatio ( pa, pb ) {
152		return (100 / (pb - pa));
153	}
154
155	// (percentage) How many percent is this value of this range?
156	function fromPercentage ( range, value ) {
157		return (value * 100) / ( range[1] - range[0] );
158	}
159
160	// (percentage) Where is this value on this range?
161	function toPercentage ( range, value ) {
162		return fromPercentage( range, range[0] < 0 ?
163			value + Math.abs(range[0]) :
164				value - range[0] );
165	}
166
167	// (value) How much is this percentage on this range?
168	function isPercentage ( range, value ) {
169		return ((value * ( range[1] - range[0] )) / 100) + range[0];
170	}
171
172
173// Range conversion
174
175	function getJ ( value, arr ) {
176
177		var j = 1;
178
179		while ( value >= arr[j] ){
180			j += 1;
181		}
182
183		return j;
184	}
185
186	// (percentage) Input a value, find where, on a scale of 0-100, it applies.
187	function toStepping ( xVal, xPct, value ) {
188
189		if ( value >= xVal.slice(-1)[0] ){
190			return 100;
191		}
192
193		var j = getJ( value, xVal ), va, vb, pa, pb;
194
195		va = xVal[j-1];
196		vb = xVal[j];
197		pa = xPct[j-1];
198		pb = xPct[j];
199
200		return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb));
201	}
202
203	// (value) Input a percentage, find where it is on the specified range.
204	function fromStepping ( xVal, xPct, value ) {
205
206		// There is no range group that fits 100
207		if ( value >= 100 ){
208			return xVal.slice(-1)[0];
209		}
210
211		var j = getJ( value, xPct ), va, vb, pa, pb;
212
213		va = xVal[j-1];
214		vb = xVal[j];
215		pa = xPct[j-1];
216		pb = xPct[j];
217
218		return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb));
219	}
220
221	// (percentage) Get the step that applies at a certain value.
222	function getStep ( xPct, xSteps, snap, value ) {
223
224		if ( value === 100 ) {
225			return value;
226		}
227
228		var j = getJ( value, xPct ), a, b;
229
230		// If 'snap' is set, steps are used as fixed points on the slider.
231		if ( snap ) {
232
233			a = xPct[j-1];
234			b = xPct[j];
235
236			// Find the closest position, a or b.
237			if ((value - a) > ((b-a)/2)){
238				return b;
239			}
240
241			return a;
242		}
243
244		if ( !xSteps[j-1] ){
245			return value;
246		}
247
248		return xPct[j-1] + closest(
249			value - xPct[j-1],
250			xSteps[j-1]
251		);
252	}
253
254
255// Entry parsing
256
257	function handleEntryPoint ( index, value, that ) {
258
259		var percentage;
260
261		// Wrap numerical input in an array.
262		if ( typeof value === "number" ) {
263			value = [value];
264		}
265
266		// Reject any invalid input, by testing whether value is an array.
267		if ( Object.prototype.toString.call( value ) !== '[object Array]' ){
268			throw new Error("noUiSlider: 'range' contains invalid value.");
269		}
270
271		// Covert min/max syntax to 0 and 100.
272		if ( index === 'min' ) {
273			percentage = 0;
274		} else if ( index === 'max' ) {
275			percentage = 100;
276		} else {
277			percentage = parseFloat( index );
278		}
279
280		// Check for correct input.
281		if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) {
282			throw new Error("noUiSlider: 'range' value isn't numeric.");
283		}
284
285		// Store values.
286		that.xPct.push( percentage );
287		that.xVal.push( value[0] );
288
289		// NaN will evaluate to false too, but to keep
290		// logging clear, set step explicitly. Make sure
291		// not to override the 'step' setting with false.
292		if ( !percentage ) {
293			if ( !isNaN( value[1] ) ) {
294				that.xSteps[0] = value[1];
295			}
296		} else {
297			that.xSteps.push( isNaN(value[1]) ? false : value[1] );
298		}
299	}
300
301	function handleStepPoint ( i, n, that ) {
302
303		// Ignore 'false' stepping.
304		if ( !n ) {
305			return true;
306		}
307
308		// Factor to range ratio
309		that.xSteps[i] = fromPercentage([
310			 that.xVal[i]
311			,that.xVal[i+1]
312		], n) / subRangeRatio (
313			that.xPct[i],
314			that.xPct[i+1] );
315	}
316
317
318// Interface
319
320	// The interface to Spectrum handles all direction-based
321	// conversions, so the above values are unaware.
322
323	function Spectrum ( entry, snap, direction, singleStep ) {
324
325		this.xPct = [];
326		this.xVal = [];
327		this.xSteps = [ singleStep || false ];
328		this.xNumSteps = [ false ];
329
330		this.snap = snap;
331		this.direction = direction;
332
333		var index, ordered = [ /* [0, 'min'], [1, '50%'], [2, 'max'] */ ];
334
335		// Map the object keys to an array.
336		for ( index in entry ) {
337			if ( entry.hasOwnProperty(index) ) {
338				ordered.push([entry[index], index]);
339			}
340		}
341
342		// Sort all entries by value (numeric sort).
343		if ( ordered.length && typeof ordered[0][0] === "object" ) {
344			ordered.sort(function(a, b) { return a[0][0] - b[0][0]; });
345		} else {
346			ordered.sort(function(a, b) { return a[0] - b[0]; });
347		}
348
349
350		// Convert all entries to subranges.
351		for ( index = 0; index < ordered.length; index++ ) {
352			handleEntryPoint(ordered[index][1], ordered[index][0], this);
353		}
354
355		// Store the actual step values.
356		// xSteps is sorted in the same order as xPct and xVal.
357		this.xNumSteps = this.xSteps.slice(0);
358
359		// Convert all numeric steps to the percentage of the subrange they represent.
360		for ( index = 0; index < this.xNumSteps.length; index++ ) {
361			handleStepPoint(index, this.xNumSteps[index], this);
362		}
363	}
364
365	Spectrum.prototype.getMargin = function ( value ) {
366		return this.xPct.length === 2 ? fromPercentage(this.xVal, value) : false;
367	};
368
369	Spectrum.prototype.toStepping = function ( value ) {
370
371		value = toStepping( this.xVal, this.xPct, value );
372
373		// Invert the value if this is a right-to-left slider.
374		if ( this.direction ) {
375			value = 100 - value;
376		}
377
378		return value;
379	};
380
381	Spectrum.prototype.fromStepping = function ( value ) {
382
383		// Invert the value if this is a right-to-left slider.
384		if ( this.direction ) {
385			value = 100 - value;
386		}
387
388		return fromStepping( this.xVal, this.xPct, value );
389	};
390
391	Spectrum.prototype.getStep = function ( value ) {
392
393		// Find the proper step for rtl sliders by search in inverse direction.
394		// Fixes issue #262.
395		if ( this.direction ) {
396			value = 100 - value;
397		}
398
399		value = getStep(this.xPct, this.xSteps, this.snap, value );
400
401		if ( this.direction ) {
402			value = 100 - value;
403		}
404
405		return value;
406	};
407
408	Spectrum.prototype.getApplicableStep = function ( value ) {
409
410		// If the value is 100%, return the negative step twice.
411		var j = getJ(value, this.xPct), offset = value === 100 ? 2 : 1;
412		return [this.xNumSteps[j-2], this.xVal[j-offset], this.xNumSteps[j-offset]];
413	};
414
415	// Outside testing
416	Spectrum.prototype.convert = function ( value ) {
417		return this.getStep(this.toStepping(value));
418	};
419
420/*	Every input option is tested and parsed. This'll prevent
421	endless validation in internal methods. These tests are
422	structured with an item for every option available. An
423	option can be marked as required by setting the 'r' flag.
424	The testing function is provided with three arguments:
425		- The provided value for the option;
426		- A reference to the options object;
427		- The name for the option;
428
429	The testing function returns false when an error is detected,
430	or true when everything is OK. It can also modify the option
431	object, to make sure all values can be correctly looped elsewhere. */
432
433	var defaultFormatter = { 'to': function( value ){
434		return value !== undefined && value.toFixed(2);
435	}, 'from': Number };
436
437	function testStep ( parsed, entry ) {
438
439		if ( !isNumeric( entry ) ) {
440			throw new Error("noUiSlider: 'step' is not numeric.");
441		}
442
443		// The step option can still be used to set stepping
444		// for linear sliders. Overwritten if set in 'range'.
445		parsed.singleStep = entry;
446	}
447
448	function testRange ( parsed, entry ) {
449
450		// Filter incorrect input.
451		if ( typeof entry !== 'object' || Array.isArray(entry) ) {
452			throw new Error("noUiSlider: 'range' is not an object.");
453		}
454
455		// Catch missing start or end.
456		if ( entry.min === undefined || entry.max === undefined ) {
457			throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'.");
458		}
459
460		// Catch equal start or end.
461		if ( entry.min === entry.max ) {
462			throw new Error("noUiSlider: 'range' 'min' and 'max' cannot be equal.");
463		}
464
465		parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep);
466	}
467
468	function testStart ( parsed, entry ) {
469
470		entry = asArray(entry);
471
472		// Validate input. Values aren't tested, as the public .val method
473		// will always provide a valid location.
474		if ( !Array.isArray( entry ) || !entry.length || entry.length > 2 ) {
475			throw new Error("noUiSlider: 'start' option is incorrect.");
476		}
477
478		// Store the number of handles.
479		parsed.handles = entry.length;
480
481		// When the slider is initialized, the .val method will
482		// be called with the start options.
483		parsed.start = entry;
484	}
485
486	function testSnap ( parsed, entry ) {
487
488		// Enforce 100% stepping within subranges.
489		parsed.snap = entry;
490
491		if ( typeof entry !== 'boolean' ){
492			throw new Error("noUiSlider: 'snap' option must be a boolean.");
493		}
494	}
495
496	function testAnimate ( parsed, entry ) {
497
498		// Enforce 100% stepping within subranges.
499		parsed.animate = entry;
500
501		if ( typeof entry !== 'boolean' ){
502			throw new Error("noUiSlider: 'animate' option must be a boolean.");
503		}
504	}
505
506	function testAnimationDuration ( parsed, entry ) {
507
508		parsed.animationDuration = entry;
509
510		if ( typeof entry !== 'number' ){
511			throw new Error("noUiSlider: 'animationDuration' option must be a number.");
512		}
513	}
514
515	function testConnect ( parsed, entry ) {
516
517		if ( entry === 'lower' && parsed.handles === 1 ) {
518			parsed.connect = 1;
519		} else if ( entry === 'upper' && parsed.handles === 1 ) {
520			parsed.connect = 2;
521		} else if ( entry === true && parsed.handles === 2 ) {
522			parsed.connect = 3;
523		} else if ( entry === false ) {
524			parsed.connect = 0;
525		} else {
526			throw new Error("noUiSlider: 'connect' option doesn't match handle count.");
527		}
528	}
529
530	function testOrientation ( parsed, entry ) {
531
532		// Set orientation to an a numerical value for easy
533		// array selection.
534		switch ( entry ){
535		  case 'horizontal':
536			parsed.ort = 0;
537			break;
538		  case 'vertical':
539			parsed.ort = 1;
540			break;
541		  default:
542			throw new Error("noUiSlider: 'orientation' option is invalid.");
543		}
544	}
545
546	function testMargin ( parsed, entry ) {
547
548		if ( !isNumeric(entry) ){
549			throw new Error("noUiSlider: 'margin' option must be numeric.");
550		}
551
552		// Issue #582
553		if ( entry === 0 ) {
554			return;
555		}
556
557		parsed.margin = parsed.spectrum.getMargin(entry);
558
559		if ( !parsed.margin ) {
560			throw new Error("noUiSlider: 'margin' option is only supported on linear sliders.");
561		}
562	}
563
564	function testLimit ( parsed, entry ) {
565
566		if ( !isNumeric(entry) ){
567			throw new Error("noUiSlider: 'limit' option must be numeric.");
568		}
569
570		parsed.limit = parsed.spectrum.getMargin(entry);
571
572		if ( !parsed.limit ) {
573			throw new Error("noUiSlider: 'limit' option is only supported on linear sliders.");
574		}
575	}
576
577	function testDirection ( parsed, entry ) {
578
579		// Set direction as a numerical value for easy parsing.
580		// Invert connection for RTL sliders, so that the proper
581		// handles get the connect/background classes.
582		switch ( entry ) {
583		  case 'ltr':
584			parsed.dir = 0;
585			break;
586		  case 'rtl':
587			parsed.dir = 1;
588			parsed.connect = [0,2,1,3][parsed.connect];
589			break;
590		  default:
591			throw new Error("noUiSlider: 'direction' option was not recognized.");
592		}
593	}
594
595	function testBehaviour ( parsed, entry ) {
596
597		// Make sure the input is a string.
598		if ( typeof entry !== 'string' ) {
599			throw new Error("noUiSlider: 'behaviour' must be a string containing options.");
600		}
601
602		// Check if the string contains any keywords.
603		// None are required.
604		var tap = entry.indexOf('tap') >= 0,
605			drag = entry.indexOf('drag') >= 0,
606			fixed = entry.indexOf('fixed') >= 0,
607			snap = entry.indexOf('snap') >= 0,
608			hover = entry.indexOf('hover') >= 0;
609
610		// Fix #472
611		if ( drag && !parsed.connect ) {
612			throw new Error("noUiSlider: 'drag' behaviour must be used with 'connect': true.");
613		}
614
615		parsed.events = {
616			tap: tap || snap,
617			drag: drag,
618			fixed: fixed,
619			snap: snap,
620			hover: hover
621		};
622	}
623
624	function testTooltips ( parsed, entry ) {
625
626		var i;
627
628		if ( entry === false ) {
629			return;
630		} else if ( entry === true ) {
631
632			parsed.tooltips = [];
633
634			for ( i = 0; i < parsed.handles; i++ ) {
635				parsed.tooltips.push(true);
636			}
637
638		} else {
639
640			parsed.tooltips = asArray(entry);
641
642			if ( parsed.tooltips.length !== parsed.handles ) {
643				throw new Error("noUiSlider: must pass a formatter for all handles.");
644			}
645
646			parsed.tooltips.forEach(function(formatter){
647				if ( typeof formatter !== 'boolean' && (typeof formatter !== 'object' || typeof formatter.to !== 'function') ) {
648					throw new Error("noUiSlider: 'tooltips' must be passed a formatter or 'false'.");
649				}
650			});
651		}
652	}
653
654	function testFormat ( parsed, entry ) {
655
656		parsed.format = entry;
657
658		// Any object with a to and from method is supported.
659		if ( typeof entry.to === 'function' && typeof entry.from === 'function' ) {
660			return true;
661		}
662
663		throw new Error("noUiSlider: 'format' requires 'to' and 'from' methods.");
664	}
665
666	function testCssPrefix ( parsed, entry ) {
667
668		if ( entry !== undefined && typeof entry !== 'string' && entry !== false ) {
669			throw new Error("noUiSlider: 'cssPrefix' must be a string or `false`.");
670		}
671
672		parsed.cssPrefix = entry;
673	}
674
675	function testCssClasses ( parsed, entry ) {
676
677		if ( entry !== undefined && typeof entry !== 'object' ) {
678			throw new Error("noUiSlider: 'cssClasses' must be an object.");
679		}
680
681		if ( typeof parsed.cssPrefix === 'string' ) {
682			parsed.cssClasses = {};
683
684			for ( var key in entry ) {
685				if ( !entry.hasOwnProperty(key) ) { continue; }
686
687				parsed.cssClasses[key] = parsed.cssPrefix + entry[key];
688			}
689		} else {
690			parsed.cssClasses = entry;
691		}
692	}
693
694	// Test all developer settings and parse to assumption-safe values.
695	function testOptions ( options ) {
696
697		// To prove a fix for #537, freeze options here.
698		// If the object is modified, an error will be thrown.
699		// Object.freeze(options);
700
701		var parsed = {
702			margin: 0,
703			limit: 0,
704			animate: true,
705			animationDuration: 300,
706			format: defaultFormatter
707		}, tests;
708
709		// Tests are executed in the order they are presented here.
710		tests = {
711			'step': { r: false, t: testStep },
712			'start': { r: true, t: testStart },
713			'connect': { r: true, t: testConnect },
714			'direction': { r: true, t: testDirection },
715			'snap': { r: false, t: testSnap },
716			'animate': { r: false, t: testAnimate },
717			'animationDuration': { r: false, t: testAnimationDuration },
718			'range': { r: true, t: testRange },
719			'orientation': { r: false, t: testOrientation },
720			'margin': { r: false, t: testMargin },
721			'limit': { r: false, t: testLimit },
722			'behaviour': { r: true, t: testBehaviour },
723			'format': { r: false, t: testFormat },
724			'tooltips': { r: false, t: testTooltips },
725			'cssPrefix': { r: false, t: testCssPrefix },
726			'cssClasses': { r: false, t: testCssClasses }
727		};
728
729		var defaults = {
730			'connect': false,
731			'direction': 'ltr',
732			'behaviour': 'tap',
733			'orientation': 'horizontal',
734			'cssPrefix' : 'noUi-',
735			'cssClasses': {
736				target: 'target',
737				base: 'base',
738				origin: 'origin',
739				handle: 'handle',
740				handleLower: 'handle-lower',
741				handleUpper: 'handle-upper',
742				horizontal: 'horizontal',
743				vertical: 'vertical',
744				background: 'background',
745				connect: 'connect',
746				ltr: 'ltr',
747				rtl: 'rtl',
748				draggable: 'draggable',
749				drag: 'state-drag',
750				tap: 'state-tap',
751				active: 'active',
752				stacking: 'stacking',
753				tooltip: 'tooltip',
754				pips: 'pips',
755				pipsHorizontal: 'pips-horizontal',
756				pipsVertical: 'pips-vertical',
757				marker: 'marker',
758				markerHorizontal: 'marker-horizontal',
759				markerVertical: 'marker-vertical',
760				markerNormal: 'marker-normal',
761				markerLarge: 'marker-large',
762				markerSub: 'marker-sub',
763				value: 'value',
764				valueHorizontal: 'value-horizontal',
765				valueVertical: 'value-vertical',
766				valueNormal: 'value-normal',
767				valueLarge: 'value-large',
768				valueSub: 'value-sub'
769			}
770		};
771
772		// Run all options through a testing mechanism to ensure correct
773		// input. It should be noted that options might get modified to
774		// be handled properly. E.g. wrapping integers in arrays.
775		Object.keys(tests).forEach(function( name ){
776
777			// If the option isn't set, but it is required, throw an error.
778			if ( options[name] === undefined && defaults[name] === undefined ) {
779
780				if ( tests[name].r ) {
781					throw new Error("noUiSlider: '" + name + "' is required.");
782				}
783
784				return true;
785			}
786
787			tests[name].t( parsed, options[name] === undefined ? defaults[name] : options[name] );
788		});
789
790		// Forward pips options
791		parsed.pips = options.pips;
792
793		// Pre-define the styles.
794		parsed.style = parsed.ort ? 'top' : 'left';
795
796		return parsed;
797	}
798
799
800function closure ( target, options, originalOptions ){
801	var
802		actions = getActions( ),
803		// All variables local to 'closure' are prefixed with 'scope_'
804		scope_Target = target,
805		scope_Locations = [-1, -1],
806		scope_Base,
807		scope_Handles,
808		scope_Spectrum = options.spectrum,
809		scope_Values = [],
810		scope_Events = {},
811		scope_Self;
812
813
814	// Delimit proposed values for handle positions.
815	function getPositions ( a, b, delimit ) {
816
817		// Add movement to current position.
818		var c = a + b[0], d = a + b[1];
819
820		// Only alter the other position on drag,
821		// not on standard sliding.
822		if ( delimit ) {
823			if ( c < 0 ) {
824				d += Math.abs(c);
825			}
826			if ( d > 100 ) {
827				c -= ( d - 100 );
828			}
829
830			// Limit values to 0 and 100.
831			return [limit(c), limit(d)];
832		}
833
834		return [c,d];
835	}
836
837	// Provide a clean event with standardized offset values.
838	function fixEvent ( e, pageOffset ) {
839
840		// Prevent scrolling and panning on touch events, while
841		// attempting to slide. The tap event also depends on this.
842		e.preventDefault();
843
844		// Filter the event to register the type, which can be
845		// touch, mouse or pointer. Offset changes need to be
846		// made on an event specific basis.
847		var touch = e.type.indexOf('touch') === 0,
848			mouse = e.type.indexOf('mouse') === 0,
849			pointer = e.type.indexOf('pointer') === 0,
850			x,y, event = e;
851
852		// IE10 implemented pointer events with a prefix;
853		if ( e.type.indexOf('MSPointer') === 0 ) {
854			pointer = true;
855		}
856
857		if ( touch ) {
858			// noUiSlider supports one movement at a time,
859			// so we can select the first 'changedTouch'.
860			x = e.changedTouches[0].pageX;
861			y = e.changedTouches[0].pageY;
862		}
863
864		pageOffset = pageOffset || getPageOffset();
865
866		if ( mouse || pointer ) {
867			x = e.clientX + pageOffset.x;
868			y = e.clientY + pageOffset.y;
869		}
870
871		event.pageOffset = pageOffset;
872		event.points = [x, y];
873		event.cursor = mouse || pointer; // Fix #435
874
875		return event;
876	}
877
878	// Append a handle to the base.
879	function addHandle ( direction, index ) {
880
881		var origin = document.createElement('div'),
882			handle = document.createElement('div'),
883			classModifier = [options.cssClasses.handleLower, options.cssClasses.handleUpper];
884
885		if ( direction ) {
886			classModifier.reverse();
887		}
888
889		addClass(handle, options.cssClasses.handle);
890		addClass(handle, classModifier[index]);
891
892		addClass(origin, options.cssClasses.origin);
893		origin.appendChild(handle);
894
895		return origin;
896	}
897
898	// Add the proper connection classes.
899	function addConnection ( connect, target, handles ) {
900
901		// Apply the required connection classes to the elements
902		// that need them. Some classes are made up for several
903		// segments listed in the class list, to allow easy
904		// renaming and provide a minor compression benefit.
905		switch ( connect ) {
906			case 1:	addClass(target, options.cssClasses.connect);
907					addClass(handles[0], options.cssClasses.background);
908					break;
909			case 3: addClass(handles[1], options.cssClasses.background);
910					/* falls through */
911			case 2: addClass(handles[0], options.cssClasses.connect);
912					/* falls through */
913			case 0: addClass(target, options.cssClasses.background);
914					break;
915		}
916	}
917
918	// Add handles to the slider base.
919	function addHandles ( nrHandles, direction, base ) {
920
921		var index, handles = [];
922
923		// Append handles.
924		for ( index = 0; index < nrHandles; index += 1 ) {
925
926			// Keep a list of all added handles.
927			handles.push( base.appendChild(addHandle( direction, index )) );
928		}
929
930		return handles;
931	}
932
933	// Initialize a single slider.
934	function addSlider ( direction, orientation, target ) {
935
936		// Apply classes and data to the target.
937		addClass(target, options.cssClasses.target);
938
939		if ( direction === 0 ) {
940			addClass(target, options.cssClasses.ltr);
941		} else {
942			addClass(target, options.cssClasses.rtl);
943		}
944
945		if ( orientation === 0 ) {
946			addClass(target, options.cssClasses.horizontal);
947		} else {
948			addClass(target, options.cssClasses.vertical);
949		}
950
951		var div = document.createElement('div');
952		addClass(div, options.cssClasses.base);
953		target.appendChild(div);
954		return div;
955	}
956
957
958	function addTooltip ( handle, index ) {
959
960		if ( !options.tooltips[index] ) {
961			return false;
962		}
963
964		var element = document.createElement('div');
965		element.className = options.cssClasses.tooltip;
966		return handle.firstChild.appendChild(element);
967	}
968
969	// The tooltips option is a shorthand for using the 'update' event.
970	function tooltips ( ) {
971
972		if ( options.dir ) {
973			options.tooltips.reverse();
974		}
975
976		// Tooltips are added with options.tooltips in original order.
977		var tips = scope_Handles.map(addTooltip);
978
979		if ( options.dir ) {
980			tips.reverse();
981			options.tooltips.reverse();
982		}
983
984		bindEvent('update', function(f, o, r) {
985			if ( tips[o] ) {
986				tips[o].innerHTML = options.tooltips[o] === true ? f[o] : options.tooltips[o].to(r[o]);
987			}
988		});
989	}
990
991
992	function getGroup ( mode, values, stepped ) {
993
994		// Use the range.
995		if ( mode === 'range' || mode === 'steps' ) {
996			return scope_Spectrum.xVal;
997		}
998
999		if ( mode === 'count' ) {
1000
1001			// Divide 0 - 100 in 'count' parts.
1002			var spread = ( 100 / (values-1) ), v, i = 0;
1003			values = [];
1004
1005			// List these parts and have them handled as 'positions'.
1006			while ((v=i++*spread) <= 100 ) {
1007				values.push(v);
1008			}
1009
1010			mode = 'positions';
1011		}
1012
1013		if ( mode === 'positions' ) {
1014
1015			// Map all percentages to on-range values.
1016			return values.map(function( value ){
1017				return scope_Spectrum.fromStepping( stepped ? scope_Spectrum.getStep( value ) : value );
1018			});
1019		}
1020
1021		if ( mode === 'values' ) {
1022
1023			// If the value must be stepped, it needs to be converted to a percentage first.
1024			if ( stepped ) {
1025
1026				return values.map(function( value ){
1027
1028					// Convert to percentage, apply step, return to value.
1029					return scope_Spectrum.fromStepping( scope_Spectrum.getStep( scope_Spectrum.toStepping( value ) ) );
1030				});
1031
1032			}
1033
1034			// Otherwise, we can simply use the values.
1035			return values;
1036		}
1037	}
1038
1039	function generateSpread ( density, mode, group ) {
1040
1041		function safeIncrement(value, increment) {
1042			// Avoid floating point variance by dropping the smallest decimal places.
1043			return (value + increment).toFixed(7) / 1;
1044		}
1045
1046		var originalSpectrumDirection = scope_Spectrum.direction,
1047			indexes = {},
1048			firstInRange = scope_Spectrum.xVal[0],
1049			lastInRange = scope_Spectrum.xVal[scope_Spectrum.xVal.length-1],
1050			ignoreFirst = false,
1051			ignoreLast = false,
1052			prevPct = 0;
1053
1054		// This function loops the spectrum in an ltr linear fashion,
1055		// while the toStepping method is direction aware. Trick it into
1056		// believing it is ltr.
1057		scope_Spectrum.direction = 0;
1058
1059		// Create a copy of the group, sort it and filter away all duplicates.
1060		group = unique(group.slice().sort(function(a, b){ return a - b; }));
1061
1062		// Make sure the range starts with the first element.
1063		if ( group[0] !== firstInRange ) {
1064			group.unshift(firstInRange);
1065			ignoreFirst = true;
1066		}
1067
1068		// Likewise for the last one.
1069		if ( group[group.length - 1] !== lastInRange ) {
1070			group.push(lastInRange);
1071			ignoreLast = true;
1072		}
1073
1074		group.forEach(function ( current, index ) {
1075
1076			// Get the current step and the lower + upper positions.
1077			var step, i, q,
1078				low = current,
1079				high = group[index+1],
1080				newPct, pctDifference, pctPos, type,
1081				steps, realSteps, stepsize;
1082
1083			// When using 'steps' mode, use the provided steps.
1084			// Otherwise, we'll step on to the next subrange.
1085			if ( mode === 'steps' ) {
1086				step = scope_Spectrum.xNumSteps[ index ];
1087			}
1088
1089			// Default to a 'full' step.
1090			if ( !step ) {
1091				step = high-low;
1092			}
1093
1094			// Low can be 0, so test for false. If high is undefined,
1095			// we are at the last subrange. Index 0 is already handled.
1096			if ( low === false || high === undefined ) {
1097				return;
1098			}
1099
1100			// Find all steps in the subrange.
1101			for ( i = low; i <= high; i = safeIncrement(i, step) ) {
1102
1103				// Get the percentage value for the current step,
1104				// calculate the size for the subrange.
1105				newPct = scope_Spectrum.toStepping( i );
1106				pctDifference = newPct - prevPct;
1107
1108				steps = pctDifference / density;
1109				realSteps = Math.round(steps);
1110
1111				// This ratio represents the ammount of percentage-space a point indicates.
1112				// For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided.
1113				// Round the percentage offset to an even number, then divide by two
1114				// to spread the offset on both sides of the range.
1115				stepsize = pctDifference/realSteps;
1116
1117				// Divide all points evenly, adding the correct number to this subrange.
1118				// Run up to <= so that 100% gets a point, event if ignoreLast is set.
1119				for ( q = 1; q <= realSteps; q += 1 ) {
1120
1121					// The ratio between the rounded value and the actual size might be ~1% off.
1122					// Correct the percentage offset by the number of points
1123					// per subrange. density = 1 will result in 100 points on the
1124					// full range, 2 for 50, 4 for 25, etc.
1125					pctPos = prevPct + ( q * stepsize );
1126					indexes[pctPos.toFixed(5)] = ['x', 0];
1127				}
1128
1129				// Determine the point type.
1130				type = (group.indexOf(i) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 );
1131
1132				// Enforce the 'ignoreFirst' option by overwriting the type for 0.
1133				if ( !index && ignoreFirst ) {
1134					type = 0;
1135				}
1136
1137				if ( !(i === high && ignoreLast)) {
1138					// Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value.
1139					indexes[newPct.toFixed(5)] = [i, type];
1140				}
1141
1142				// Update the percentage count.
1143				prevPct = newPct;
1144			}
1145		});
1146
1147		// Reset the spectrum.
1148		scope_Spectrum.direction = originalSpectrumDirection;
1149
1150		return indexes;
1151	}
1152
1153	function addMarking ( spread, filterFunc, formatter ) {
1154
1155		var element = document.createElement('div'),
1156			out = '',
1157			valueSizeClasses = [
1158				options.cssClasses.valueNormal,
1159				options.cssClasses.valueLarge,
1160				options.cssClasses.valueSub
1161			],
1162			markerSizeClasses = [
1163				options.cssClasses.markerNormal,
1164				options.cssClasses.markerLarge,
1165				options.cssClasses.markerSub
1166			],
1167			valueOrientationClasses = [
1168				options.cssClasses.valueHorizontal,
1169				options.cssClasses.valueVertical
1170			],
1171			markerOrientationClasses = [
1172				options.cssClasses.markerHorizontal,
1173				options.cssClasses.markerVertical
1174			];
1175
1176		addClass(element, options.cssClasses.pips);
1177		addClass(element, options.ort === 0 ? options.cssClasses.pipsHorizontal : options.cssClasses.pipsVertical);
1178
1179		function getClasses( type, source ){
1180			var a = source === options.cssClasses.value,
1181				orientationClasses = a ? valueOrientationClasses : markerOrientationClasses,
1182				sizeClasses = a ? valueSizeClasses : markerSizeClasses;
1183
1184			return source + ' ' + orientationClasses[options.ort] + ' ' + sizeClasses[type];
1185		}
1186
1187		function getTags( offset, source, values ) {
1188			return 'class="' + getClasses(values[1], source) + '" style="' + options.style + ': ' + offset + '%"';
1189		}
1190
1191		function addSpread ( offset, values ){
1192
1193			if ( scope_Spectrum.direction ) {
1194				offset = 100 - offset;
1195			}
1196
1197			// Apply the filter function, if it is set.
1198			values[1] = (values[1] && filterFunc) ? filterFunc(values[0], values[1]) : values[1];
1199
1200			// Add a marker for every point
1201			out += '<div ' + getTags(offset, options.cssClasses.marker, values) + '></div>';
1202
1203			// Values are only appended for points marked '1' or '2'.
1204			if ( values[1] ) {
1205				out += '<div ' + getTags(offset, options.cssClasses.value, values) + '>' + formatter.to(values[0]) + '</div>';
1206			}
1207		}
1208
1209		// Append all points.
1210		Object.keys(spread).forEach(function(a){
1211			addSpread(a, spread[a]);
1212		});
1213
1214		element.innerHTML = out;
1215
1216		return element;
1217	}
1218
1219	function pips ( grid ) {
1220
1221	var mode = grid.mode,
1222		density = grid.density || 1,
1223		filter = grid.filter || false,
1224		values = grid.values || false,
1225		stepped = grid.stepped || false,
1226		group = getGroup( mode, values, stepped ),
1227		spread = generateSpread( density, mode, group ),
1228		format = grid.format || {
1229			to: Math.round
1230		};
1231
1232		return scope_Target.appendChild(addMarking(
1233			spread,
1234			filter,
1235			format
1236		));
1237	}
1238
1239
1240	// Shorthand for base dimensions.
1241	function baseSize ( ) {
1242		var rect = scope_Base.getBoundingClientRect(), alt = 'offset' + ['Width', 'Height'][options.ort];
1243		return options.ort === 0 ? (rect.width||scope_Base[alt]) : (rect.height||scope_Base[alt]);
1244	}
1245
1246	// External event handling
1247	function fireEvent ( event, handleNumber, tap ) {
1248
1249		var i;
1250
1251		// During initialization, do not fire events.
1252		for ( i = 0; i < options.handles; i++ ) {
1253			if ( scope_Locations[i] === -1 ) {
1254				return;
1255			}
1256		}
1257
1258		if ( handleNumber !== undefined && options.handles !== 1 ) {
1259			handleNumber = Math.abs(handleNumber - options.dir);
1260		}
1261
1262		Object.keys(scope_Events).forEach(function( targetEvent ) {
1263
1264			var eventType = targetEvent.split('.')[0];
1265
1266			if ( event === eventType ) {
1267				scope_Events[targetEvent].forEach(function( callback ) {
1268
1269					callback.call(
1270						// Use the slider public API as the scope ('this')
1271						scope_Self,
1272						// Return values as array, so arg_1[arg_2] is always valid.
1273						asArray(valueGet()),
1274						// Handle index, 0 or 1
1275						handleNumber,
1276						// Unformatted slider values
1277						asArray(inSliderOrder(Array.prototype.slice.call(scope_Values))),
1278						// Event is fired by tap, true or false
1279						tap || false,
1280						// Left offset of the handle, in relation to the slider
1281						scope_Locations
1282					);
1283				});
1284			}
1285		});
1286	}
1287
1288	// Returns the input array, respecting the slider direction configuration.
1289	function inSliderOrder ( values ) {
1290
1291		// If only one handle is used, return a single value.
1292		if ( values.length === 1 ){
1293			return values[0];
1294		}
1295
1296		if ( options.dir ) {
1297			return values.reverse();
1298		}
1299
1300		return values;
1301	}
1302
1303
1304	// Handler for attaching events trough a proxy.
1305	function attach ( events, element, callback, data ) {
1306
1307		// This function can be used to 'filter' events to the slider.
1308		// element is a node, not a nodeList
1309
1310		var method = function ( e ){
1311
1312			if ( scope_Target.hasAttribute('disabled') ) {
1313				return false;
1314			}
1315
1316			// Stop if an active 'tap' transition is taking place.
1317			if ( hasClass(scope_Target, options.cssClasses.tap) ) {
1318				return false;
1319			}
1320
1321			e = fixEvent(e, data.pageOffset);
1322
1323			// Ignore right or middle clicks on start #454
1324			if ( events === actions.start && e.buttons !== undefined && e.buttons > 1 ) {
1325				return false;
1326			}
1327
1328			// Ignore right or middle clicks on start #454
1329			if ( data.hover && e.buttons ) {
1330				return false;
1331			}
1332
1333			e.calcPoint = e.points[ options.ort ];
1334
1335			// Call the event handler with the event [ and additional data ].
1336			callback ( e, data );
1337
1338		}, methods = [];
1339
1340		// Bind a closure on the target for every event type.
1341		events.split(' ').forEach(function( eventName ){
1342			element.addEventListener(eventName, method, false);
1343			methods.push([eventName, method]);
1344		});
1345
1346		return methods;
1347	}
1348
1349	// Handle movement on document for handle and range drag.
1350	function move ( event, data ) {
1351
1352		// Fix #498
1353		// Check value of .buttons in 'start' to work around a bug in IE10 mobile (data.buttonsProperty).
1354		// https://connect.microsoft.com/IE/feedback/details/927005/mobile-ie10-windows-phone-buttons-property-of-pointermove-event-always-zero
1355		// IE9 has .buttons and .which zero on mousemove.
1356		// Firefox breaks the spec MDN defines.
1357		if ( navigator.appVersion.indexOf("MSIE 9") === -1 && event.buttons === 0 && data.buttonsProperty !== 0 ) {
1358			return end(event, data);
1359		}
1360
1361		var handles = data.handles || scope_Handles, positions, state = false,
1362			proposal = ((event.calcPoint - data.start) * 100) / data.baseSize,
1363			handleNumber = handles[0] === scope_Handles[0] ? 0 : 1, i;
1364
1365		// Calculate relative positions for the handles.
1366		positions = getPositions( proposal, data.positions, handles.length > 1);
1367
1368		state = setHandle ( handles[0], positions[handleNumber], handles.length === 1 );
1369
1370		if ( handles.length > 1 ) {
1371
1372			state = setHandle ( handles[1], positions[handleNumber?0:1], false ) || state;
1373
1374			if ( state ) {
1375				// fire for both handles
1376				for ( i = 0; i < data.handles.length; i++ ) {
1377					fireEvent('slide', i);
1378				}
1379			}
1380		} else if ( state ) {
1381			// Fire for a single handle
1382			fireEvent('slide', handleNumber);
1383		}
1384	}
1385
1386	// Unbind move events on document, call callbacks.
1387	function end ( event, data ) {
1388
1389		// The handle is no longer active, so remove the class.
1390		var active = scope_Base.querySelector( '.' + options.cssClasses.active ),
1391			handleNumber = data.handles[0] === scope_Handles[0] ? 0 : 1;
1392
1393		if ( active !== null ) {
1394			removeClass(active, options.cssClasses.active);
1395		}
1396
1397		// Remove cursor styles and text-selection events bound to the body.
1398		if ( event.cursor ) {
1399			document.body.style.cursor = '';
1400			document.body.removeEventListener('selectstart', document.body.noUiListener);
1401		}
1402
1403		var d = document.documentElement;
1404
1405		// Unbind the move and end events, which are added on 'start'.
1406		d.noUiListeners.forEach(function( c ) {
1407			d.removeEventListener(c[0], c[1]);
1408		});
1409
1410		// Remove dragging class.
1411		removeClass(scope_Target, options.cssClasses.drag);
1412
1413		// Fire the change and set events.
1414		fireEvent('set', handleNumber);
1415		fireEvent('change', handleNumber);
1416
1417		// If this is a standard handle movement, fire the end event.
1418		if ( data.handleNumber !== undefined ) {
1419			fireEvent('end', data.handleNumber);
1420		}
1421	}
1422
1423	// Fire 'end' when a mouse or pen leaves the document.
1424	function documentLeave ( event, data ) {
1425		if ( event.type === "mouseout" && event.target.nodeName === "HTML" && event.relatedTarget === null ){
1426			end ( event, data );
1427		}
1428	}
1429
1430	// Bind move events on document.
1431	function start ( event, data ) {
1432
1433		var d = document.documentElement;
1434
1435		// Mark the handle as 'active' so it can be styled.
1436		if ( data.handles.length === 1 ) {
1437			// Support 'disabled' handles
1438			if ( data.handles[0].hasAttribute('disabled') ) {
1439				return false;
1440			}
1441
1442			addClass(data.handles[0].children[0], options.cssClasses.active);
1443		}
1444
1445		// Fix #551, where a handle gets selected instead of dragged.
1446		event.preventDefault();
1447
1448		// A drag should never propagate up to the 'tap' event.
1449		event.stopPropagation();
1450
1451		// Attach the move and end events.
1452		var moveEvent = attach(actions.move, d, move, {
1453			start: event.calcPoint,
1454			baseSize: baseSize(),
1455			pageOffset: event.pageOffset,
1456			handles: data.handles,
1457			handleNumber: data.handleNumber,
1458			buttonsProperty: event.buttons,
1459			positions: [
1460				scope_Locations[0],
1461				scope_Locations[scope_Handles.length - 1]
1462			]
1463		}), endEvent = attach(actions.end, d, end, {
1464			handles: data.handles,
1465			handleNumber: data.handleNumber
1466		});
1467
1468		var outEvent = attach("mouseout", d, documentLeave, {
1469			handles: data.handles,
1470			handleNumber: data.handleNumber
1471		});
1472
1473		d.noUiListeners = moveEvent.concat(endEvent, outEvent);
1474
1475		// Text selection isn't an issue on touch devices,
1476		// so adding cursor styles can be skipped.
1477		if ( event.cursor ) {
1478
1479			// Prevent the 'I' cursor and extend the range-drag cursor.
1480			document.body.style.cursor = getComputedStyle(event.target).cursor;
1481
1482			// Mark the target with a dragging state.
1483			if ( scope_Handles.length > 1 ) {
1484				addClass(scope_Target, options.cssClasses.drag);
1485			}
1486
1487			var f = function(){
1488				return false;
1489			};
1490
1491			document.body.noUiListener = f;
1492
1493			// Prevent text selection when dragging the handles.
1494			document.body.addEventListener('selectstart', f, false);
1495		}
1496
1497		if ( data.handleNumber !== undefined ) {
1498			fireEvent('start', data.handleNumber);
1499		}
1500	}
1501
1502	// Move closest handle to tapped location.
1503	function tap ( event ) {
1504
1505		var location = event.calcPoint, total = 0, handleNumber, to;
1506
1507		// The tap event shouldn't propagate up and cause 'edge' to run.
1508		event.stopPropagation();
1509
1510		// Add up the handle offsets.
1511		scope_Handles.forEach(function(a){
1512			total += offset(a)[ options.style ];
1513		});
1514
1515		// Find the handle closest to the tapped position.
1516		handleNumber = ( location < total/2 || scope_Handles.length === 1 ) ? 0 : 1;
1517
1518		// Check if handler is not disablet if yes set number to the next handler
1519		if (scope_Handles[handleNumber].hasAttribute('disabled')) {
1520			handleNumber = handleNumber ? 0 : 1;
1521		}
1522
1523		location -= offset(scope_Base)[ options.style ];
1524
1525		// Calculate the new position.
1526		to = ( location * 100 ) / baseSize();
1527
1528		if ( !options.events.snap ) {
1529			// Flag the slider as it is now in a transitional state.
1530			// Transition takes a configurable amount of ms (default 300). Re-enable the slider after that.
1531			addClassFor( scope_Target, options.cssClasses.tap, options.animationDuration );
1532		}
1533
1534		// Support 'disabled' handles
1535		if ( scope_Handles[handleNumber].hasAttribute('disabled') ) {
1536			return false;
1537		}
1538
1539		// Find the closest handle and calculate the tapped point.
1540		// The set handle to the new position.
1541		setHandle( scope_Handles[handleNumber], to );
1542
1543		fireEvent('slide', handleNumber, true);
1544		fireEvent('set', handleNumber, true);
1545		fireEvent('change', handleNumber, true);
1546
1547		if ( options.events.snap ) {
1548			start(event, { handles: [scope_Handles[handleNumber]] });
1549		}
1550	}
1551
1552	// Fires a 'hover' event for a hovered mouse/pen position.
1553	function hover ( event ) {
1554
1555		var location = event.calcPoint - offset(scope_Base)[ options.style ],
1556			to = scope_Spectrum.getStep(( location * 100 ) / baseSize()),
1557			value = scope_Spectrum.fromStepping( to );
1558
1559		Object.keys(scope_Events).forEach(function( targetEvent ) {
1560			if ( 'hover' === targetEvent.split('.')[0] ) {
1561				scope_Events[targetEvent].forEach(function( callback ) {
1562					callback.call( scope_Self, value );
1563				});
1564			}
1565		});
1566	}
1567
1568	// Attach events to several slider parts.
1569	function events ( behaviour ) {
1570
1571		// Attach the standard drag event to the handles.
1572		if ( !behaviour.fixed ) {
1573
1574			scope_Handles.forEach(function( handle, index ){
1575
1576				// These events are only bound to the visual handle
1577				// element, not the 'real' origin element.
1578				attach ( actions.start, handle.children[0], start, {
1579					handles: [ handle ],
1580					handleNumber: index
1581				});
1582			});
1583		}
1584
1585		// Attach the tap event to the slider base.
1586		if ( behaviour.tap ) {
1587
1588			attach ( actions.start, scope_Base, tap, {
1589				handles: scope_Handles
1590			});
1591		}
1592
1593		// Fire hover events
1594		if ( behaviour.hover ) {
1595			attach ( actions.move, scope_Base, hover, { hover: true } );
1596		}
1597
1598		// Make the range draggable.
1599		if ( behaviour.drag ){
1600
1601			var drag = [scope_Base.querySelector( '.' + options.cssClasses.connect )];
1602			addClass(drag[0], options.cssClasses.draggable);
1603
1604			// When the range is fixed, the entire range can
1605			// be dragged by the handles. The handle in the first
1606			// origin will propagate the start event upward,
1607			// but it needs to be bound manually on the other.
1608			if ( behaviour.fixed ) {
1609				drag.push(scope_Handles[(drag[0] === scope_Handles[0] ? 1 : 0)].children[0]);
1610			}
1611
1612			drag.forEach(function( element ) {
1613				attach ( actions.start, element, start, {
1614					handles: scope_Handles
1615				});
1616			});
1617		}
1618	}
1619
1620
1621	// Test suggested values and apply margin, step.
1622	function setHandle ( handle, to, noLimitOption ) {
1623
1624		var trigger = handle !== scope_Handles[0] ? 1 : 0,
1625			lowerMargin = scope_Locations[0] + options.margin,
1626			upperMargin = scope_Locations[1] - options.margin,
1627			lowerLimit = scope_Locations[0] + options.limit,
1628			upperLimit = scope_Locations[1] - options.limit;
1629
1630		// For sliders with multiple handles,
1631		// limit movement to the other handle.
1632		// Apply the margin option by adding it to the handle positions.
1633		if ( scope_Handles.length > 1 ) {
1634			to = trigger ? Math.max( to, lowerMargin ) : Math.min( to, upperMargin );
1635		}
1636
1637		// The limit option has the opposite effect, limiting handles to a
1638		// maximum distance from another. Limit must be > 0, as otherwise
1639		// handles would be unmoveable. 'noLimitOption' is set to 'false'
1640		// for the .val() method, except for pass 4/4.
1641		if ( noLimitOption !== false && options.limit && scope_Handles.length > 1 ) {
1642			to = trigger ? Math.min ( to, lowerLimit ) : Math.max( to, upperLimit );
1643		}
1644
1645		// Handle the step option.
1646		to = scope_Spectrum.getStep( to );
1647
1648		// Limit percentage to the 0 - 100 range
1649		to = limit(to);
1650
1651		// Return false if handle can't move
1652		if ( to === scope_Locations[trigger] ) {
1653			return false;
1654		}
1655
1656		// Set the handle to the new position.
1657		// Use requestAnimationFrame for efficient painting.
1658		// No significant effect in Chrome, Edge sees dramatic
1659		// performace improvements.
1660		if ( window.requestAnimationFrame ) {
1661			window.requestAnimationFrame(function(){
1662				handle.style[options.style] = to + '%';
1663			});
1664		} else {
1665			handle.style[options.style] = to + '%';
1666		}
1667
1668		// Force proper handle stacking
1669		if ( !handle.previousSibling ) {
1670			removeClass(handle, options.cssClasses.stacking);
1671			if ( to > 50 ) {
1672				addClass(handle, options.cssClasses.stacking);
1673			}
1674		}
1675
1676		// Update locations.
1677		scope_Locations[trigger] = to;
1678
1679		// Convert the value to the slider stepping/range.
1680		scope_Values[trigger] = scope_Spectrum.fromStepping( to );
1681
1682		fireEvent('update', trigger);
1683
1684		return true;
1685	}
1686
1687	// Loop values from value method and apply them.
1688	function setValues ( count, values ) {
1689
1690		var i, trigger, to;
1691
1692		// With the limit option, we'll need another limiting pass.
1693		if ( options.limit ) {
1694			count += 1;
1695		}
1696
1697		// If there are multiple handles to be set run the setting
1698		// mechanism twice for the first handle, to make sure it
1699		// can be bounced of the second one properly.
1700		for ( i = 0; i < count; i += 1 ) {
1701
1702			trigger = i%2;
1703
1704			// Get the current argument from the array.
1705			to = values[trigger];
1706
1707			// Setting with null indicates an 'ignore'.
1708			// Inputting 'false' is invalid.
1709			if ( to !== null && to !== false ) {
1710
1711				// If a formatted number was passed, attemt to decode it.
1712				if ( typeof to === 'number' ) {
1713					to = String(to);
1714				}
1715
1716				to = options.format.from( to );
1717
1718				// Request an update for all links if the value was invalid.
1719				// Do so too if setting the handle fails.
1720				if ( to === false || isNaN(to) || setHandle( scope_Handles[trigger], scope_Spectrum.toStepping( to ), i === (3 - options.dir) ) === false ) {
1721					fireEvent('update', trigger);
1722				}
1723			}
1724		}
1725	}
1726
1727	// Set the slider value.
1728	function valueSet ( input, fireSetEvent ) {
1729
1730		var count, values = asArray( input ), i;
1731
1732		// Event fires by default
1733		fireSetEvent = (fireSetEvent === undefined ? true : !!fireSetEvent);
1734
1735		// The RTL settings is implemented by reversing the front-end,
1736		// internal mechanisms are the same.
1737		if ( options.dir && options.handles > 1 ) {
1738			values.reverse();
1739		}
1740
1741		// Animation is optional.
1742		// Make sure the initial values where set before using animated placement.
1743		if ( options.animate && scope_Locations[0] !== -1 ) {
1744			addClassFor( scope_Target, options.cssClasses.tap, options.animationDuration );
1745		}
1746
1747		// Determine how often to set the handles.
1748		count = scope_Handles.length > 1 ? 3 : 1;
1749
1750		if ( values.length === 1 ) {
1751			count = 1;
1752		}
1753
1754		setValues ( count, values );
1755
1756		// Fire the 'set' event for both handles.
1757		for ( i = 0; i < scope_Handles.length; i++ ) {
1758
1759			// Fire the event only for handles that received a new value, as per #579
1760			if ( values[i] !== null && fireSetEvent ) {
1761				fireEvent('set', i);
1762			}
1763		}
1764	}
1765
1766	// Get the slider value.
1767	function valueGet ( ) {
1768
1769		var i, retour = [];
1770
1771		// Get the value from all handles.
1772		for ( i = 0; i < options.handles; i += 1 ){
1773			retour[i] = options.format.to( scope_Values[i] );
1774		}
1775
1776		return inSliderOrder( retour );
1777	}
1778
1779	// Removes classes from the root and empties it.
1780	function destroy ( ) {
1781
1782		for ( var key in options.cssClasses ) {
1783			if ( !options.cssClasses.hasOwnProperty(key) ) { continue; }
1784			removeClass(scope_Target, options.cssClasses[key]);
1785		}
1786
1787		while (scope_Target.firstChild) {
1788			scope_Target.removeChild(scope_Target.firstChild);
1789		}
1790
1791		delete scope_Target.noUiSlider;
1792	}
1793
1794	// Get the current step size for the slider.
1795	function getCurrentStep ( ) {
1796
1797		// Check all locations, map them to their stepping point.
1798		// Get the step point, then find it in the input list.
1799		var retour = scope_Locations.map(function( location, index ){
1800
1801			var step = scope_Spectrum.getApplicableStep( location ),
1802
1803				// As per #391, the comparison for the decrement step can have some rounding issues.
1804				// Round the value to the precision used in the step.
1805				stepDecimals = countDecimals(String(step[2])),
1806
1807				// Get the current numeric value
1808				value = scope_Values[index],
1809
1810				// To move the slider 'one step up', the current step value needs to be added.
1811				// Use null if we are at the maximum slider value.
1812				increment = location === 100 ? null : step[2],
1813
1814				// Going 'one step down' might put the slider in a different sub-range, so we
1815				// need to switch between the current or the previous step.
1816				prev = Number((value - step[2]).toFixed(stepDecimals)),
1817
1818				// If the value fits the step, return the current step value. Otherwise, use the
1819				// previous step. Return null if the slider is at its minimum value.
1820				decrement = location === 0 ? null : (prev >= step[1]) ? step[2] : (step[0] || false);
1821
1822			return [decrement, increment];
1823		});
1824
1825		// Return values in the proper order.
1826		return inSliderOrder( retour );
1827	}
1828
1829	// Attach an event to this slider, possibly including a namespace
1830	function bindEvent ( namespacedEvent, callback ) {
1831		scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || [];
1832		scope_Events[namespacedEvent].push(callback);
1833
1834		// If the event bound is 'update,' fire it immediately for all handles.
1835		if ( namespacedEvent.split('.')[0] === 'update' ) {
1836			scope_Handles.forEach(function(a, index){
1837				fireEvent('update', index);
1838			});
1839		}
1840	}
1841
1842	// Undo attachment of event
1843	function removeEvent ( namespacedEvent ) {
1844
1845		var event = namespacedEvent && namespacedEvent.split('.')[0],
1846			namespace = event && namespacedEvent.substring(event.length);
1847
1848		Object.keys(scope_Events).forEach(function( bind ){
1849
1850			var tEvent = bind.split('.')[0],
1851				tNamespace = bind.substring(tEvent.length);
1852
1853			if ( (!event || event === tEvent) && (!namespace || namespace === tNamespace) ) {
1854				delete scope_Events[bind];
1855			}
1856		});
1857	}
1858
1859	// Updateable: margin, limit, step, range, animate, snap
1860	function updateOptions ( optionsToUpdate, fireSetEvent ) {
1861
1862		// Spectrum is created using the range, snap, direction and step options.
1863		// 'snap' and 'step' can be updated, 'direction' cannot, due to event binding.
1864		// If 'snap' and 'step' are not passed, they should remain unchanged.
1865		var v = valueGet(), newOptions = testOptions({
1866			start: [0, 0],
1867			margin: optionsToUpdate.margin,
1868			limit: optionsToUpdate.limit,
1869			step: optionsToUpdate.step === undefined ? options.singleStep : optionsToUpdate.step,
1870			range: optionsToUpdate.range,
1871			animate: optionsToUpdate.animate,
1872			snap: optionsToUpdate.snap === undefined ? options.snap : optionsToUpdate.snap
1873		});
1874
1875		['margin', 'limit', 'range', 'animate'].forEach(function(name){
1876
1877			// Only change options that we're actually passed to update.
1878			if ( optionsToUpdate[name] !== undefined ) {
1879				options[name] = optionsToUpdate[name];
1880			}
1881		});
1882
1883		// Save current spectrum direction as testOptions in testRange call
1884		// doesn't rely on current direction
1885		newOptions.spectrum.direction = scope_Spectrum.direction;
1886		scope_Spectrum = newOptions.spectrum;
1887
1888		// Invalidate the current positioning so valueSet forces an update.
1889		scope_Locations = [-1, -1];
1890		valueSet(optionsToUpdate.start || v, fireSetEvent);
1891	}
1892
1893
1894	// Throw an error if the slider was already initialized.
1895	if ( scope_Target.noUiSlider ) {
1896		throw new Error('Slider was already initialized.');
1897	}
1898
1899	// Create the base element, initialise HTML and set classes.
1900	// Add handles and links.
1901	scope_Base = addSlider( options.dir, options.ort, scope_Target );
1902	scope_Handles = addHandles( options.handles, options.dir, scope_Base );
1903
1904	// Set the connect classes.
1905	addConnection ( options.connect, scope_Target, scope_Handles );
1906
1907	if ( options.pips ) {
1908		pips(options.pips);
1909	}
1910
1911	if ( options.tooltips ) {
1912		tooltips();
1913	}
1914
1915	scope_Self = {
1916		destroy: destroy,
1917		steps: getCurrentStep,
1918		on: bindEvent,
1919		off: removeEvent,
1920		get: valueGet,
1921		set: valueSet,
1922		updateOptions: updateOptions,
1923		options: originalOptions, // Issue #600
1924		target: scope_Target, // Issue #597
1925		pips: pips // Issue #594
1926	};
1927
1928	// Attach user events.
1929	events( options.events );
1930
1931	return scope_Self;
1932
1933}
1934
1935
1936	// Run the standard initializer
1937	function initialize ( target, originalOptions ) {
1938
1939		if ( !target.nodeName ) {
1940			throw new Error('noUiSlider.create requires a single element.');
1941		}
1942
1943		// Test the options and create the slider environment;
1944		var options = testOptions( originalOptions, target ),
1945			slider = closure( target, options, originalOptions );
1946
1947		// Use the public value method to set the start values.
1948		slider.set(options.start);
1949
1950		target.noUiSlider = slider;
1951		return slider;
1952	}
1953
1954	// Use an object instead of a function for future expansibility;
1955	return {
1956		create: initialize
1957	};
1958
1959}));

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.