nouislider-rails 8.0.2 → 8.2.1

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