PageSourceSearch

https://rematon.ru/g/templates/shop2/2.30.2/js/nouislider.min.js

js rematon.ru collected 2026-10-07 15:29:14 UTC 58,668 bytes, 2,314 lines download raw bytes

vendor: 5,162 bytes, lines 1-211
1/*! noUiSlider - 7.0.10 - 2014-12-27 14:50:46 */
2
3(function(){
4
5	'use strict';
6
7var
8/** @const */ FormatOptions = [
9	'decimals',
10	'thousand',
11	'mark',
12	'prefix',
13	'postfix',
14	'encoder',
15	'decoder',
16	'negativeBefore',
17	'negative',
18	'edit',
19	'undo'
20];
21
22// General
23
24	// Reverse a string
25	function strReverse ( a ) {
26		return a.split('').reverse().join('');
27	}
28
29	// Check if a string starts with a specified prefix.
30	function strStartsWith ( input, match ) {
31		return input.substring(0, match.length) === match;
32	}
33
34	// Check is a string ends in a specified postfix.
35	function strEndsWith ( input, match ) {
36		return input.slice(-1 * match.length) === match;
37	}
38
39	// Throw an error if formatting options are incompatible.
40	function throwEqualError( F, a, b ) {
41		if ( (F[a] || F[b]) && (F[a] === F[b]) ) {
42			throw new Error(a);
43		}
44	}
45
46	// Check if a number is finite and not NaN
47	function isValidNumber ( input ) {
48		return typeof input === 'number' && isFinite( input );
49	}
50
51	// Provide rounding-accurate toFixed method.
52	function toFixed ( value, decimals ) {
53		var scale = Math.pow(10, decimals);
54		return ( Math.round(value * scale) / scale).toFixed( decimals );
55	}
56
57
58// Formatting
59
60	// Accept a number as input, output formatted string.
61	function formatTo ( decimals, thousand, mark, prefix, postfix, encoder, decoder, negativeBefore, negative, edit, undo, input ) {
62
63		var originalInput = input, inputIsNegative, inputPieces, inputBase, inputDecimals = '', output = '';
64
65		// Apply user encoder to the input.
66		// Expected outcome: number.
67		if ( encoder ) {
68			input = encoder(input);
69		}
70
71		// Stop if no valid number was provided, the number is infinite or NaN.
72		if ( !isValidNumber(input) ) {
73			return false;
74		}
75
76		// Rounding away decimals might cause a value of -0
77		// when using very small ranges. Remove those cases.
78		if ( decimals !== false && parseFloat(input.toFixed(decimals)) === 0 ) {
79			input = 0;
80		}
81
82		// Formatting is done on absolute numbers,
83		// decorated by an optional negative symbol.
84		if ( input < 0 ) {
85			inputIsNegative = true;
86			input = Math.abs(input);
87		}
88
89		// Reduce the number of decimals to the specified option.
90		if ( decimals !== false ) {
91			input = toFixed( input, decimals );
92		}
93
94		// Transform the number into a string, so it can be split.
95		input = input.toString();
96
97		// Break the number on the decimal separator.
98		if ( input.indexOf('.') !== -1 ) {
99			inputPieces = input.split('.');
100
101			inputBase = inputPieces[0];
102
103			if ( mark ) {
104				inputDecimals = mark + inputPieces[1];
105			}
106
107		} else {
108
109		// If it isn't split, the entire number will do.
110			inputBase = input;
111		}
112
113		// Group numbers in sets of three.
114		if ( thousand ) {
115			inputBase = strReverse(inputBase).match(/.{1,3}/g);
116			inputBase = strReverse(inputBase.join( strReverse( thousand ) ));
117		}
118
119		// If the number is negative, prefix with negation symbol.
120		if ( inputIsNegative && negativeBefore ) {
121			output += negativeBefore;
122		}
123
124		// Prefix the number
125		if ( prefix ) {
126			output += prefix;
127		}
128
129		// Normal negative option comes after the prefix. Defaults to '-'.
130		if ( inputIsNegative && negative ) {
131			output += negative;
132		}
133
134		// Append the actual number.
135		output += inputBase;
136		output += inputDecimals;
137
138		// Apply the postfix.
139		if ( postfix ) {
140			output += postfix;
141		}
142
143		// Run the output through a user-specified post-formatter.
144		if ( edit ) {
145			output = edit ( output, originalInput );
146		}
147
148		// All done.
149		return output;
150	}
151
152	// Accept a sting as input, output decoded number.
153	function formatFrom ( decimals, thousand, mark, prefix, postfix, encoder, decoder, negativeBefore, negative, edit, undo, input ) {
154
155		var originalInput = input, inputIsNegative, output = '';
156
157		// User defined pre-decoder. Result must be a non empty string.
158		if ( undo ) {
159			input = undo(input);
160		}
161
162		// Test the input. Can't be empty.
163		if ( !input || typeof input !== 'string' ) {
164			return false;
165		}
166
167		// If the string starts with the negativeBefore value: remove it.
168		// Remember is was there, the number is negative.
169		if ( negativeBefore && strStartsWith(input, negativeBefore) ) {
170			input = input.replace(negativeBefore, '');
171			inputIsNegative = true;
172		}
173
174		// Repeat the same procedure for the prefix.
175		if ( prefix && strStartsWith(input, prefix) ) {
176			input = input.replace(prefix, '');
177		}
178
179		// And again for negative.
180		if ( negative && strStartsWith(input, negative) ) {
181			input = input.replace(negative, '');
182			inputIsNegative = true;
183		}
184
185		// Remove the postfix.
186		// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
187		if ( postfix && strEndsWith(input, postfix) ) {
188			input = input.slice(0, -1 * postfix.length);
189		}
190
191		// Remove the thousand grouping.
192		if ( thousand ) {
193			input = input.split(thousand).join('');
194		}
195
196		// Set the decimal separator back to period.
197		if ( mark ) {
198			input = input.replace(mark, '.');
199		}
200
201		// Prepend the negative symbol.
202		if ( inputIsNegative ) {
203			output += '-';
204		}
205
206		// Add the number
207		output += input;
208
209		// Trim all non-numeric characters (allow '.' and '-');
210		output = output.replace(/[^0-9\.\-.]/g, '');
211
212		// The value contains no parse-able number.
213		if ( output === '' ) {
214			return false;
215		}
216
217		// Covert to number.
218		output = Number(output);
219
220		// Run the user-specified post-decoder.
221		if ( decoder ) {
222			output = decoder(output);
223		}
224
225		// Check is the output is valid, otherwise: return false.
226		if ( !isValidNumber(output) ) {
227			return false;
228		}
229
230		return output;
231	}
232
233
234// Framework
235
236	// Validate formatting options
237	function validate ( inputOptions ) {
238
239		var i, optionName, optionValue,
240			filteredOptions = {};
241
242		for ( i = 0; i < FormatOptions.length; i+=1 ) {
243
244			optionName = FormatOptions[i];
245			optionValue = inputOptions[optionName];
246
247			if ( optionValue === undefined ) {
248
249				// Only default if negativeBefore isn't set.
250				if ( optionName === 'negative' && !filteredOptions.negativeBefore ) {
251					filteredOptions[optionName] = '-';
252				// Don't set a default for mark when 'thousand' is set.
253				} else if ( optionName === 'mark' && filteredOptions.thousand !== '.' ) {
254					filteredOptions[optionName] = '.';
255				} else {
256					filteredOptions[optionName] = false;
257				}
258
259			// Floating points in JS are stable up to 7 decimals.
260			} else if ( optionName === 'decimals' ) {
261				if ( optionValue >= 0 && optionValue < 8 ) {
262					filteredOptions[optionName] = optionValue;
263				} else {
264					throw new Error(optionName);
265				}
266
267			// These options, when provided, must be functions.
268			} else if ( optionName === 'encoder' || optionName === 'decoder' || optionName === 'edit' || optionName === 'undo' ) {
269				if ( typeof optionValue === 'function' ) {
270					filteredOptions[optionName] = optionValue;
271				} else {
272					throw new Error(optionName);
273				}
274
275			// Other options are strings.
276			} else {
277
278				if ( typeof optionValue === 'string' ) {
279					filteredOptions[optionName] = optionValue;
280				} else {
281					throw new Error(optionName);
282				}
283			}
284		}
285
286		// Some values can't be extracted from a
287		// string if certain combinations are present.
288		throwEqualError(filteredOptions, 'mark', 'thousand');
289		throwEqualError(filteredOptions, 'prefix', 'negative');
290		throwEqualError(filteredOptions, 'prefix', 'negativeBefore');
291
292		return filteredOptions;
293	}
294
295	// Pass all options as function arguments
296	function passAll ( options, method, input ) {
297		var i, args = [];
298
299		// Add all options in order of FormatOptions
300		for ( i = 0; i < FormatOptions.length; i+=1 ) {
301			args.push(options[FormatOptions[i]]);
302		}
303
304		// Append the input, then call the method, presenting all
305		// options as arguments.
306		args.push(input);
307		return method.apply('', args);
308	}
309
310	/** @constructor */
311	function wNumb ( options ) {
312
313		if ( !(this instanceof wNumb) ) {
314			return new wNumb ( options );
315		}
316
317		if ( typeof options !== "object" ) {
318			return;
319		}
320
321		options = validate(options);
322
323		// Call 'formatTo' with proper arguments.
324		this.to = function ( input ) {
325			return passAll(options, formatTo, input);
326		};
327
328		// Call 'formatFrom' with proper arguments.
329		this.from = function ( input ) {
330			return passAll(options, formatFrom, input);
331		};
332	}
333
334	/** @export */
335	window.wNumb = wNumb;
336
337}());
338
339/*jslint browser: true */
340/*jslint white: true */
341
342(function( $ ){
343
344	'use strict';
345
346// Helpers
347
348	// Test in an object is an instance of jQuery or Zepto.
349	function isInstance ( a ) {
350		return a instanceof $ || ( $.zepto && $.zepto.isZ(a) );
351	}
352
353
354// Link types
355
356	function fromPrefix ( target, method ) {
357
358		// If target is a string, a new hidden input will be created.
359		if ( typeof target === 'string' && target.indexOf('-inline-') === 0 ) {
360
361			// By default, use the 'html' method.
362			this.method = method || 'html';
363
364			// Use jQuery to create the element
365			this.target = this.el = $( target.replace('-inline-', '') || '<div/>' );
366
367			return true;
368		}
369	}
370
371	function fromString ( target ) {
372
373		// If the string doesn't begin with '-', which is reserved, add a new hidden input.
374		if ( typeof target === 'string' && target.indexOf('-') !== 0 ) {
375
376			this.method = 'val';
377
378			var element = document.createElement('input');
379				element.name = target;
380				element.type = 'hidden';
381			this.target = this.el = $(element);
382
383			return true;
384		}
385	}
386
387	function fromFunction ( target ) {
388
389		// The target can also be a function, which will be called.
390		if ( typeof target === 'function' ) {
391			this.target = false;
392			this.method = target;
393
394			return true;
395		}
396	}
397
398	function fromInstance ( target, method ) {
399
400		if ( isInstance( target ) && !method ) {
401
402		// If a jQuery/Zepto input element is provided, but no method is set,
403		// the element can assume it needs to respond to 'change'...
404			if ( target.is('input, select, textarea') ) {
405
406				// Default to .val if this is an input element.
407				this.method = 'val';
408
409				// Fire the API changehandler when the target changes.
410				this.target = target.on('change.liblink', this.changeHandler);
411
412			} else {
413
414				this.target = target;
415
416				// If no method is set, and we are not auto-binding an input, default to 'html'.
417				this.method = 'html';
418			}
419
420			return true;
421		}
422	}
423
424	function fromInstanceMethod ( target, method ) {
425
426		// The method must exist on the element.
427		if ( isInstance( target ) &&
428			(typeof method === 'function' ||
429				(typeof method === 'string' && target[method]))
430		) {
431			this.method = method;
432			this.target = target;
433
434			return true;
435		}
436	}
437
438var
439/** @const */
440	creationFunctions = [fromPrefix, fromString, fromFunction, fromInstance, fromInstanceMethod];
441
442
443// Link Instance
444
445/** @constructor */
446	function Link ( target, method, format ) {
447
448		var that = this, valid = false;
449
450		// Forward calls within scope.
451		this.changeHandler = function ( changeEvent ) {
452			var decodedValue = that.formatInstance.from( $(this).val() );
453
454			// If the value is invalid, stop this event, as well as it's propagation.
455			if ( decodedValue === false || isNaN(decodedValue) ) {
456
457				// Reset the value.
458				$(this).val(that.lastSetValue);
459				return false;
460			}
461
462			that.changeHandlerMethod.call( '', changeEvent, decodedValue );
463		};
464
465		// See if this Link needs individual targets based on its usage.
466		// If so, return the element that needs to be copied by the
467		// implementing interface.
468		// Default the element to false.
469		this.el = false;
470
471		// Store the formatter, or use the default.
472		this.formatInstance = format;
473
474		// Try all Link types.
475		/*jslint unparam: true*/
476		$.each(creationFunctions, function(i, fn){
477			valid = fn.call(that, target, method);
478			return !valid;
479		});
480		/*jslint unparam: false*/
481
482		// Nothing matched, throw error.
483		if ( !valid ) {
484			throw new RangeError("(Link) Invalid Link.");
485		}
486	}
487
488	// Provides external items with the object value.
489	Link.prototype.set = function ( value ) {
490
491		// Ignore the value, so only the passed-on arguments remain.
492		var args = Array.prototype.slice.call( arguments ),
493			additionalArgs = args.slice(1);
494
495		// Store some values. The actual, numerical value,
496		// the formatted value and the parameters for use in 'resetValue'.
497		// Slice additionalArgs to break the relation.
498		this.lastSetValue = this.formatInstance.to( value );
499
500		// Prepend the value to the function arguments.
501		additionalArgs.unshift(
502			this.lastSetValue
503		);
504
505		// When target is undefined, the target was a function.
506		// In that case, provided the object as the calling scope.
507		// Branch between writing to a function or an object.
508		( typeof this.method === 'function' ?
509			this.method :
510			this.target[ this.method ] ).apply( this.target, additionalArgs );
511	};
512
513
514// Developer API
515
516/** @constructor */
517	function LinkAPI ( origin ) {
518		this.items = [];
519		this.elements = [];
520		this.origin = origin;
521	}
522
523	LinkAPI.prototype.push = function( item, element ) {
524		this.items.push(item);
525
526		// Prevent 'false' elements
527		if ( element ) {
528			this.elements.push(element);
529		}
530	};
531
532	LinkAPI.prototype.reconfirm = function ( flag ) {
533		var i;
534		for ( i = 0; i < this.elements.length; i += 1 ) {
535			this.origin.LinkConfirm(flag, this.elements[i]);
536		}
537	};
538
539	LinkAPI.prototype.remove = function ( flag ) {
540		var i;
541		for ( i = 0; i < this.items.length; i += 1 ) {
542			this.items[i].target.off('.liblink');
543		}
544		for ( i = 0; i < this.elements.length; i += 1 ) {
545			this.elements[i].remove();
546		}
547	};
548
549	LinkAPI.prototype.change = function ( value ) {
550
551		if ( this.origin.LinkIsEmitting ) {
552			return false;
553		}
554
555		this.origin.LinkIsEmitting = true;
556
557		var args = Array.prototype.slice.call( arguments, 1 ), i;
558		args.unshift( value );
559
560		// Write values to serialization Links.
561		// Convert the value to the correct relative representation.
562		for ( i = 0; i < this.items.length; i += 1 ) {
563			this.items[i].set.apply(this.items[i], args);
564		}
565
566		this.origin.LinkIsEmitting = false;
567	};
568
569
570// jQuery plugin
571
572	function binder ( flag, target, method, format ){
573
574		if ( flag === 0 ) {
575			flag = this.LinkDefaultFlag;
576		}
577
578		// Create a list of API's (if it didn't exist yet);
579		if ( !this.linkAPI ) {
580			this.linkAPI = {};
581		}
582
583		// Add an API point.
584		if ( !this.linkAPI[flag] ) {
585			this.linkAPI[flag] = new LinkAPI(this);
586		}
587
588		var linkInstance = new Link ( target, method, format || this.LinkDefaultFormatter );
589
590		// Default the calling scope to the linked object.
591		if ( !linkInstance.target ) {
592			linkInstance.target = $(this);
593		}
594
595		// If the Link requires creation of a new element,
596		// Pass the element and request confirmation to get the changehandler.
597		// Set the method to be called when a Link changes.
598		linkInstance.changeHandlerMethod = this.LinkConfirm( flag, linkInstance.el );
599
600		// Store the linkInstance in the flagged list.
601		this.linkAPI[flag].push( linkInstance, linkInstance.el );
602
603		// Now that Link have been connected, request an update.
604		this.LinkUpdate( flag );
605	}
606
607	/** @export */
608	$.fn.Link = function( flag ){
609
610		var that = this;
611
612		// Delete all linkAPI
613		if ( flag === false ) {
614
615			return that.each(function(){
616
617				// .Link(false) can be called on elements without Links.
618				// When that happens, the objects can't be looped.
619				if ( !this.linkAPI ) {
620					return;
621				}
622
623				$.map(this.linkAPI, function(api){
624					api.remove();
625				});
626
627				delete this.linkAPI;
628			});
629		}
630
631		if ( flag === undefined ) {
632
633			flag = 0;
634
635		} else if ( typeof flag !== 'string') {
636
637			throw new Error("Flag must be string.");
638		}
639
640		return {
641			to: function( a, b, c ){
642				return that.each(function(){
643					binder.call(this, flag, a, b, c);
644				});
645			}
646		};
647	};
648
649}( window.jQuery || window.Zepto ));
650
651/*jslint browser: true */
652/*jslint white: true */
653
654(function( $ ){
655
656	'use strict';
657
658
659	// Removes duplicates from an array.
660	function unique(array) {
661		return $.grep(array, function(el, index) {
662			return index === $.inArray(el, array);
663		});
664	}
665
666	// Round a value to the closest 'to'.
667	function closest ( value, to ) {
668		return Math.round(value / to) * to;
669	}
670
671	// Checks whether a value is numerical.
672	function isNumeric ( a ) {
673		return typeof a === 'number' && !isNaN( a ) && isFinite( a );
674	}
675
676	// Rounds a number to 7 supported decimals.
677	function accurateNumber( number ) {
678		var p = Math.pow(10, 7);
679		return Number((Math.round(number*p)/p).toFixed(7));
680	}
681
682	// Sets a class and removes it after [duration] ms.
683	function addClassFor ( element, className, duration ) {
684		element.addClass(className);
685		setTimeout(function(){
686			element.removeClass(className);
687		}, duration);
688	}
689
690	// Limits a value to 0 - 100
691	function limit ( a ) {
692		return Math.max(Math.min(a, 100), 0);
693	}
694
695	// Wraps a variable as an array, if it isn't one yet.
696	function asArray ( a ) {
697		return $.isArray(a) ? a : [a];
698	}
699
700	// Counts decimals
701	function countDecimals ( numStr ) {
702		var pieces = numStr.split(".");
703		return pieces.length > 1 ? pieces[1].length : 0;
704	}
705
706
707	var
708	// Cache the document selector;
709	/** @const */
710	doc = $(document),
711	// Make a backup of the original jQuery/Zepto .val() method.
712	/** @const */
713	$val = $.fn.val,
714	// Namespace for binding and unbinding slider events;
715	/** @const */
716	namespace = '.nui',
717	// Determine the events to bind. IE11 implements pointerEvents without
718	// a prefix, which breaks compatibility with the IE10 implementation.
719	/** @const */
720	actions = window.navigator.pointerEnabled ? {
721		start: 'pointerdown',
722		move: 'pointermove',
723		end: 'pointerup'
724	} : window.navigator.msPointerEnabled ? {
725		start: 'MSPointerDown',
726		move: 'MSPointerMove',
727		end: 'MSPointerUp'
728	} : {
729		start: 'mousedown touchstart',
730		move: 'mousemove touchmove',
731		end: 'mouseup touchend'
732	},
733	// Re-usable list of classes;
734	/** @const */
735	Classes = [
736/*  0 */  'noUi-target'
737/*  1 */ ,'noUi-base'
738/*  2 */ ,'noUi-origin'
739/*  3 */ ,'noUi-handle'
740/*  4 */ ,'noUi-horizontal'
741/*  5 */ ,'noUi-vertical'
742/*  6 */ ,'noUi-background'
743/*  7 */ ,'noUi-connect'
744/*  8 */ ,'noUi-ltr'
745/*  9 */ ,'noUi-rtl'
746/* 10 */ ,'noUi-dragable'
747/* 11 */ ,''
748/* 12 */ ,'noUi-state-drag'
749/* 13 */ ,''
750/* 14 */ ,'noUi-state-tap'
751/* 15 */ ,'noUi-active'
752/* 16 */ ,''
753/* 17 */ ,'noUi-stacking'
754	];
755
756
757// Value calculation
758
759	// Determine the size of a sub-range in relation to a full range.
760	function subRangeRatio ( pa, pb ) {
761		return (100 / (pb - pa));
762	}
763
764	// (percentage) How many percent is this value of this range?
765	function fromPercentage ( range, value ) {
766		return (value * 100) / ( range[1] - range[0] );
767	}
768
769	// (percentage) Where is this value on this range?
770	function toPercentage ( range, value ) {
771		return fromPercentage( range, range[0] < 0 ?
772			value + Math.abs(range[0]) :
773				value - range[0] );
774	}
775
776	// (value) How much is this percentage on this range?
777	function isPercentage ( range, value ) {
778		return ((value * ( range[1] - range[0] )) / 100) + range[0];
779	}
780
781
782// Range conversion
783
784	function getJ ( value, arr ) {
785
786		var j = 1;
787
788		while ( value >= arr[j] ){
789			j += 1;
790		}
791
792		return j;
793	}
794
795	// (percentage) Input a value, find where, on a scale of 0-100, it applies.
796	function toStepping ( xVal, xPct, value ) {
797
798		if ( value >= xVal.slice(-1)[0] ){
799			return 100;
800		}
801
802		var j = getJ( value, xVal ), va, vb, pa, pb;
803
804		va = xVal[j-1];
805		vb = xVal[j];
806		pa = xPct[j-1];
807		pb = xPct[j];
808
809		return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb));
810	}
811
812	// (value) Input a percentage, find where it is on the specified range.
813	function fromStepping ( xVal, xPct, value ) {
814
815		// There is no range group that fits 100
816		if ( value >= 100 ){
817			return xVal.slice(-1)[0];
818		}
819
820		var j = getJ( value, xPct ), va, vb, pa, pb;
821
822		va = xVal[j-1];
823		vb = xVal[j];
824		pa = xPct[j-1];
825		pb = xPct[j];
826
827		return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb));
828	}
829
830	// (percentage) Get the step that applies at a certain value.
831	function getStep ( xPct, xSteps, snap, value ) {
832
833		if ( value === 100 ) {
834			return value;
835		}
836
837		var j = getJ( value, xPct ), a, b;
838
839		// If 'snap' is set, steps are used as fixed points on the slider.
840		if ( snap ) {
841
842			a = xPct[j-1];
843			b = xPct[j];
844
845			// Find the closest position, a or b.
846			if ((value - a) > ((b-a)/2)){
847				return b;
848			}
849
850			return a;
851		}
852
853		if ( !xSteps[j-1] ){
854			return value;
855		}
856
857		return xPct[j-1] + closest(
858			value - xPct[j-1],
859			xSteps[j-1]
860		);
861	}
862
863
864// Entry parsing
865
866	function handleEntryPoint ( index, value, that ) {
867
868		var percentage;
869
870		// Wrap numerical input in an array.
871		if ( typeof value === "number" ) {
872			value = [value];
873		}
874
875		// Reject any invalid input, by testing whether value is an array.
876		if ( Object.prototype.toString.call( value ) !== '[object Array]' ){
877			throw new Error("noUiSlider: 'range' contains invalid value.");
878		}
879
880		// Covert min/max syntax to 0 and 100.
881		if ( index === 'min' ) {
882			percentage = 0;
883		} else if ( index === 'max' ) {
884			percentage = 100;
885		} else {
886			percentage = parseFloat( index );
887		}
888
889		// Check for correct input.
890		if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) {
891			throw new Error("noUiSlider: 'range' value isn't numeric.");
892		}
893
894		// Store values.
895		that.xPct.push( percentage );
896		that.xVal.push( value[0] );
897
898		// NaN will evaluate to false too, but to keep
899		// logging clear, set step explicitly. Make sure
900		// not to override the 'step' setting with false.
901		if ( !percentage ) {
902			if ( !isNaN( value[1] ) ) {
903				that.xSteps[0] = value[1];
904			}
905		} else {
906			that.xSteps.push( isNaN(value[1]) ? false : value[1] );
907		}
908	}
909
910	function handleStepPoint ( i, n, that ) {
911
912		// Ignore 'false' stepping.
913		if ( !n ) {
914			return true;
915		}
916
917		// Factor to range ratio
918		that.xSteps[i] = fromPercentage([
919			 that.xVal[i]
920			,that.xVal[i+1]
921		], n) / subRangeRatio (
922			that.xPct[i],
923			that.xPct[i+1] );
924	}
925
926
927// Interface
928
929	// The interface to Spectrum handles all direction-based
930	// conversions, so the above values are unaware.
931
932	function Spectrum ( entry, snap, direction, singleStep ) {
933
934		this.xPct = [];
935		this.xVal = [];
936		this.xSteps = [ singleStep || false ];
937		this.xNumSteps = [ false ];
938
939		this.snap = snap;
940		this.direction = direction;
941
942		var index, ordered = [ /* [0, 'min'], [1, '50%'], [2, 'max'] */ ];
943
944		// Map the object keys to an array.
945		for ( index in entry ) {
946			if ( entry.hasOwnProperty(index) ) {
947				ordered.push([entry[index], index]);
948			}
949		}
950
951		// Sort all entries by value (numeric sort).
952		ordered.sort(function(a, b) { return a[0] - b[0]; });
953
954		// Convert all entries to subranges.
955		for ( index = 0; index < ordered.length; index++ ) {
956			handleEntryPoint(ordered[index][1], ordered[index][0], this);
957		}
958
959		// Store the actual step values.
960		// xSteps is sorted in the same order as xPct and xVal.
961		this.xNumSteps = this.xSteps.slice(0);
962
963		// Convert all numeric steps to the percentage of the subrange they represent.
964		for ( index = 0; index < this.xNumSteps.length; index++ ) {
965			handleStepPoint(index, this.xNumSteps[index], this);
966		}
967	}
968
969	Spectrum.prototype.getMargin = function ( value ) {
970		return this.xPct.length === 2 ? fromPercentage(this.xVal, value) : false;
971	};
972
973	Spectrum.prototype.toStepping = function ( value ) {
974
975		value = toStepping( this.xVal, this.xPct, value );
976
977		// Invert the value if this is a right-to-left slider.
978		if ( this.direction ) {
979			value = 100 - value;
980		}
981
982		return value;
983	};
984
985	Spectrum.prototype.fromStepping = function ( value ) {
986
987		// Invert the value if this is a right-to-left slider.
988		if ( this.direction ) {
989			value = 100 - value;
990		}
991
992		return accurateNumber(fromStepping( this.xVal, this.xPct, value ));
993	};
994
995	Spectrum.prototype.getStep = function ( value ) {
996
997		// Find the proper step for rtl sliders by search in inverse direction.
998		// Fixes issue #262.
999		if ( this.direction ) {
1000			value = 100 - value;
1001		}
1002
1003		value = getStep(this.xPct, this.xSteps, this.snap, value );
1004
1005		if ( this.direction ) {
1006			value = 100 - value;
1007		}
1008
1009		return value;
1010	};
1011
1012	Spectrum.prototype.getApplicableStep = function ( value ) {
1013
1014		// If the value is 100%, return the negative step twice.
1015		var j = getJ(value, this.xPct), offset = value === 100 ? 2 : 1;
1016		return [this.xNumSteps[j-2], this.xVal[j-offset], this.xNumSteps[j-offset]];
1017	};
1018
1019	// Outside testing
1020	Spectrum.prototype.convert = function ( value ) {
1021		return this.getStep(this.toStepping(value));
1022	};
1023
1024/*	Every input option is tested and parsed. This'll prevent
1025	endless validation in internal methods. These tests are
1026	structured with an item for every option available. An
1027	option can be marked as required by setting the 'r' flag.
1028	The testing function is provided with three arguments:
1029		- The provided value for the option;
1030		- A reference to the options object;
1031		- The name for the option;
1032
1033	The testing function returns false when an error is detected,
1034	or true when everything is OK. It can also modify the option
1035	object, to make sure all values can be correctly looped elsewhere. */
1036
1037	/** @const */
1038	var defaultFormatter = { 'to': function( value ){
1039		return value.toFixed(2);
1040	}, 'from': Number };
1041
1042	function testStep ( parsed, entry ) {
1043
1044		if ( !isNumeric( entry ) ) {
1045			throw new Error("noUiSlider: 'step' is not numeric.");
1046		}
1047
1048		// The step option can still be used to set stepping
1049		// for linear sliders. Overwritten if set in 'range'.
1050		parsed.singleStep = entry;
1051	}
1052
1053	function testRange ( parsed, entry ) {
1054
1055		// Filter incorrect input.
1056		if ( typeof entry !== 'object' || $.isArray(entry) ) {
1057			throw new Error("noUiSlider: 'range' is not an object.");
1058		}
1059
1060		// Catch missing start or end.
1061		if ( entry.min === undefined || entry.max === undefined ) {
1062			throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'.");
1063		}
1064
1065		parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep);
1066	}
1067
1068	function testStart ( parsed, entry ) {
1069
1070		entry = asArray(entry);
1071
1072		// Validate input. Values aren't tested, as the public .val method
1073		// will always provide a valid location.
1074		if ( !$.isArray( entry ) || !entry.length || entry.length > 2 ) {
1075			throw new Error("noUiSlider: 'start' option is incorrect.");
1076		}
1077
1078		// Store the number of handles.
1079		parsed.handles = entry.length;
1080
1081		// When the slider is initialized, the .val method will
1082		// be called with the start options.
1083		parsed.start = entry;
1084	}
1085
1086	function testSnap ( parsed, entry ) {
1087
1088		// Enforce 100% stepping within subranges.
1089		parsed.snap = entry;
1090
1091		if ( typeof entry !== 'boolean' ){
1092			throw new Error("noUiSlider: 'snap' option must be a boolean.");
1093		}
1094	}
1095
1096	function testAnimate ( parsed, entry ) {
1097
1098		// Enforce 100% stepping within subranges.
1099		parsed.animate = entry;
1100
1101		if ( typeof entry !== 'boolean' ){
1102			throw new Error("noUiSlider: 'animate' option must be a boolean.");
1103		}
1104	}
1105
1106	function testConnect ( parsed, entry ) {
1107
1108		if ( entry === 'lower' && parsed.handles === 1 ) {
1109			parsed.connect = 1;
1110		} else if ( entry === 'upper' && parsed.handles === 1 ) {
1111			parsed.connect = 2;
1112		} else if ( entry === true && parsed.handles === 2 ) {
1113			parsed.connect = 3;
1114		} else if ( entry === false ) {
1115			parsed.connect = 0;
1116		} else {
1117			throw new Error("noUiSlider: 'connect' option doesn't match handle count.");
1118		}
1119	}
1120
1121	function testOrientation ( parsed, entry ) {
1122
1123		// Set orientation to an a numerical value for easy
1124		// array selection.
1125		switch ( entry ){
1126		  case 'horizontal':
1127			parsed.ort = 0;
1128			break;
1129		  case 'vertical':
1130			parsed.ort = 1;
1131			break;
1132		  default:
1133			throw new Error("noUiSlider: 'orientation' option is invalid.");
1134		}
1135	}
1136
1137	function testMargin ( parsed, entry ) {
1138
1139		if ( !isNumeric(entry) ){
1140			throw new Error("noUiSlider: 'margin' option must be numeric.");
1141		}
1142
1143		parsed.margin = parsed.spectrum.getMargin(entry);
1144
1145		if ( !parsed.margin ) {
1146			throw new Error("noUiSlider: 'margin' option is only supported on linear sliders.");
1147		}
1148	}
1149
1150	function testLimit ( parsed, entry ) {
1151
1152		if ( !isNumeric(entry) ){
1153			throw new Error("noUiSlider: 'limit' option must be numeric.");
1154		}
1155
1156		parsed.limit = parsed.spectrum.getMargin(entry);
1157
1158		if ( !parsed.limit ) {
1159			throw new Error("noUiSlider: 'limit' option is only supported on linear sliders.");
1160		}
1161	}
1162
1163	function testDirection ( parsed, entry ) {
1164
1165		// Set direction as a numerical value for easy parsing.
1166		// Invert connection for RTL sliders, so that the proper
1167		// handles get the connect/background classes.
1168		switch ( entry ) {
1169		  case 'ltr':
1170			parsed.dir = 0;
1171			break;
1172		  case 'rtl':
1173			parsed.dir = 1;
1174			parsed.connect = [0,2,1,3][parsed.connect];
1175			break;
1176		  default:
1177			throw new Error("noUiSlider: 'direction' option was not recognized.");
1178		}
1179	}
1180
1181	function testBehaviour ( parsed, entry ) {
1182
1183		// Make sure the input is a string.
1184		if ( typeof entry !== 'string' ) {
1185			throw new Error("noUiSlider: 'behaviour' must be a string containing options.");
1186		}
1187
1188		// Check if the string contains any keywords.
1189		// None are required.
1190		var tap = entry.indexOf('tap') >= 0,
1191			drag = entry.indexOf('drag') >= 0,
1192			fixed = entry.indexOf('fixed') >= 0,
1193			snap = entry.indexOf('snap') >= 0;
1194
1195		parsed.events = {
1196			tap: tap || snap,
1197			drag: drag,
1198			fixed: fixed,
1199			snap: snap
1200		};
1201	}
1202
1203	function testFormat ( parsed, entry ) {
1204
1205		parsed.format = entry;
1206
1207		// Any object with a to and from method is supported.
1208		if ( typeof entry.to === 'function' && typeof entry.from === 'function' ) {
1209			return true;
1210		}
1211
1212		throw new Error( "noUiSlider: 'format' requires 'to' and 'from' methods.");
1213	}
1214
1215	// Test all developer settings and parse to assumption-safe values.
1216	function testOptions ( options ) {
1217
1218		var parsed = {
1219			margin: 0,
1220			limit: 0,
1221			animate: true,
1222			format: defaultFormatter
1223		}, tests;
1224
1225		// Tests are executed in the order they are presented here.
1226		tests = {
1227			'step': { r: false, t: testStep },
1228			'start': { r: true, t: testStart },
1229			'connect': { r: true, t: testConnect },
1230			'direction': { r: true, t: testDirection },
1231			'snap': { r: false, t: testSnap },
1232			'animate': { r: false, t: testAnimate },
1233			'range': { r: true, t: testRange },
1234			'orientation': { r: false, t: testOrientation },
1235			'margin': { r: false, t: testMargin },
1236			'limit': { r: false, t: testLimit },
1237			'behaviour': { r: true, t: testBehaviour },
1238			'format': { r: false, t: testFormat }
1239		};
1240
1241		// Set defaults where applicable.
1242		options = $.extend({
1243			'connect': false,
1244			'direction': 'ltr',
1245			'behaviour': 'tap',
1246			'orientation': 'horizontal'
1247		}, options);
1248
1249		// Run all options through a testing mechanism to ensure correct
1250		// input. It should be noted that options might get modified to
1251		// be handled properly. E.g. wrapping integers in arrays.
1252		$.each( tests, function( name, test ){
1253
1254			// If the option isn't set, but it is required, throw an error.
1255			if ( options[name] === undefined ) {
1256
1257				if ( test.r ) {
1258					throw new Error("noUiSlider: '" + name + "' is required.");
1259				}
1260
1261				return true;
1262			}
1263
1264			test.t( parsed, options[name] );
1265		});
1266
1267		// Pre-define the styles.
1268		parsed.style = parsed.ort ? 'top' : 'left';
1269
1270		return parsed;
1271	}
1272
1273// Class handling
1274
1275	// Delimit proposed values for handle positions.
1276	function getPositions ( a, b, delimit ) {
1277
1278		// Add movement to current position.
1279		var c = a + b[0], d = a + b[1];
1280
1281		// Only alter the other position on drag,
1282		// not on standard sliding.
1283		if ( delimit ) {
1284			if ( c < 0 ) {
1285				d += Math.abs(c);
1286			}
1287			if ( d > 100 ) {
1288				c -= ( d - 100 );
1289			}
1290
1291			// Limit values to 0 and 100.
1292			return [limit(c), limit(d)];
1293		}
1294
1295		return [c,d];
1296	}
1297
1298
1299// Event handling
1300
1301	// Provide a clean event with standardized offset values.
1302	function fixEvent ( e ) {
1303
1304		// Prevent scrolling and panning on touch events, while
1305		// attempting to slide. The tap event also depends on this.
1306		e.preventDefault();
1307
1308		// Filter the event to register the type, which can be
1309		// touch, mouse or pointer. Offset changes need to be
1310		// made on an event specific basis.
1311		var  touch = e.type.indexOf('touch') === 0
1312			,mouse = e.type.indexOf('mouse') === 0
1313			,pointer = e.type.indexOf('pointer') === 0
1314			,x,y, event = e;
1315
1316		// IE10 implemented pointer events with a prefix;
1317		if ( e.type.indexOf('MSPointer') === 0 ) {
1318			pointer = true;
1319		}
1320
1321		// Get the originalEvent, if the event has been wrapped
1322		// by jQuery. Zepto doesn't wrap the event.
1323		if ( e.originalEvent ) {
1324			e = e.originalEvent;
1325		}
1326
1327		if ( touch ) {
1328			// noUiSlider supports one movement at a time,
1329			// so we can select the first 'changedTouch'.
1330			x = e.changedTouches[0].pageX;
1331			y = e.changedTouches[0].pageY;
1332		}
1333
1334		if ( mouse || pointer ) {
1335
1336			// Polyfill the pageXOffset and pageYOffset
1337			// variables for IE7 and IE8;
1338			if( !pointer && window.pageXOffset === undefined ){
1339				window.pageXOffset = document.documentElement.scrollLeft;
1340				window.pageYOffset = document.documentElement.scrollTop;
1341			}
1342
1343			x = e.clientX + window.pageXOffset;
1344			y = e.clientY + window.pageYOffset;
1345		}
1346
1347		event.points = [x, y];
1348		event.cursor = mouse;
1349
1350		return event;
1351	}
1352
1353
1354// DOM additions
1355
1356	// Append a handle to the base.
1357	function addHandle ( direction, index ) {
1358
1359		var handle = $('<div><div/></div>').addClass( Classes[2] ),
1360			additions = [ '-lower', '-upper' ];
1361
1362		if ( direction ) {
1363			additions.reverse();
1364		}
1365
1366		handle.children().addClass(
1367			Classes[3] + " " + Classes[3]+additions[index]
1368		);
1369
1370		return handle;
1371	}
1372
1373	// Add the proper connection classes.
1374	function addConnection ( connect, target, handles ) {
1375
1376		// Apply the required connection classes to the elements
1377		// that need them. Some classes are made up for several
1378		// segments listed in the class list, to allow easy
1379		// renaming and provide a minor compression benefit.
1380		switch ( connect ) {
1381			case 1:	target.addClass( Classes[7] );
1382					handles[0].addClass( Classes[6] );
1383					break;
1384			case 3: handles[1].addClass( Classes[6] );
1385					/* falls through */
1386			case 2: handles[0].addClass( Classes[7] );
1387					/* falls through */
1388			case 0: target.addClass(Classes[6]);
1389					break;
1390		}
1391	}
1392
1393	// Add handles to the slider base.
1394	function addHandles ( nrHandles, direction, base ) {
1395
1396		var index, handles = [];
1397
1398		// Append handles.
1399		for ( index = 0; index < nrHandles; index += 1 ) {
1400
1401			// Keep a list of all added handles.
1402			handles.push( addHandle( direction, index ).appendTo(base) );
1403		}
1404
1405		return handles;
1406	}
1407
1408	// Initialize a single slider.
1409	function addSlider ( direction, orientation, target ) {
1410
1411		// Apply classes and data to the target.
1412		target.addClass([
1413			Classes[0],
1414			Classes[8 + direction],
1415			Classes[4 + orientation]
1416		].join(' '));
1417
1418		return $('<div/>').appendTo(target).addClass( Classes[1] );
1419	}
1420
1421function closure ( target, options, originalOptions ){
1422
1423// Internal variables
1424
1425	// All variables local to 'closure' are marked $.
1426	var $Target = $(target),
1427		$Locations = [-1, -1],
1428		$Base,
1429		$Handles,
1430		$Spectrum = options.spectrum,
1431		$Values = [],
1432	// libLink. For rtl sliders, 'lower' and 'upper' should not be inverted
1433	// for one-handle sliders, so trim 'upper' it that case.
1434		triggerPos = ['lower', 'upper'].slice(0, options.handles);
1435
1436	// Invert the libLink connection for rtl sliders.
1437	if ( options.dir ) {
1438		triggerPos.reverse();
1439	}
1440
1441// Helpers
1442
1443	// Shorthand for base dimensions.
1444	function baseSize ( ) {
1445		return $Base[['width', 'height'][options.ort]]();
1446	}
1447
1448	// External event handling
1449	function fireEvents ( events ) {
1450
1451		// Use the external api to get the values.
1452		// Wrap the values in an array, as .trigger takes
1453		// only one additional argument.
1454		var index, values = [ $Target.val() ];
1455
1456		for ( index = 0; index < events.length; index += 1 ){
1457			$Target.trigger(events[index], values);
1458		}
1459	}
1460
1461	// Returns the input array, respecting the slider direction configuration.
1462	function inSliderOrder ( values ) {
1463
1464		// If only one handle is used, return a single value.
1465		if ( values.length === 1 ){
1466			return values[0];
1467		}
1468
1469		if ( options.dir ) {
1470			return values.reverse();
1471		}
1472
1473		return values;
1474	}
1475
1476// libLink integration
1477
1478	// Create a new function which calls .val on input change.
1479	function createChangeHandler ( trigger ) {
1480		return function ( ignore, value ){
1481			// Determine which array position to 'null' based on 'trigger'.
1482			$Target.val( [ trigger ? null : value, trigger ? value : null ], true );
1483		};
1484	}
1485
1486	// Called by libLink when it wants a set of links updated.
1487	function linkUpdate ( flag ) {
1488
1489		var trigger = $.inArray(flag, triggerPos);
1490
1491		// The API might not have been set yet.
1492		if ( $Target[0].linkAPI && $Target[0].linkAPI[flag] ) {
1493			$Target[0].linkAPI[flag].change(
1494				$Values[trigger],
1495				$Handles[trigger].children(),
1496				$Target
1497			);
1498		}
1499	}
1500
1501	// Called by libLink to append an element to the slider.
1502	function linkConfirm ( flag, element ) {
1503
1504		// Find the trigger for the passed flag.
1505		var trigger = $.inArray(flag, triggerPos);
1506
1507		// If set, append the element to the handle it belongs to.
1508		if ( element ) {
1509			element.appendTo( $Handles[trigger].children() );
1510		}
1511
1512		// The public API is reversed for rtl sliders, so the changeHandler
1513		// should not be aware of the inverted trigger positions.
1514		// On rtl slider with one handle, 'lower' should be used.
1515		if ( options.dir && options.handles > 1 ) {
1516			trigger = trigger === 1 ? 0 : 1;
1517		}
1518
1519		return createChangeHandler( trigger );
1520	}
1521
1522	// Place elements back on the slider.
1523	function reAppendLink ( ) {
1524
1525		var i, flag;
1526
1527		// The API keeps a list of elements: we can re-append them on rebuild.
1528		for ( i = 0; i < triggerPos.length; i += 1 ) {
1529			if ( this.linkAPI && this.linkAPI[(flag = triggerPos[i])] ) {
1530				this.linkAPI[flag].reconfirm(flag);
1531			}
1532		}
1533	}
1534
1535	target.LinkUpdate = linkUpdate;
1536	target.LinkConfirm = linkConfirm;
1537	target.LinkDefaultFormatter = options.format;
1538	target.LinkDefaultFlag = 'lower';
1539
1540	target.reappend = reAppendLink;
1541
1542
1543	// Handler for attaching events trough a proxy.
1544	function attach ( events, element, callback, data ) {
1545
1546		// This function can be used to 'filter' events to the slider.
1547
1548		// Add the noUiSlider namespace to all events.
1549		events = events.replace( /\s/g, namespace + ' ' ) + namespace;
1550
1551		// Bind a closure on the target.
1552		return element.on( events, function( e ){
1553
1554			// jQuery and Zepto (1) handle unset attributes differently,
1555			// but always falsy; #208
1556			if ( !!$Target.attr('disabled') ) {
1557				return false;
1558			}
1559
1560			// Stop if an active 'tap' transition is taking place.
1561			if ( $Target.hasClass( Classes[14] ) ) {
1562				return false;
1563			}
1564
1565			e = fixEvent(e);
1566			e.calcPoint = e.points[ options.ort ];
1567
1568			// Call the event handler with the event [ and additional data ].
1569			callback ( e, data );
1570		});
1571	}
1572
1573	// Handle movement on document for handle and range drag.
1574	function move ( event, data ) {
1575
1576		var handles = data.handles || $Handles, positions, state = false,
1577			proposal = ((event.calcPoint - data.start) * 100) / baseSize(),
1578			h = handles[0][0] !== $Handles[0][0] ? 1 : 0;
1579
1580		// Calculate relative positions for the handles.
1581		positions = getPositions( proposal, data.positions, handles.length > 1);
1582
1583		state = setHandle ( handles[0], positions[h], handles.length === 1 );
1584
1585		if ( handles.length > 1 ) {
1586			state = setHandle ( handles[1], positions[h?0:1], false ) || state;
1587		}
1588
1589		// Fire the 'slide' event if any handle moved.
1590		if ( state ) {
1591			fireEvents(['slide']);
1592		}
1593	}
1594
1595	// Unbind move events on document, call callbacks.
1596	function end ( event ) {
1597
1598		// The handle is no longer active, so remove the class.
1599		$('.' + Classes[15]).removeClass(Classes[15]);
1600
1601		// Remove cursor styles and text-selection events bound to the body.
1602		if ( event.cursor ) {
1603			$('body').css('cursor', '').off( namespace );
1604		}
1605
1606		// Unbind the move and end events, which are added on 'start'.
1607		doc.off( namespace );
1608
1609		// Remove dragging class.
1610		$Target.removeClass(Classes[12]);
1611
1612		// Fire the change and set events.
1613		fireEvents(['set', 'change']);
1614	}
1615
1616	// Bind move events on document.
1617	function start ( event, data ) {
1618
1619		// Mark the handle as 'active' so it can be styled.
1620		if( data.handles.length === 1 ) {
1621			data.handles[0].children().addClass(Classes[15]);
1622		}
1623
1624		// A drag should never propagate up to the 'tap' event.
1625		event.stopPropagation();
1626
1627		// Attach the move event.
1628		attach ( actions.move, doc, move, {
1629			start: event.calcPoint,
1630			handles: data.handles,
1631			positions: [
1632				$Locations[0],
1633				$Locations[$Handles.length - 1]
1634			]
1635		});
1636
1637		// Unbind all movement when the drag ends.
1638		attach ( actions.end, doc, end, null );
1639
1640		// Text selection isn't an issue on touch devices,
1641		// so adding cursor styles can be skipped.
1642		if ( event.cursor ) {
1643
1644			// Prevent the 'I' cursor and extend the range-drag cursor.
1645			$('body').css('cursor', $(event.target).css('cursor'));
1646
1647			// Mark the target with a dragging state.
1648			if ( $Handles.length > 1 ) {
1649				$Target.addClass(Classes[12]);
1650			}
1651
1652			// Prevent text selection when dragging the handles.
1653			$('body').on('selectstart' + namespace, false);
1654		}
1655	}
1656
1657	// Move closest handle to tapped location.
1658	function tap ( event ) {
1659
1660		var location = event.calcPoint, total = 0, to;
1661
1662		// The tap event shouldn't propagate up and cause 'edge' to run.
1663		event.stopPropagation();
1664
1665		// Add up the handle offsets.
1666		$.each( $Handles, function(){
1667			total += this.offset()[ options.style ];
1668		});
1669
1670		// Find the handle closest to the tapped position.
1671		total = ( location < total/2 || $Handles.length === 1 ) ? 0 : 1;
1672
1673		location -= $Base.offset()[ options.style ];
1674
1675		// Calculate the new position.
1676		to = ( location * 100 ) / baseSize();
1677
1678		if ( !options.events.snap ) {
1679			// Flag the slider as it is now in a transitional state.
1680			// Transition takes 300 ms, so re-enable the slider afterwards.
1681			addClassFor( $Target, Classes[14], 300 );
1682		}
1683
1684		// Find the closest handle and calculate the tapped point.
1685		// The set handle to the new position.
1686		setHandle( $Handles[total], to );
1687
1688		fireEvents(['slide', 'set', 'change']);
1689
1690		if ( options.events.snap ) {
1691			start(event, { handles: [$Handles[total]] });
1692		}
1693	}
1694
1695	// Attach events to several slider parts.
1696	function events ( behaviour ) {
1697
1698		var i, drag;
1699
1700		// Attach the standard drag event to the handles.
1701		if ( !behaviour.fixed ) {
1702
1703			for ( i = 0; i < $Handles.length; i += 1 ) {
1704
1705				// These events are only bound to the visual handle
1706				// element, not the 'real' origin element.
1707				attach ( actions.start, $Handles[i].children(), start, {
1708					handles: [ $Handles[i] ]
1709				});
1710			}
1711		}
1712
1713		// Attach the tap event to the slider base.
1714		if ( behaviour.tap ) {
1715
1716			attach ( actions.start, $Base, tap, {
1717				handles: $Handles
1718			});
1719		}
1720
1721		// Make the range dragable.
1722		if ( behaviour.drag ){
1723
1724			drag = $Base.find( '.' + Classes[7] ).addClass( Classes[10] );
1725
1726			// When the range is fixed, the entire range can
1727			// be dragged by the handles. The handle in the first
1728			// origin will propagate the start event upward,
1729			// but it needs to be bound manually on the other.
1730			if ( behaviour.fixed ) {
1731				drag = drag.add($Base.children().not( drag ).children());
1732			}
1733
1734			attach ( actions.start, drag, start, {
1735				handles: $Handles
1736			});
1737		}
1738	}
1739
1740
1741	// Test suggested values and apply margin, step.
1742	function setHandle ( handle, to, noLimitOption ) {
1743
1744		var trigger = handle[0] !== $Handles[0][0] ? 1 : 0,
1745			lowerMargin = $Locations[0] + options.margin,
1746			upperMargin = $Locations[1] - options.margin,
1747			lowerLimit = $Locations[0] + options.limit,
1748			upperLimit = $Locations[1] - options.limit;
1749
1750		// For sliders with multiple handles,
1751		// limit movement to the other handle.
1752		// Apply the margin option by adding it to the handle positions.
1753		if ( $Handles.length > 1 ) {
1754			to = trigger ? Math.max( to, lowerMargin ) : Math.min( to, upperMargin );
1755		}
1756
1757		// The limit option has the opposite effect, limiting handles to a
1758		// maximum distance from another. Limit must be > 0, as otherwise
1759		// handles would be unmoveable. 'noLimitOption' is set to 'false'
1760		// for the .val() method, except for pass 4/4.
1761		if ( noLimitOption !== false && options.limit && $Handles.length > 1 ) {
1762			to = trigger ? Math.min ( to, lowerLimit ) : Math.max( to, upperLimit );
1763		}
1764
1765		// Handle the step option.
1766		to = $Spectrum.getStep( to );
1767
1768		// Limit to 0/100 for .val input, trim anything beyond 7 digits, as
1769		// JavaScript has some issues in its floating point implementation.
1770		to = limit(parseFloat(to.toFixed(7)));
1771
1772		// Return false if handle can't move.
1773		if ( to === $Locations[trigger] ) {
1774			return false;
1775		}
1776
1777		// Set the handle to the new position.
1778		handle.css( options.style, to + '%' );
1779
1780		// Force proper handle stacking
1781		if ( handle.is(':first-child') ) {
1782			handle.toggleClass(Classes[17], to > 50 );
1783		}
1784
1785		// Update locations.
1786		$Locations[trigger] = to;
1787
1788		// Convert the value to the slider stepping/range.
1789		$Values[trigger] = $Spectrum.fromStepping( to );
1790
1791		linkUpdate(triggerPos[trigger]);
1792
1793		return true;
1794	}
1795
1796	// Loop values from value method and apply them.
1797	function setValues ( count, values ) {
1798
1799		var i, trigger, to;
1800
1801		// With the limit option, we'll need another limiting pass.
1802		if ( options.limit ) {
1803			count += 1;
1804		}
1805
1806		// If there are multiple handles to be set run the setting
1807		// mechanism twice for the first handle, to make sure it
1808		// can be bounced of the second one properly.
1809		for ( i = 0; i < count; i += 1 ) {
1810
1811			trigger = i%2;
1812
1813			// Get the current argument from the array.
1814			to = values[trigger];
1815
1816			// Setting with null indicates an 'ignore'.
1817			// Inputting 'false' is invalid.
1818			if ( to !== null && to !== false ) {
1819
1820				// If a formatted number was passed, attemt to decode it.
1821				if ( typeof to === 'number' ) {
1822					to = String(to);
1823				}
1824
1825				to = options.format.from( to );
1826
1827				// Request an update for all links if the value was invalid.
1828				// Do so too if setting the handle fails.
1829				if ( to === false || isNaN(to) || setHandle( $Handles[trigger], $Spectrum.toStepping( to ), i === (3 - options.dir) ) === false ) {
1830
1831					linkUpdate(triggerPos[trigger]);
1832				}
1833			}
1834		}
1835	}
1836
1837	// Set the slider value.
1838	function valueSet ( input ) {
1839
1840		// LibLink: don't accept new values when currently emitting changes.
1841		if ( $Target[0].LinkIsEmitting ) {
1842			return this;
1843		}
1844
1845		var count, values = asArray( input );
1846
1847		// The RTL settings is implemented by reversing the front-end,
1848		// internal mechanisms are the same.
1849		if ( options.dir && options.handles > 1 ) {
1850			values.reverse();
1851		}
1852
1853		// Animation is optional.
1854		// Make sure the initial values where set before using animated
1855		// placement. (no report, unit testing);
1856		if ( options.animate && $Locations[0] !== -1 ) {
1857			addClassFor( $Target, Classes[14], 300 );
1858		}
1859
1860		// Determine how often to set the handles.
1861		count = $Handles.length > 1 ? 3 : 1;
1862
1863		if ( values.length === 1 ) {
1864			count = 1;
1865		}
1866
1867		setValues ( count, values );
1868
1869		// Fire the 'set' event. As of noUiSlider 7,
1870		// this is no longer optional.
1871		fireEvents(['set']);
1872
1873		return this;
1874	}
1875
1876	// Get the slider value.
1877	function valueGet ( ) {
1878
1879		var i, retour = [];
1880
1881		// Get the value from all handles.
1882		for ( i = 0; i < options.handles; i += 1 ){
1883			retour[i] = options.format.to( $Values[i] );
1884		}
1885
1886		return inSliderOrder( retour );
1887	}
1888
1889	// Destroy the slider and unbind all events.
1890	function destroyTarget ( ) {
1891
1892		// Unbind events on the slider, remove all classes and child elements.
1893		$(this).off(namespace)
1894			.removeClass(Classes.join(' '))
1895			.empty();
1896
1897		delete this.LinkUpdate;
1898		delete this.LinkConfirm;
1899		delete this.LinkDefaultFormatter;
1900		delete this.LinkDefaultFlag;
1901		delete this.reappend;
1902		delete this.vGet;
1903		delete this.vSet;
1904		delete this.getCurrentStep;
1905		delete this.getInfo;
1906		delete this.destroy;
1907
1908		// Return the original options from the closure.
1909		return originalOptions;
1910	}
1911
1912	// Get the current step size for the slider.
1913	function getCurrentStep ( ) {
1914
1915		// Check all locations, map them to their stepping point.
1916		// Get the step point, then find it in the input list.
1917		var retour = $.map($Locations, function( location, index ){
1918
1919			var step = $Spectrum.getApplicableStep( location ),
1920
1921				// As per #391, the comparison for the decrement step can have some rounding issues.
1922				// Round the value to the precision used in the step.
1923				stepDecimals = countDecimals(String(step[2])),
1924
1925				// Get the current numeric value
1926				value = $Values[index],
1927
1928				// To move the slider 'one step up', the current step value needs to be added.
1929				// Use null if we are at the maximum slider value.
1930				increment = location === 100 ? null : step[2],
1931
1932				// Going 'one step down' might put the slider in a different sub-range, so we
1933				// need to switch between the current or the previous step.
1934				prev = Number((value - step[2]).toFixed(stepDecimals)),
1935
1936				// If the value fits the step, return the current step value. Otherwise, use the
1937				// previous step. Return null if the slider is at its minimum value.
1938				decrement = location === 0 ? null : (prev >= step[1]) ? step[2] : (step[0] || false);
1939
1940			return [[decrement, increment]];
1941		});
1942
1943		// Return values in the proper order.
1944		return inSliderOrder( retour );
1945	}
1946
1947	// Get the original set of options.
1948	function getOriginalOptions ( ) {
1949		return originalOptions;
1950	}
1951
1952
1953// Initialize slider
1954
1955	// Throw an error if the slider was already initialized.
1956	if ( $Target.hasClass(Classes[0]) ) {
1957		throw new Error('Slider was already initialized.');
1958	}
1959
1960	// Create the base element, initialise HTML and set classes.
1961	// Add handles and links.
1962	$Base = addSlider( options.dir, options.ort, $Target );
1963	$Handles = addHandles( options.handles, options.dir, $Base );
1964
1965	// Set the connect classes.
1966	addConnection ( options.connect, $Target, $Handles );
1967
1968	// Attach user events.
1969	events( options.events );
1970
1971// Methods
1972
1973	target.vSet = valueSet;
1974	target.vGet = valueGet;
1975	target.destroy = destroyTarget;
1976
1977	target.getCurrentStep = getCurrentStep;
1978	target.getOriginalOptions = getOriginalOptions;
1979
1980	target.getInfo = function(){
1981		return [
1982			$Spectrum,
1983			options.style,
1984			options.ort
1985		];
1986	};
1987
1988	// Use the public value method to set the start values.
1989	$Target.val( options.start );
1990
1991}
1992
1993
1994	// Run the standard initializer
1995	function initialize ( originalOptions ) {
1996
1997		// Test the options once, not for every slider.
1998		var options = testOptions( originalOptions, this );
1999
2000		// Loop all items, and provide a new closed-scope environment.
2001		return this.each(function(){
2002			closure(this, options, originalOptions);
2003		});
2004	}
2005
2006	// Destroy the slider, then re-enter initialization.
2007	function rebuild ( options ) {
2008
2009		return this.each(function(){
2010
2011			// The rebuild flag can be used if the slider wasn't initialized yet.
2012			if ( !this.destroy ) {
2013				$(this).noUiSlider( options );
2014				return;
2015			}
2016
2017			// Get the current values from the slider,
2018			// including the initialization options.
2019			var values = $(this).val(), originalOptions = this.destroy(),
2020
2021				// Extend the previous options with the newly provided ones.
2022				newOptions = $.extend( {}, originalOptions, options );
2023
2024			// Run the standard initializer.
2025			$(this).noUiSlider( newOptions );
2026
2027			// Place Link elements back.
2028			this.reappend();
2029
2030			// If the start option hasn't changed,
2031			// reset the previous values.
2032			if ( originalOptions.start === newOptions.start ) {
2033				$(this).val(values);
2034			}
2035		});
2036	}
2037
2038	// Access the internal getting and setting methods based on argument count.
2039	function value ( ) {
2040		return this[0][ !arguments.length ? 'vGet' : 'vSet' ].apply(this[0], arguments);
2041	}
2042
2043	// Override the .val() method. Test every element. Is it a slider? Go to
2044	// the slider value handling. No? Use the standard method.
2045	// Note how $.fn.val expects 'this' to be an instance of $. For convenience,
2046	// the above 'value' function does too.
2047	$.fn.val = function ( arg ) {
2048
2049		// this === instanceof $
2050
2051		function valMethod( a ){
2052			return a.hasClass(Classes[0]) ? value : $val;
2053		}
2054
2055		// If no value is passed, this is 'get'.
2056		if ( !arguments.length ) {
2057			var first = $(this[0]);
2058			return valMethod(first).call(first);
2059		}
2060
2061		var isFunction = $.isFunction(arg);
2062
2063		// Return the set so it remains chainable. Make sure not to break
2064		// jQuery's .val(function( index, value ){}) signature.
2065		return this.each(function( i ){
2066
2067			var val = arg, $t = $(this);
2068
2069			if ( isFunction ) {
2070				val = arg.call(this, i, $t.val());
2071			}
2072
2073			valMethod($t).call($t, val);
2074		});
2075	};
2076
2077// Extend jQuery/Zepto with the noUiSlider method.
2078	$.fn.noUiSlider = function ( options, rebuildFlag ) {
2079
2080		switch ( options ) {
2081			case 'step': return this[0].getCurrentStep();
2082			case 'options': return this[0].getOriginalOptions();
2083		}
2084
2085		return ( rebuildFlag ? rebuild : initialize ).call(this, options);
2086	};
2087
2088	function getGroup ( $Spectrum, mode, values, stepped ) {
2089
2090		// Use the range.
2091		if ( mode === 'range' || mode === 'steps' ) {
2092			return $Spectrum.xVal;
2093		}
2094
2095		if ( mode === 'count' ) {
2096
2097			// Divide 0 - 100 in 'count' parts.
2098			var spread = ( 100 / (values-1) ), v, i = 0;
2099			values = [];
2100
2101			// List these parts and have them handled as 'positions'.
2102			while ((v=i++*spread) <= 100 ) {
2103				values.push(v);
2104			}
2105
2106			mode = 'positions';
2107		}
2108
2109		if ( mode === 'positions' ) {
2110
2111			// Map all percentages to on-range values.
2112			return $.map(values, function( value ){
2113				return $Spectrum.fromStepping( stepped ? $Spectrum.getStep( value ) : value );
2114			});
2115		}
2116
2117		if ( mode === 'values' ) {
2118
2119			// If the value must be stepped, it needs to be converted to a percentage first.
2120			if ( stepped ) {
2121
2122				return $.map(values, function( value ){
2123
2124					// Convert to percentage, apply step, return to value.
2125					return $Spectrum.fromStepping( $Spectrum.getStep( $Spectrum.toStepping( value ) ) );
2126				});
2127
2128			}
2129
2130			// Otherwise, we can simply use the values.
2131			return values;
2132		}
2133	}
2134
2135	function generateSpread ( $Spectrum, density, mode, group ) {
2136
2137		var originalSpectrumDirection = $Spectrum.direction,
2138			indexes = {},
2139			firstInRange = $Spectrum.xVal[0],
2140			lastInRange = $Spectrum.xVal[$Spectrum.xVal.length-1],
2141			ignoreFirst = false,
2142			ignoreLast = false,
2143			prevPct = 0;
2144
2145		// This function loops the spectrum in an ltr linear fashion,
2146		// while the toStepping method is direction aware. Trick it into
2147		// believing it is ltr.
2148		$Spectrum.direction = 0;
2149
2150		// Create a copy of the group, sort it and filter away all duplicates.
2151		group = unique(group.slice().sort(function(a, b){ return a - b; }));
2152
2153		// Make sure the range starts with the first element.
2154		if ( group[0] !== firstInRange ) {
2155			group.unshift(firstInRange);
2156			ignoreFirst = true;
2157		}
2158
2159		// Likewise for the last one.
2160		if ( group[group.length - 1] !== lastInRange ) {
2161			group.push(lastInRange);
2162			ignoreLast = true;
2163		}
2164
2165		$.each(group, function ( index ) {
2166
2167			// Get the current step and the lower + upper positions.
2168			var step, i, q,
2169				low = group[index],
2170				high = group[index+1],
2171				newPct, pctDifference, pctPos, type,
2172				steps, realSteps, stepsize;
2173
2174			// When using 'steps' mode, use the provided steps.
2175			// Otherwise, we'll step on to the next subrange.
2176			if ( mode === 'steps' ) {
2177				step = $Spectrum.xNumSteps[ index ];
2178			}
2179
2180			// Default to a 'full' step.
2181			if ( !step ) {
2182				step = high-low;
2183			}
2184
2185			// Low can be 0, so test for false. If high is undefined,
2186			// we are at the last subrange. Index 0 is already handled.
2187			if ( low === false || high === undefined ) {
2188				return;
2189			}
2190
2191			// Find all steps in the subrange.
2192			for ( i = low; i <= high; i += step ) {
2193
2194				// Get the percentage value for the current step,
2195				// calculate the size for the subrange.
2196				newPct = $Spectrum.toStepping( i );
2197				pctDifference = newPct - prevPct;
2198
2199				steps = pctDifference / density;
2200				realSteps = Math.round(steps);
2201
2202				// This ratio represents the ammount of percentage-space a point indicates.
2203				// For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided.
2204				// Round the percentage offset to an even number, then divide by two
2205				// to spread the offset on both sides of the range.
2206				stepsize = pctDifference/realSteps;
2207
2208				// Divide all points evenly, adding the correct number to this subrange.
2209				// Run up to <= so that 100% gets a point, event if ignoreLast is set.
2210				for ( q = 1; q <= realSteps; q += 1 ) {
2211
2212					// The ratio between the rounded value and the actual size might be ~1% off.
2213					// Correct the percentage offset by the number of points
2214					// per subrange. density = 1 will result in 100 points on the
2215					// full range, 2 for 50, 4 for 25, etc.
2216					pctPos = prevPct + ( q * stepsize );
2217					indexes[pctPos.toFixed(5)] = ['x', 0];
2218				}
2219
2220				// Determine the point type.
2221				type = ($.inArray(i, group) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 );
2222
2223				// Enforce the 'ignoreFirst' option by overwriting the type for 0.
2224				if ( !index && ignoreFirst ) {
2225					type = 0;
2226				}
2227
2228				if ( !(i === high && ignoreLast)) {
2229					// Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value.
2230					indexes[newPct.toFixed(5)] = [i, type];
2231				}
2232
2233				// Update the percentage count.
2234				prevPct = newPct;
2235			}
2236		});
2237
2238		// Reset the spectrum.
2239		$Spectrum.direction = originalSpectrumDirection;
2240
2241		return indexes;
2242	}
2243
2244	function addMarking ( CSSstyle, orientation, direction, spread, filterFunc, formatter ) {
2245
2246		var style = ['horizontal', 'vertical'][orientation],
2247			element = $('<div/>');
2248
2249		element.addClass('noUi-pips noUi-pips-'+style);
2250
2251		function getSize( type, value ){
2252			return [ '-normal', '-large', '-sub' ][type];
2253		}
2254
2255		function getTags( offset, source, values ) {
2256			return 'class="' + source + ' ' +
2257				source + '-' + style + ' ' +
2258				source + getSize(values[1], values[0]) +
2259				'" style="' + CSSstyle + ': ' + offset + '%"';
2260		}
2261
2262		function addSpread ( offset, values ){
2263
2264			if ( direction ) {
2265				offset = 100 - offset;
2266			}
2267
2268			// Apply the filter function, if it is set.
2269			values[1] = (values[1] && filterFunc) ? filterFunc(values[0], values[1]) : values[1];
2270
2271			// Add a marker for every point
2272			element.append('<div ' + getTags(offset, 'noUi-marker', values) + '></div>');
2273
2274			// Values are only appended for points marked '1' or '2'.
2275			if ( values[1] ) {
2276				element.append('<div '+getTags(offset, 'noUi-value', values)+'>' + formatter.to(values[0]) + '</div>');
2277			}
2278		}
2279
2280		// Append all points.
2281		$.each(spread, addSpread);
2282
2283		return element;
2284	}
2285
2286	$.fn.noUiSlider_pips = function ( grid ) {
2287
2288	var mode = grid.mode,
2289		density = grid.density || 1,
2290		filter = grid.filter || false,
2291		values = grid.values || false,
2292		format = grid.format || {
2293			to: Math.round
2294		},
2295		stepped = grid.stepped || false;
2296
2297		return this.each(function(){
2298
2299		var info = this.getInfo(),
2300			group = getGroup( info[0], mode, values, stepped ),
2301			spread = generateSpread( info[0], density, mode, group );
2302
2303			return $(this).append(addMarking(
2304				info[1],
2305				info[2],
2306				info[0].direction,
2307				spread,
2308				filter,
2309				format
2310			));
2311		});
2312	};
2313
2314}( window.jQuery || window.Zepto ));

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.