pghero 2.8.3 → 3.3.3

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