PageSourceSearch

https://www.lydfordmotorcompany.co.uk/asset/js/plugins/range_slider.js

js lydfordmotorcompany.co.uk collected 2026-10-04 08:44:07 UTC 68,731 bytes, 1,944 lines download raw bytes

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

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