PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / trunk
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript vtrunk
4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.0.30 4.0.29 4.0.28 4.0.27 4.0.26 4.0.24 4.0.25 4.0.23 4.0.22 4.0.21 4.0.19 4.0.18 4.0.17 4.0.16 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 All 169 releases
searchpro / admin / js / bootstrap-slider.js

bootstrap-slider.js in BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript trunk, at admin/js/bootstrap-slider.js

2,058 lines 69.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 "use strict";
2
3 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
4
5 /*! =========================================================
6 * bootstrap-slider.js
7 *
8 * Maintainers:
9 * Kyle Kemp
10 * - Twitter: @seiyria
11 * - Github: seiyria
12 * Rohit Kalkur
13 * - Twitter: @Rovolutionary
14 * - Github: rovolution
15 *
16 * =========================================================
17 *
18 * bootstrap-slider is released under the MIT License
19 * Copyright (c) 2019 Kyle Kemp, Rohit Kalkur, and contributors
20 *
21 * Permission is hereby granted, free of charge, to any person
22 * obtaining a copy of this software and associated documentation
23 * files (the "Software"), to deal in the Software without
24 * restriction, including without limitation the rights to use,
25 * copy, modify, merge, publish, distribute, sublicense, and/or sell
26 * copies of the Software, and to permit persons to whom the
27 * Software is furnished to do so, subject to the following
28 * conditions:
29 *
30 * The above copyright notice and this permission notice shall be
31 * included in all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
35 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
36 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
37 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
38 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
39 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
40 * OTHER DEALINGS IN THE SOFTWARE.
41 *
42 * ========================================================= */
43
44 /**
45 * Bridget makes jQuery widgets
46 * v1.0.1
47 * MIT license
48 */
49 var windowIsDefined = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object";
50
51 (function (factory) {
52 if (typeof define === "function" && define.amd) {
53 define(["jquery"], factory);
54 } else if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" && module.exports) {
55 var jQuery;
56 try {
57 jQuery = require("jquery");
58 } catch (err) {
59 jQuery = null;
60 }
61 module.exports = factory(jQuery);
62 } else if (window) {
63 window.Slider = factory(window.jQuery);
64 }
65 })(function ($) {
66 // Constants
67 var NAMESPACE_MAIN = 'slider';
68 var NAMESPACE_ALTERNATE = 'bootstrapSlider';
69
70 // Polyfill console methods
71 if (windowIsDefined && !window.console) {
72 window.console = {};
73 }
74 if (windowIsDefined && !window.console.log) {
75 window.console.log = function () {};
76 }
77 if (windowIsDefined && !window.console.warn) {
78 window.console.warn = function () {};
79 }
80
81 // Reference to Slider constructor
82 var Slider;
83
84 (function ($) {
85
86 'use strict';
87
88 // -------------------------- utils -------------------------- //
89
90 var slice = Array.prototype.slice;
91
92 function noop() {}
93
94 // -------------------------- definition -------------------------- //
95
96 function defineBridget($) {
97
98 // bail if no jQuery
99 if (!$) {
100 return;
101 }
102
103 // -------------------------- addOptionMethod -------------------------- //
104
105 /**
106 * adds option method -> $().plugin('option', {...})
107 * @param {Function} PluginClass - constructor class
108 */
109 function addOptionMethod(PluginClass) {
110 // don't overwrite original option method
111 if (PluginClass.prototype.option) {
112 return;
113 }
114
115 // option setter
116 PluginClass.prototype.option = function (opts) {
117 // bail out if not an object
118 if (!$.isPlainObject(opts)) {
119 return;
120 }
121 this.options = $.extend(true, this.options, opts);
122 };
123 }
124
125 // -------------------------- plugin bridge -------------------------- //
126
127 // helper function for logging errors
128 // $.error breaks jQuery chaining
129 var logError = typeof console === 'undefined' ? noop : function (message) {
130 console.error(message);
131 };
132
133 /**
134 * jQuery plugin bridge, access methods like $elem.plugin('method')
135 * @param {String} namespace - plugin name
136 * @param {Function} PluginClass - constructor class
137 */
138 function bridge(namespace, PluginClass) {
139 // add to jQuery fn namespace
140 $.fn[namespace] = function (options) {
141 if (typeof options === 'string') {
142 // call plugin method when first argument is a string
143 // get arguments for method
144 var args = slice.call(arguments, 1);
145
146 for (var i = 0, len = this.length; i < len; i++) {
147 var elem = this[i];
148 var instance = $.data(elem, namespace);
149 if (!instance) {
150 logError("cannot call methods on " + namespace + " prior to initialization; " + "attempted to call '" + options + "'");
151 continue;
152 }
153 if (!$.isFunction(instance[options]) || options.charAt(0) === '_') {
154 logError("no such method '" + options + "' for " + namespace + " instance");
155 continue;
156 }
157
158 // trigger method with arguments
159 var returnValue = instance[options].apply(instance, args);
160
161 // break look and return first value if provided
162 if (returnValue !== undefined && returnValue !== instance) {
163 return returnValue;
164 }
165 }
166 // return this if no return value
167 return this;
168 } else {
169 var objects = this.map(function () {
170 var instance = $.data(this, namespace);
171 if (instance) {
172 // apply options & init
173 instance.option(options);
174 instance._init();
175 } else {
176 // initialize new instance
177 instance = new PluginClass(this, options);
178 $.data(this, namespace, instance);
179 }
180 return $(this);
181 });
182
183 if (objects.length === 1) {
184 return objects[0];
185 }
186 return objects;
187 }
188 };
189 }
190
191 // -------------------------- bridget -------------------------- //
192
193 /**
194 * converts a Prototypical class into a proper jQuery plugin
195 * the class must have a ._init method
196 * @param {String} namespace - plugin name, used in $().pluginName
197 * @param {Function} PluginClass - constructor class
198 */
199 $.bridget = function (namespace, PluginClass) {
200 addOptionMethod(PluginClass);
201 bridge(namespace, PluginClass);
202 };
203
204 return $.bridget;
205 }
206
207 // get jquery from browser global
208 defineBridget($);
209 })($);
210
211 /*************************************************
212 BOOTSTRAP-SLIDER SOURCE CODE
213 **************************************************/
214
215 (function ($) {
216 var autoRegisterNamespace = void 0;
217
218 var ErrorMsgs = {
219 formatInvalidInputErrorMsg: function formatInvalidInputErrorMsg(input) {
220 return "Invalid input value '" + input + "' passed in";
221 },
222 callingContextNotSliderInstance: "Calling context element does not have instance of Slider bound to it. Check your code to make sure the JQuery object returned from the call to the slider() initializer is calling the method"
223 };
224
225 var SliderScale = {
226 linear: {
227 getValue: function getValue(value, options) {
228 if (value < options.min) {
229 return options.min;
230 } else if (value > options.max) {
231 return options.max;
232 } else {
233 return value;
234 }
235 },
236 toValue: function toValue(percentage) {
237 var rawValue = percentage / 100 * (this.options.max - this.options.min);
238 var shouldAdjustWithBase = true;
239 if (this.options.ticks_positions.length > 0) {
240 var minv,
241 maxv,
242 minp,
243 maxp = 0;
244 for (var i = 1; i < this.options.ticks_positions.length; i++) {
245 if (percentage <= this.options.ticks_positions[i]) {
246 minv = this.options.ticks[i - 1];
247 minp = this.options.ticks_positions[i - 1];
248 maxv = this.options.ticks[i];
249 maxp = this.options.ticks_positions[i];
250
251 break;
252 }
253 }
254 var partialPercentage = (percentage - minp) / (maxp - minp);
255 rawValue = minv + partialPercentage * (maxv - minv);
256 shouldAdjustWithBase = false;
257 }
258
259 var adjustment = shouldAdjustWithBase ? this.options.min : 0;
260 var value = adjustment + Math.round(rawValue / this.options.step) * this.options.step;
261 return SliderScale.linear.getValue(value, this.options);
262 },
263 toPercentage: function toPercentage(value) {
264 if (this.options.max === this.options.min) {
265 return 0;
266 }
267
268 if (this.options.ticks_positions.length > 0) {
269 var minv,
270 maxv,
271 minp,
272 maxp = 0;
273 for (var i = 0; i < this.options.ticks.length; i++) {
274 if (value <= this.options.ticks[i]) {
275 minv = i > 0 ? this.options.ticks[i - 1] : 0;
276 minp = i > 0 ? this.options.ticks_positions[i - 1] : 0;
277 maxv = this.options.ticks[i];
278 maxp = this.options.ticks_positions[i];
279
280 break;
281 }
282 }
283 if (i > 0) {
284 var partialPercentage = (value - minv) / (maxv - minv);
285 return minp + partialPercentage * (maxp - minp);
286 }
287 }
288
289 return 100 * (value - this.options.min) / (this.options.max - this.options.min);
290 }
291 },
292
293 logarithmic: {
294 /* Based on http://stackoverflow.com/questions/846221/logarithmic-slider */
295 toValue: function toValue(percentage) {
296 var offset = 1 - this.options.min;
297 var min = Math.log(this.options.min + offset);
298 var max = Math.log(this.options.max + offset);
299 var value = Math.exp(min + (max - min) * percentage / 100) - offset;
300 if (Math.round(value) === max) {
301 return max;
302 }
303 value = this.options.min + Math.round((value - this.options.min) / this.options.step) * this.options.step;
304 /* Rounding to the nearest step could exceed the min or
305 * max, so clip to those values. */
306 return SliderScale.linear.getValue(value, this.options);
307 },
308 toPercentage: function toPercentage(value) {
309 if (this.options.max === this.options.min) {
310 return 0;
311 } else {
312 var offset = 1 - this.options.min;
313 var max = Math.log(this.options.max + offset);
314 var min = Math.log(this.options.min + offset);
315 var v = Math.log(value + offset);
316 return 100 * (v - min) / (max - min);
317 }
318 }
319 }
320 };
321
322 /*************************************************
323 CONSTRUCTOR
324 **************************************************/
325 Slider = function Slider(element, options) {
326 createNewSlider.call(this, element, options);
327 return this;
328 };
329
330 function createNewSlider(element, options) {
331
332 /*
333 The internal state object is used to store data about the current 'state' of slider.
334 This includes values such as the `value`, `enabled`, etc...
335 */
336 this._state = {
337 value: null,
338 enabled: null,
339 offset: null,
340 size: null,
341 percentage: null,
342 inDrag: false,
343 over: false,
344 tickIndex: null
345 };
346
347 // The objects used to store the reference to the tick methods if ticks_tooltip is on
348 this.ticksCallbackMap = {};
349 this.handleCallbackMap = {};
350
351 if (typeof element === "string") {
352 this.element = document.querySelector(element);
353 } else if (element instanceof HTMLElement) {
354 this.element = element;
355 }
356
357 /*************************************************
358 Process Options
359 **************************************************/
360 options = options ? options : {};
361 var optionTypes = Object.keys(this.defaultOptions);
362
363 var isMinSet = options.hasOwnProperty('min');
364 var isMaxSet = options.hasOwnProperty('max');
365
366 for (var i = 0; i < optionTypes.length; i++) {
367 var optName = optionTypes[i];
368
369 // First check if an option was passed in via the constructor
370 var val = options[optName];
371 // If no data attrib, then check data atrributes
372 val = typeof val !== 'undefined' ? val : getDataAttrib(this.element, optName);
373 // Finally, if nothing was specified, use the defaults
374 val = val !== null ? val : this.defaultOptions[optName];
375
376 // Set all options on the instance of the Slider
377 if (!this.options) {
378 this.options = {};
379 }
380 this.options[optName] = val;
381 }
382
383 this.ticksAreValid = Array.isArray(this.options.ticks) && this.options.ticks.length > 0;
384
385 // Lock to ticks only when ticks[] is defined and set
386 if (!this.ticksAreValid) {
387 this.options.lock_to_ticks = false;
388 }
389
390 // Check options.rtl
391 if (this.options.rtl === 'auto') {
392 var computedStyle = window.getComputedStyle(this.element);
393 if (computedStyle != null) {
394 this.options.rtl = computedStyle.direction === 'rtl';
395 } else {
396 // Fix for Firefox bug in versions less than 62:
397 // https://bugzilla.mozilla.org/show_bug.cgi?id=548397
398 // https://bugzilla.mozilla.org/show_bug.cgi?id=1467722
399 this.options.rtl = this.element.style.direction === 'rtl';
400 }
401 }
402
403 /*
404 Validate `tooltip_position` against 'orientation`
405 - if `tooltip_position` is incompatible with orientation, switch it to a default compatible with specified `orientation`
406 -- default for "vertical" -> "right", "left" if rtl
407 -- default for "horizontal" -> "top"
408 */
409 if (this.options.orientation === "vertical" && (this.options.tooltip_position === "top" || this.options.tooltip_position === "bottom")) {
410 if (this.options.rtl) {
411 this.options.tooltip_position = "left";
412 } else {
413 this.options.tooltip_position = "right";
414 }
415 } else if (this.options.orientation === "horizontal" && (this.options.tooltip_position === "left" || this.options.tooltip_position === "right")) {
416
417 this.options.tooltip_position = "top";
418 }
419
420 function getDataAttrib(element, optName) {
421 var dataName = "data-slider-" + optName.replace(/_/g, '-');
422 var dataValString = element.getAttribute(dataName);
423
424 try {
425 return JSON.parse(dataValString);
426 } catch (err) {
427 return dataValString;
428 }
429 }
430
431 /*************************************************
432 Create Markup
433 **************************************************/
434
435 var origWidth = this.element.style.width;
436 var updateSlider = false;
437 var parent = this.element.parentNode;
438 var sliderTrackSelection;
439 var sliderTrackLow, sliderTrackHigh;
440 var sliderMinHandle;
441 var sliderMaxHandle;
442
443 if (this.sliderElem) {
444 updateSlider = true;
445 } else {
446 /* Create elements needed for slider */
447 this.sliderElem = document.createElement("div");
448 this.sliderElem.className = "slider";
449
450 /* Create slider track elements */
451 var sliderTrack = document.createElement("div");
452 sliderTrack.className = "slider-track";
453
454 sliderTrackLow = document.createElement("div");
455 sliderTrackLow.className = "slider-track-low";
456
457 sliderTrackSelection = document.createElement("div");
458 sliderTrackSelection.className = "slider-selection";
459
460 sliderTrackHigh = document.createElement("div");
461 sliderTrackHigh.className = "slider-track-high";
462
463 sliderMinHandle = document.createElement("div");
464 sliderMinHandle.className = "slider-handle min-slider-handle";
465 sliderMinHandle.setAttribute('role', 'slider');
466 sliderMinHandle.setAttribute('aria-valuemin', this.options.min);
467 sliderMinHandle.setAttribute('aria-valuemax', this.options.max);
468
469 sliderMaxHandle = document.createElement("div");
470 sliderMaxHandle.className = "slider-handle max-slider-handle";
471 sliderMaxHandle.setAttribute('role', 'slider');
472 sliderMaxHandle.setAttribute('aria-valuemin', this.options.min);
473 sliderMaxHandle.setAttribute('aria-valuemax', this.options.max);
474
475 sliderTrack.appendChild(sliderTrackLow);
476 sliderTrack.appendChild(sliderTrackSelection);
477 sliderTrack.appendChild(sliderTrackHigh);
478
479 /* Create highlight range elements */
480 this.rangeHighlightElements = [];
481 var rangeHighlightsOpts = this.options.rangeHighlights;
482 if (Array.isArray(rangeHighlightsOpts) && rangeHighlightsOpts.length > 0) {
483 for (var j = 0; j < rangeHighlightsOpts.length; j++) {
484 var rangeHighlightElement = document.createElement("div");
485 var customClassString = rangeHighlightsOpts[j].class || "";
486 rangeHighlightElement.className = "slider-rangeHighlight slider-selection " + customClassString;
487 this.rangeHighlightElements.push(rangeHighlightElement);
488 sliderTrack.appendChild(rangeHighlightElement);
489 }
490 }
491
492 /* Add aria-labelledby to handle's */
493 var isLabelledbyArray = Array.isArray(this.options.labelledby);
494 if (isLabelledbyArray && this.options.labelledby[0]) {
495 sliderMinHandle.setAttribute('aria-labelledby', this.options.labelledby[0]);
496 }
497 if (isLabelledbyArray && this.options.labelledby[1]) {
498 sliderMaxHandle.setAttribute('aria-labelledby', this.options.labelledby[1]);
499 }
500 if (!isLabelledbyArray && this.options.labelledby) {
501 sliderMinHandle.setAttribute('aria-labelledby', this.options.labelledby);
502 sliderMaxHandle.setAttribute('aria-labelledby', this.options.labelledby);
503 }
504
505 /* Create ticks */
506 this.ticks = [];
507 if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {
508 this.ticksContainer = document.createElement('div');
509 this.ticksContainer.className = 'slider-tick-container';
510
511 for (i = 0; i < this.options.ticks.length; i++) {
512 var tick = document.createElement('div');
513 tick.className = 'slider-tick';
514 if (this.options.ticks_tooltip) {
515 var tickListenerReference = this._addTickListener();
516 var enterCallback = tickListenerReference.addMouseEnter(this, tick, i);
517 var leaveCallback = tickListenerReference.addMouseLeave(this, tick);
518
519 this.ticksCallbackMap[i] = {
520 mouseEnter: enterCallback,
521 mouseLeave: leaveCallback
522 };
523 }
524 this.ticks.push(tick);
525 this.ticksContainer.appendChild(tick);
526 }
527
528 sliderTrackSelection.className += " tick-slider-selection";
529 }
530
531 this.tickLabels = [];
532 if (Array.isArray(this.options.ticks_labels) && this.options.ticks_labels.length > 0) {
533 this.tickLabelContainer = document.createElement('div');
534 this.tickLabelContainer.className = 'slider-tick-label-container';
535
536 for (i = 0; i < this.options.ticks_labels.length; i++) {
537 var label = document.createElement('div');
538 var noTickPositionsSpecified = this.options.ticks_positions.length === 0;
539 var tickLabelsIndex = this.options.reversed && noTickPositionsSpecified ? this.options.ticks_labels.length - (i + 1) : i;
540 label.className = 'slider-tick-label';
541 label.innerHTML = this.options.ticks_labels[tickLabelsIndex];
542
543 this.tickLabels.push(label);
544 this.tickLabelContainer.appendChild(label);
545 }
546 }
547
548 var createAndAppendTooltipSubElements = function createAndAppendTooltipSubElements(tooltipElem) {
549 var arrow = document.createElement("div");
550 arrow.className = "arrow";
551
552 var inner = document.createElement("div");
553 inner.className = "tooltip-inner";
554
555 tooltipElem.appendChild(arrow);
556 tooltipElem.appendChild(inner);
557 };
558
559 /* Create tooltip elements */
560 var sliderTooltip = document.createElement("div");
561 sliderTooltip.className = "tooltip tooltip-main";
562 sliderTooltip.setAttribute('role', 'presentation');
563 createAndAppendTooltipSubElements(sliderTooltip);
564
565 var sliderTooltipMin = document.createElement("div");
566 sliderTooltipMin.className = "tooltip tooltip-min";
567 sliderTooltipMin.setAttribute('role', 'presentation');
568 createAndAppendTooltipSubElements(sliderTooltipMin);
569
570 var sliderTooltipMax = document.createElement("div");
571 sliderTooltipMax.className = "tooltip tooltip-max";
572 sliderTooltipMax.setAttribute('role', 'presentation');
573 createAndAppendTooltipSubElements(sliderTooltipMax);
574
575 /* Append components to sliderElem */
576 this.sliderElem.appendChild(sliderTrack);
577 this.sliderElem.appendChild(sliderTooltip);
578 this.sliderElem.appendChild(sliderTooltipMin);
579 this.sliderElem.appendChild(sliderTooltipMax);
580
581 if (this.tickLabelContainer) {
582 this.sliderElem.appendChild(this.tickLabelContainer);
583 }
584 if (this.ticksContainer) {
585 this.sliderElem.appendChild(this.ticksContainer);
586 }
587
588 this.sliderElem.appendChild(sliderMinHandle);
589 this.sliderElem.appendChild(sliderMaxHandle);
590
591 /* Append slider element to parent container, right before the original <input> element */
592 parent.insertBefore(this.sliderElem, this.element);
593
594 /* Hide original <input> element */
595 this.element.style.display = "none";
596 }
597 /* If JQuery exists, cache JQ references */
598 if ($) {
599 this.$element = $(this.element);
600 this.$sliderElem = $(this.sliderElem);
601 }
602
603 /*************************************************
604 Setup
605 **************************************************/
606 this.eventToCallbackMap = {};
607 this.sliderElem.id = this.options.id;
608
609 this.touchCapable = 'ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch;
610
611 this.touchX = 0;
612 this.touchY = 0;
613
614 this.tooltip = this.sliderElem.querySelector('.tooltip-main');
615 this.tooltipInner = this.tooltip.querySelector('.tooltip-inner');
616
617 this.tooltip_min = this.sliderElem.querySelector('.tooltip-min');
618 this.tooltipInner_min = this.tooltip_min.querySelector('.tooltip-inner');
619
620 this.tooltip_max = this.sliderElem.querySelector('.tooltip-max');
621 this.tooltipInner_max = this.tooltip_max.querySelector('.tooltip-inner');
622
623 if (SliderScale[this.options.scale]) {
624 this.options.scale = SliderScale[this.options.scale];
625 }
626
627 if (updateSlider === true) {
628 // Reset classes
629 this._removeClass(this.sliderElem, 'slider-horizontal');
630 this._removeClass(this.sliderElem, 'slider-vertical');
631 this._removeClass(this.sliderElem, 'slider-rtl');
632 this._removeClass(this.tooltip, 'hide');
633 this._removeClass(this.tooltip_min, 'hide');
634 this._removeClass(this.tooltip_max, 'hide');
635
636 // Undo existing inline styles for track
637 ["left", "right", "top", "width", "height"].forEach(function (prop) {
638 this._removeProperty(this.trackLow, prop);
639 this._removeProperty(this.trackSelection, prop);
640 this._removeProperty(this.trackHigh, prop);
641 }, this);
642
643 // Undo inline styles on handles
644 [this.handle1, this.handle2].forEach(function (handle) {
645 this._removeProperty(handle, 'left');
646 this._removeProperty(handle, 'right');
647 this._removeProperty(handle, 'top');
648 }, this);
649
650 // Undo inline styles and classes on tooltips
651 [this.tooltip, this.tooltip_min, this.tooltip_max].forEach(function (tooltip) {
652 this._removeProperty(tooltip, 'bs-tooltip-left');
653 this._removeProperty(tooltip, 'bs-tooltip-right');
654 this._removeProperty(tooltip, 'bs-tooltip-top');
655
656 this._removeClass(tooltip, 'bs-tooltip-right');
657 this._removeClass(tooltip, 'bs-tooltip-left');
658 this._removeClass(tooltip, 'bs-tooltip-top');
659 }, this);
660 }
661
662 if (this.options.orientation === 'vertical') {
663 this._addClass(this.sliderElem, 'slider-vertical');
664 this.stylePos = 'top';
665 this.mousePos = 'pageY';
666 this.sizePos = 'offsetHeight';
667 } else {
668 this._addClass(this.sliderElem, 'slider-horizontal');
669 this.sliderElem.style.width = origWidth;
670 this.options.orientation = 'horizontal';
671 if (this.options.rtl) {
672 this.stylePos = 'right';
673 } else {
674 this.stylePos = 'left';
675 }
676 this.mousePos = 'clientX';
677 this.sizePos = 'offsetWidth';
678 }
679 // specific rtl class
680 if (this.options.rtl) {
681 this._addClass(this.sliderElem, 'slider-rtl');
682 }
683 this._setTooltipPosition();
684 /* In case ticks are specified, overwrite the min and max bounds */
685 if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {
686 if (!isMaxSet) {
687 this.options.max = Math.max.apply(Math, this.options.ticks);
688 }
689 if (!isMinSet) {
690 this.options.min = Math.min.apply(Math, this.options.ticks);
691 }
692 }
693
694 if (Array.isArray(this.options.value)) {
695 this.options.range = true;
696 this._state.value = this.options.value;
697 } else if (this.options.range) {
698 // User wants a range, but value is not an array
699 this._state.value = [this.options.value, this.options.max];
700 } else {
701 this._state.value = this.options.value;
702 }
703
704 this.trackLow = sliderTrackLow || this.trackLow;
705 this.trackSelection = sliderTrackSelection || this.trackSelection;
706 this.trackHigh = sliderTrackHigh || this.trackHigh;
707
708 if (this.options.selection === 'none') {
709 this._addClass(this.trackLow, 'hide');
710 this._addClass(this.trackSelection, 'hide');
711 this._addClass(this.trackHigh, 'hide');
712 } else if (this.options.selection === 'after' || this.options.selection === 'before') {
713 this._removeClass(this.trackLow, 'hide');
714 this._removeClass(this.trackSelection, 'hide');
715 this._removeClass(this.trackHigh, 'hide');
716 }
717
718 this.handle1 = sliderMinHandle || this.handle1;
719 this.handle2 = sliderMaxHandle || this.handle2;
720
721 if (updateSlider === true) {
722 // Reset classes
723 this._removeClass(this.handle1, 'round triangle');
724 this._removeClass(this.handle2, 'round triangle hide');
725
726 for (i = 0; i < this.ticks.length; i++) {
727 this._removeClass(this.ticks[i], 'round triangle hide');
728 }
729 }
730
731 var availableHandleModifiers = ['round', 'triangle', 'custom'];
732 var isValidHandleType = availableHandleModifiers.indexOf(this.options.handle) !== -1;
733 if (isValidHandleType) {
734 this._addClass(this.handle1, this.options.handle);
735 this._addClass(this.handle2, this.options.handle);
736
737 for (i = 0; i < this.ticks.length; i++) {
738 this._addClass(this.ticks[i], this.options.handle);
739 }
740 }
741
742 this._state.offset = this._offset(this.sliderElem);
743 this._state.size = this.sliderElem[this.sizePos];
744 this.setValue(this._state.value);
745
746 /******************************************
747 Bind Event Listeners
748 ******************************************/
749
750 // Bind keyboard handlers
751 this.handle1Keydown = this._keydown.bind(this, 0);
752 this.handle1.addEventListener("keydown", this.handle1Keydown, false);
753
754 this.handle2Keydown = this._keydown.bind(this, 1);
755 this.handle2.addEventListener("keydown", this.handle2Keydown, false);
756
757 this.mousedown = this._mousedown.bind(this);
758 this.touchstart = this._touchstart.bind(this);
759 this.touchmove = this._touchmove.bind(this);
760
761 if (this.touchCapable) {
762 this.sliderElem.addEventListener("touchstart", this.touchstart, false);
763 this.sliderElem.addEventListener("touchmove", this.touchmove, false);
764 }
765
766 this.sliderElem.addEventListener("mousedown", this.mousedown, false);
767
768 // Bind window handlers
769 this.resize = this._resize.bind(this);
770 window.addEventListener("resize", this.resize, false);
771
772 // Bind tooltip-related handlers
773 if (this.options.tooltip === 'hide') {
774 this._addClass(this.tooltip, 'hide');
775 this._addClass(this.tooltip_min, 'hide');
776 this._addClass(this.tooltip_max, 'hide');
777 } else if (this.options.tooltip === 'always') {
778 this._showTooltip();
779 this._alwaysShowTooltip = true;
780 } else {
781 this.showTooltip = this._showTooltip.bind(this);
782 this.hideTooltip = this._hideTooltip.bind(this);
783
784 if (this.options.ticks_tooltip) {
785 var callbackHandle = this._addTickListener();
786 //create handle1 listeners and store references in map
787 var mouseEnter = callbackHandle.addMouseEnter(this, this.handle1);
788 var mouseLeave = callbackHandle.addMouseLeave(this, this.handle1);
789 this.handleCallbackMap.handle1 = {
790 mouseEnter: mouseEnter,
791 mouseLeave: mouseLeave
792 };
793 //create handle2 listeners and store references in map
794 mouseEnter = callbackHandle.addMouseEnter(this, this.handle2);
795 mouseLeave = callbackHandle.addMouseLeave(this, this.handle2);
796 this.handleCallbackMap.handle2 = {
797 mouseEnter: mouseEnter,
798 mouseLeave: mouseLeave
799 };
800 } else {
801 this.sliderElem.addEventListener("mouseenter", this.showTooltip, false);
802 this.sliderElem.addEventListener("mouseleave", this.hideTooltip, false);
803
804 if (this.touchCapable) {
805 this.sliderElem.addEventListener("touchstart", this.showTooltip, false);
806 this.sliderElem.addEventListener("touchmove", this.showTooltip, false);
807 this.sliderElem.addEventListener("touchend", this.hideTooltip, false);
808 }
809 }
810
811 this.handle1.addEventListener("focus", this.showTooltip, false);
812 this.handle1.addEventListener("blur", this.hideTooltip, false);
813
814 this.handle2.addEventListener("focus", this.showTooltip, false);
815 this.handle2.addEventListener("blur", this.hideTooltip, false);
816
817 if (this.touchCapable) {
818 this.handle1.addEventListener("touchstart", this.showTooltip, false);
819 this.handle1.addEventListener("touchmove", this.showTooltip, false);
820 this.handle1.addEventListener("touchend", this.hideTooltip, false);
821
822 this.handle2.addEventListener("touchstart", this.showTooltip, false);
823 this.handle2.addEventListener("touchmove", this.showTooltip, false);
824 this.handle2.addEventListener("touchend", this.hideTooltip, false);
825 }
826 }
827
828 if (this.options.enabled) {
829 this.enable();
830 } else {
831 this.disable();
832 }
833 }
834
835 /*************************************************
836 INSTANCE PROPERTIES/METHODS
837 - Any methods bound to the prototype are considered
838 part of the plugin's `public` interface
839 **************************************************/
840 Slider.prototype = {
841 _init: function _init() {}, // NOTE: Must exist to support bridget
842
843 constructor: Slider,
844
845 defaultOptions: {
846 id: "",
847 min: 0,
848 max: 10,
849 step: 1,
850 precision: 0,
851 orientation: 'horizontal',
852 value: 5,
853 range: false,
854 selection: 'before',
855 tooltip: 'show',
856 tooltip_split: false,
857 lock_to_ticks: false,
858 handle: 'round',
859 reversed: false,
860 rtl: 'auto',
861 enabled: true,
862 formatter: function formatter(val) {
863 if (Array.isArray(val)) {
864 return val[0] + " : " + val[1];
865 } else {
866 return val;
867 }
868 },
869 natural_arrow_keys: false,
870 ticks: [],
871 ticks_positions: [],
872 ticks_labels: [],
873 ticks_snap_bounds: 0,
874 ticks_tooltip: false,
875 scale: 'linear',
876 focus: false,
877 tooltip_position: null,
878 labelledby: null,
879 rangeHighlights: []
880 },
881
882 getElement: function getElement() {
883 return this.sliderElem;
884 },
885
886 getValue: function getValue() {
887 if (this.options.range) {
888 return this._state.value;
889 } else {
890 return this._state.value[0];
891 }
892 },
893
894 setValue: function setValue(val, triggerSlideEvent, triggerChangeEvent) {
895 if (!val) {
896 val = 0;
897 }
898 var oldValue = this.getValue();
899 this._state.value = this._validateInputValue(val);
900 var applyPrecision = this._applyPrecision.bind(this);
901
902 if (this.options.range) {
903 this._state.value[0] = applyPrecision(this._state.value[0]);
904 this._state.value[1] = applyPrecision(this._state.value[1]);
905
906 if (this.ticksAreValid && this.options.lock_to_ticks) {
907 this._state.value[0] = this.options.ticks[this._getClosestTickIndex(this._state.value[0])];
908 this._state.value[1] = this.options.ticks[this._getClosestTickIndex(this._state.value[1])];
909 }
910
911 this._state.value[0] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[0]));
912 this._state.value[1] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[1]));
913 } else {
914 this._state.value = applyPrecision(this._state.value);
915
916 if (this.ticksAreValid && this.options.lock_to_ticks) {
917 this._state.value = this.options.ticks[this._getClosestTickIndex(this._state.value)];
918 }
919
920 this._state.value = [Math.max(this.options.min, Math.min(this.options.max, this._state.value))];
921 this._addClass(this.handle2, 'hide');
922 if (this.options.selection === 'after') {
923 this._state.value[1] = this.options.max;
924 } else {
925 this._state.value[1] = this.options.min;
926 }
927 }
928
929 // Determine which ticks the handle(s) are set at (if applicable)
930 this._setTickIndex();
931
932 if (this.options.max > this.options.min) {
933 this._state.percentage = [this._toPercentage(this._state.value[0]), this._toPercentage(this._state.value[1]), this.options.step * 100 / (this.options.max - this.options.min)];
934 } else {
935 this._state.percentage = [0, 0, 100];
936 }
937
938 this._layout();
939 var newValue = this.options.range ? this._state.value : this._state.value[0];
940
941 this._setDataVal(newValue);
942 if (triggerSlideEvent === true) {
943 this._trigger('slide', newValue);
944 }
945
946 var hasChanged = false;
947 if (Array.isArray(newValue)) {
948 hasChanged = oldValue[0] !== newValue[0] || oldValue[1] !== newValue[1];
949 } else {
950 hasChanged = oldValue !== newValue;
951 }
952
953 if (hasChanged && triggerChangeEvent === true) {
954 this._trigger('change', {
955 oldValue: oldValue,
956 newValue: newValue
957 });
958 }
959
960 return this;
961 },
962
963 destroy: function destroy() {
964 // Remove event handlers on slider elements
965 this._removeSliderEventHandlers();
966
967 // Remove the slider from the DOM
968 this.sliderElem.parentNode.removeChild(this.sliderElem);
969 /* Show original <input> element */
970 this.element.style.display = "";
971
972 // Clear out custom event bindings
973 this._cleanUpEventCallbacksMap();
974
975 // Remove data values
976 this.element.removeAttribute("data");
977
978 // Remove JQuery handlers/data
979 if ($) {
980 this._unbindJQueryEventHandlers();
981 if (autoRegisterNamespace === NAMESPACE_MAIN) {
982 this.$element.removeData(autoRegisterNamespace);
983 }
984 this.$element.removeData(NAMESPACE_ALTERNATE);
985 }
986 },
987
988 disable: function disable() {
989 this._state.enabled = false;
990 this.handle1.removeAttribute("tabindex");
991 this.handle2.removeAttribute("tabindex");
992 this._addClass(this.sliderElem, 'slider-disabled');
993 this._trigger('slideDisabled');
994
995 return this;
996 },
997
998 enable: function enable() {
999 this._state.enabled = true;
1000 this.handle1.setAttribute("tabindex", 0);
1001 this.handle2.setAttribute("tabindex", 0);
1002 this._removeClass(this.sliderElem, 'slider-disabled');
1003 this._trigger('slideEnabled');
1004
1005 return this;
1006 },
1007
1008 toggle: function toggle() {
1009 if (this._state.enabled) {
1010 this.disable();
1011 } else {
1012 this.enable();
1013 }
1014 return this;
1015 },
1016
1017 isEnabled: function isEnabled() {
1018 return this._state.enabled;
1019 },
1020
1021 on: function on(evt, callback) {
1022 this._bindNonQueryEventHandler(evt, callback);
1023 return this;
1024 },
1025
1026 off: function off(evt, callback) {
1027 if ($) {
1028 this.$element.off(evt, callback);
1029 this.$sliderElem.off(evt, callback);
1030 } else {
1031 this._unbindNonQueryEventHandler(evt, callback);
1032 }
1033 },
1034
1035 getAttribute: function getAttribute(attribute) {
1036 if (attribute) {
1037 return this.options[attribute];
1038 } else {
1039 return this.options;
1040 }
1041 },
1042
1043 setAttribute: function setAttribute(attribute, value) {
1044 this.options[attribute] = value;
1045 return this;
1046 },
1047
1048 refresh: function refresh(options) {
1049 var currentValue = this.getValue();
1050 this._removeSliderEventHandlers();
1051 createNewSlider.call(this, this.element, this.options);
1052 // Don't reset slider's value on refresh if `useCurrentValue` is true
1053 if (options && options.useCurrentValue === true) {
1054 this.setValue(currentValue);
1055 }
1056 if ($) {
1057 // Bind new instance of slider to the element
1058 if (autoRegisterNamespace === NAMESPACE_MAIN) {
1059 $.data(this.element, NAMESPACE_MAIN, this);
1060 $.data(this.element, NAMESPACE_ALTERNATE, this);
1061 } else {
1062 $.data(this.element, NAMESPACE_ALTERNATE, this);
1063 }
1064 }
1065 return this;
1066 },
1067
1068 relayout: function relayout() {
1069 this._resize();
1070 return this;
1071 },
1072
1073 /******************************+
1074 HELPERS
1075 - Any method that is not part of the public interface.
1076 - Place it underneath this comment block and write its signature like so:
1077 _fnName : function() {...}
1078 ********************************/
1079 _removeTooltipListener: function _removeTooltipListener(event, handler) {
1080 this.handle1.removeEventListener(event, handler, false);
1081 this.handle2.removeEventListener(event, handler, false);
1082 },
1083 _removeSliderEventHandlers: function _removeSliderEventHandlers() {
1084 // Remove keydown event listeners
1085 this.handle1.removeEventListener("keydown", this.handle1Keydown, false);
1086 this.handle2.removeEventListener("keydown", this.handle2Keydown, false);
1087
1088 //remove the listeners from the ticks and handles if they had their own listeners
1089 if (this.options.ticks_tooltip) {
1090 var ticks = this.ticksContainer.getElementsByClassName('slider-tick');
1091 for (var i = 0; i < ticks.length; i++) {
1092 ticks[i].removeEventListener('mouseenter', this.ticksCallbackMap[i].mouseEnter, false);
1093 ticks[i].removeEventListener('mouseleave', this.ticksCallbackMap[i].mouseLeave, false);
1094 }
1095 if (this.handleCallbackMap.handle1 && this.handleCallbackMap.handle2) {
1096 this.handle1.removeEventListener('mouseenter', this.handleCallbackMap.handle1.mouseEnter, false);
1097 this.handle2.removeEventListener('mouseenter', this.handleCallbackMap.handle2.mouseEnter, false);
1098 this.handle1.removeEventListener('mouseleave', this.handleCallbackMap.handle1.mouseLeave, false);
1099 this.handle2.removeEventListener('mouseleave', this.handleCallbackMap.handle2.mouseLeave, false);
1100 }
1101 }
1102
1103 this.handleCallbackMap = null;
1104 this.ticksCallbackMap = null;
1105
1106 if (this.showTooltip) {
1107 this._removeTooltipListener("focus", this.showTooltip);
1108 }
1109 if (this.hideTooltip) {
1110 this._removeTooltipListener("blur", this.hideTooltip);
1111 }
1112
1113 // Remove event listeners from sliderElem
1114 if (this.showTooltip) {
1115 this.sliderElem.removeEventListener("mouseenter", this.showTooltip, false);
1116 }
1117 if (this.hideTooltip) {
1118 this.sliderElem.removeEventListener("mouseleave", this.hideTooltip, false);
1119 }
1120
1121 this.sliderElem.removeEventListener("mousedown", this.mousedown, false);
1122
1123 if (this.touchCapable) {
1124 // Remove touch event listeners from handles
1125 if (this.showTooltip) {
1126 this.handle1.removeEventListener("touchstart", this.showTooltip, false);
1127 this.handle1.removeEventListener("touchmove", this.showTooltip, false);
1128 this.handle2.removeEventListener("touchstart", this.showTooltip, false);
1129 this.handle2.removeEventListener("touchmove", this.showTooltip, false);
1130 }
1131 if (this.hideTooltip) {
1132 this.handle1.removeEventListener("touchend", this.hideTooltip, false);
1133 this.handle2.removeEventListener("touchend", this.hideTooltip, false);
1134 }
1135
1136 // Remove event listeners from sliderElem
1137 if (this.showTooltip) {
1138 this.sliderElem.removeEventListener("touchstart", this.showTooltip, false);
1139 this.sliderElem.removeEventListener("touchmove", this.showTooltip, false);
1140 }
1141 if (this.hideTooltip) {
1142 this.sliderElem.removeEventListener("touchend", this.hideTooltip, false);
1143 }
1144
1145 this.sliderElem.removeEventListener("touchstart", this.touchstart, false);
1146 this.sliderElem.removeEventListener("touchmove", this.touchmove, false);
1147 }
1148
1149 // Remove window event listener
1150 window.removeEventListener("resize", this.resize, false);
1151 },
1152 _bindNonQueryEventHandler: function _bindNonQueryEventHandler(evt, callback) {
1153 if (this.eventToCallbackMap[evt] === undefined) {
1154 this.eventToCallbackMap[evt] = [];
1155 }
1156 this.eventToCallbackMap[evt].push(callback);
1157 },
1158 _unbindNonQueryEventHandler: function _unbindNonQueryEventHandler(evt, callback) {
1159 var callbacks = this.eventToCallbackMap[evt];
1160 if (callbacks !== undefined) {
1161 for (var i = 0; i < callbacks.length; i++) {
1162 if (callbacks[i] === callback) {
1163 callbacks.splice(i, 1);
1164 break;
1165 }
1166 }
1167 }
1168 },
1169 _cleanUpEventCallbacksMap: function _cleanUpEventCallbacksMap() {
1170 var eventNames = Object.keys(this.eventToCallbackMap);
1171 for (var i = 0; i < eventNames.length; i++) {
1172 var eventName = eventNames[i];
1173 delete this.eventToCallbackMap[eventName];
1174 }
1175 },
1176 _showTooltip: function _showTooltip() {
1177 if (this.options.tooltip_split === false) {
1178 this._addClass(this.tooltip, 'show');
1179 this.tooltip_min.style.display = 'none';
1180 this.tooltip_max.style.display = 'none';
1181 } else {
1182 this._addClass(this.tooltip_min, 'show');
1183 this._addClass(this.tooltip_max, 'show');
1184 this.tooltip.style.display = 'none';
1185 }
1186 this._state.over = true;
1187 },
1188 _hideTooltip: function _hideTooltip() {
1189 if (this._state.inDrag === false && this._alwaysShowTooltip !== true) {
1190 this._removeClass(this.tooltip, 'show');
1191 this._removeClass(this.tooltip_min, 'show');
1192 this._removeClass(this.tooltip_max, 'show');
1193 }
1194 this._state.over = false;
1195 },
1196 _setToolTipOnMouseOver: function _setToolTipOnMouseOver(tempState) {
1197 var self = this;
1198 var formattedTooltipVal = this.options.formatter(!tempState ? this._state.value[0] : tempState.value[0]);
1199 var positionPercentages = !tempState ? getPositionPercentages(this._state, this.options.reversed) : getPositionPercentages(tempState, this.options.reversed);
1200 this._setText(this.tooltipInner, formattedTooltipVal);
1201
1202 this.tooltip.style[this.stylePos] = positionPercentages[0] + "%";
1203
1204 function getPositionPercentages(state, reversed) {
1205 if (reversed) {
1206 return [100 - state.percentage[0], self.options.range ? 100 - state.percentage[1] : state.percentage[1]];
1207 }
1208 return [state.percentage[0], state.percentage[1]];
1209 }
1210 },
1211 _copyState: function _copyState() {
1212 return {
1213 value: [this._state.value[0], this._state.value[1]],
1214 enabled: this._state.enabled,
1215 offset: this._state.offset,
1216 size: this._state.size,
1217 percentage: [this._state.percentage[0], this._state.percentage[1], this._state.percentage[2]],
1218 inDrag: this._state.inDrag,
1219 over: this._state.over,
1220 // deleted or null'd keys
1221 dragged: this._state.dragged,
1222 keyCtrl: this._state.keyCtrl
1223 };
1224 },
1225 _addTickListener: function _addTickListener() {
1226 return {
1227 addMouseEnter: function addMouseEnter(reference, element, index) {
1228 var enter = function enter() {
1229 var tempState = reference._copyState();
1230 // Which handle is being hovered over?
1231 var val = element === reference.handle1 ? tempState.value[0] : tempState.value[1];
1232 var per = void 0;
1233
1234 // Setup value and percentage for tick's 'mouseenter'
1235 if (index !== undefined) {
1236 val = reference.options.ticks[index];
1237 per = reference.options.ticks_positions.length > 0 && reference.options.ticks_positions[index] || reference._toPercentage(reference.options.ticks[index]);
1238 } else {
1239 per = reference._toPercentage(val);
1240 }
1241
1242 tempState.value[0] = val;
1243 tempState.percentage[0] = per;
1244 reference._setToolTipOnMouseOver(tempState);
1245 reference._showTooltip();
1246 };
1247 element.addEventListener("mouseenter", enter, false);
1248 return enter;
1249 },
1250 addMouseLeave: function addMouseLeave(reference, element) {
1251 var leave = function leave() {
1252 reference._hideTooltip();
1253 };
1254 element.addEventListener("mouseleave", leave, false);
1255 return leave;
1256 }
1257 };
1258 },
1259 _layout: function _layout() {
1260 var positionPercentages;
1261 var formattedValue;
1262
1263 if (this.options.reversed) {
1264 positionPercentages = [100 - this._state.percentage[0], this.options.range ? 100 - this._state.percentage[1] : this._state.percentage[1]];
1265 } else {
1266 positionPercentages = [this._state.percentage[0], this._state.percentage[1]];
1267 }
1268
1269 this.handle1.style[this.stylePos] = positionPercentages[0] + "%";
1270 this.handle1.setAttribute('aria-valuenow', this._state.value[0]);
1271 formattedValue = this.options.formatter(this._state.value[0]);
1272 if (isNaN(formattedValue)) {
1273 this.handle1.setAttribute('aria-valuetext', formattedValue);
1274 } else {
1275 this.handle1.removeAttribute('aria-valuetext');
1276 }
1277
1278 this.handle2.style[this.stylePos] = positionPercentages[1] + "%";
1279 this.handle2.setAttribute('aria-valuenow', this._state.value[1]);
1280 formattedValue = this.options.formatter(this._state.value[1]);
1281 if (isNaN(formattedValue)) {
1282 this.handle2.setAttribute('aria-valuetext', formattedValue);
1283 } else {
1284 this.handle2.removeAttribute('aria-valuetext');
1285 }
1286
1287 /* Position highlight range elements */
1288 if (this.rangeHighlightElements.length > 0 && Array.isArray(this.options.rangeHighlights) && this.options.rangeHighlights.length > 0) {
1289 for (var _i = 0; _i < this.options.rangeHighlights.length; _i++) {
1290 var startPercent = this._toPercentage(this.options.rangeHighlights[_i].start);
1291 var endPercent = this._toPercentage(this.options.rangeHighlights[_i].end);
1292
1293 if (this.options.reversed) {
1294 var sp = 100 - endPercent;
1295 endPercent = 100 - startPercent;
1296 startPercent = sp;
1297 }
1298
1299 var currentRange = this._createHighlightRange(startPercent, endPercent);
1300
1301 if (currentRange) {
1302 if (this.options.orientation === 'vertical') {
1303 this.rangeHighlightElements[_i].style.top = currentRange.start + "%";
1304 this.rangeHighlightElements[_i].style.height = currentRange.size + "%";
1305 } else {
1306 if (this.options.rtl) {
1307 this.rangeHighlightElements[_i].style.right = currentRange.start + "%";
1308 } else {
1309 this.rangeHighlightElements[_i].style.left = currentRange.start + "%";
1310 }
1311 this.rangeHighlightElements[_i].style.width = currentRange.size + "%";
1312 }
1313 } else {
1314 this.rangeHighlightElements[_i].style.display = "none";
1315 }
1316 }
1317 }
1318
1319 /* Position ticks and labels */
1320 if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {
1321
1322 var styleSize = this.options.orientation === 'vertical' ? 'height' : 'width';
1323 var styleMargin;
1324 if (this.options.orientation === 'vertical') {
1325 styleMargin = 'marginTop';
1326 } else {
1327 if (this.options.rtl) {
1328 styleMargin = 'marginRight';
1329 } else {
1330 styleMargin = 'marginLeft';
1331 }
1332 }
1333 var labelSize = this._state.size / (this.options.ticks.length - 1);
1334
1335 if (this.tickLabelContainer) {
1336 var extraMargin = 0;
1337 if (this.options.ticks_positions.length === 0) {
1338 if (this.options.orientation !== 'vertical') {
1339 this.tickLabelContainer.style[styleMargin] = -labelSize / 2 + "px";
1340 }
1341
1342 extraMargin = this.tickLabelContainer.offsetHeight;
1343 } else {
1344 /* Chidren are position absolute, calculate height by finding the max offsetHeight of a child */
1345 for (i = 0; i < this.tickLabelContainer.childNodes.length; i++) {
1346 if (this.tickLabelContainer.childNodes[i].offsetHeight > extraMargin) {
1347 extraMargin = this.tickLabelContainer.childNodes[i].offsetHeight;
1348 }
1349 }
1350 }
1351 if (this.options.orientation === 'horizontal') {
1352 this.sliderElem.style.marginBottom = extraMargin + "px";
1353 }
1354 }
1355 for (var i = 0; i < this.options.ticks.length; i++) {
1356
1357 var percentage = this.options.ticks_positions[i] || this._toPercentage(this.options.ticks[i]);
1358
1359 if (this.options.reversed) {
1360 percentage = 100 - percentage;
1361 }
1362
1363 this.ticks[i].style[this.stylePos] = percentage + "%";
1364
1365 /* Set class labels to denote whether ticks are in the selection */
1366 this._removeClass(this.ticks[i], 'in-selection');
1367 if (!this.options.range) {
1368 if (this.options.selection === 'after' && percentage >= positionPercentages[0]) {
1369 this._addClass(this.ticks[i], 'in-selection');
1370 } else if (this.options.selection === 'before' && percentage <= positionPercentages[0]) {
1371 this._addClass(this.ticks[i], 'in-selection');
1372 }
1373 } else if (percentage >= positionPercentages[0] && percentage <= positionPercentages[1]) {
1374 this._addClass(this.ticks[i], 'in-selection');
1375 }
1376
1377 if (this.tickLabels[i]) {
1378 this.tickLabels[i].style[styleSize] = labelSize + "px";
1379
1380 if (this.options.orientation !== 'vertical' && this.options.ticks_positions[i] !== undefined) {
1381 this.tickLabels[i].style.position = 'absolute';
1382 this.tickLabels[i].style[this.stylePos] = percentage + "%";
1383 this.tickLabels[i].style[styleMargin] = -labelSize / 2 + 'px';
1384 } else if (this.options.orientation === 'vertical') {
1385 if (this.options.rtl) {
1386 this.tickLabels[i].style['marginRight'] = this.sliderElem.offsetWidth + "px";
1387 } else {
1388 this.tickLabels[i].style['marginLeft'] = this.sliderElem.offsetWidth + "px";
1389 }
1390 this.tickLabelContainer.style[styleMargin] = this.sliderElem.offsetWidth / 2 * -1 + 'px';
1391 }
1392
1393 /* Set class labels to indicate tick labels are in the selection or selected */
1394 this._removeClass(this.tickLabels[i], 'label-in-selection label-is-selection');
1395 if (!this.options.range) {
1396 if (this.options.selection === 'after' && percentage >= positionPercentages[0]) {
1397 this._addClass(this.tickLabels[i], 'label-in-selection');
1398 } else if (this.options.selection === 'before' && percentage <= positionPercentages[0]) {
1399 this._addClass(this.tickLabels[i], 'label-in-selection');
1400 }
1401 if (percentage === positionPercentages[0]) {
1402 this._addClass(this.tickLabels[i], 'label-is-selection');
1403 }
1404 } else if (percentage >= positionPercentages[0] && percentage <= positionPercentages[1]) {
1405 this._addClass(this.tickLabels[i], 'label-in-selection');
1406 if (percentage === positionPercentages[0] || positionPercentages[1]) {
1407 this._addClass(this.tickLabels[i], 'label-is-selection');
1408 }
1409 }
1410 }
1411 }
1412 }
1413
1414 var formattedTooltipVal;
1415
1416 if (this.options.range) {
1417 formattedTooltipVal = this.options.formatter(this._state.value);
1418 this._setText(this.tooltipInner, formattedTooltipVal);
1419 this.tooltip.style[this.stylePos] = (positionPercentages[1] + positionPercentages[0]) / 2 + "%";
1420
1421 var innerTooltipMinText = this.options.formatter(this._state.value[0]);
1422 this._setText(this.tooltipInner_min, innerTooltipMinText);
1423
1424 var innerTooltipMaxText = this.options.formatter(this._state.value[1]);
1425 this._setText(this.tooltipInner_max, innerTooltipMaxText);
1426
1427 this.tooltip_min.style[this.stylePos] = positionPercentages[0] + "%";
1428
1429 this.tooltip_max.style[this.stylePos] = positionPercentages[1] + "%";
1430 } else {
1431 formattedTooltipVal = this.options.formatter(this._state.value[0]);
1432 this._setText(this.tooltipInner, formattedTooltipVal);
1433
1434 this.tooltip.style[this.stylePos] = positionPercentages[0] + "%";
1435 }
1436
1437 if (this.options.orientation === 'vertical') {
1438 this.trackLow.style.top = '0';
1439 this.trackLow.style.height = Math.min(positionPercentages[0], positionPercentages[1]) + '%';
1440
1441 this.trackSelection.style.top = Math.min(positionPercentages[0], positionPercentages[1]) + '%';
1442 this.trackSelection.style.height = Math.abs(positionPercentages[0] - positionPercentages[1]) + '%';
1443
1444 this.trackHigh.style.bottom = '0';
1445 this.trackHigh.style.height = 100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1]) + '%';
1446 } else {
1447 if (this.stylePos === 'right') {
1448 this.trackLow.style.right = '0';
1449 } else {
1450 this.trackLow.style.left = '0';
1451 }
1452 this.trackLow.style.width = Math.min(positionPercentages[0], positionPercentages[1]) + '%';
1453
1454 if (this.stylePos === 'right') {
1455 this.trackSelection.style.right = Math.min(positionPercentages[0], positionPercentages[1]) + '%';
1456 } else {
1457 this.trackSelection.style.left = Math.min(positionPercentages[0], positionPercentages[1]) + '%';
1458 }
1459 this.trackSelection.style.width = Math.abs(positionPercentages[0] - positionPercentages[1]) + '%';
1460
1461 if (this.stylePos === 'right') {
1462 this.trackHigh.style.left = '0';
1463 } else {
1464 this.trackHigh.style.right = '0';
1465 }
1466 this.trackHigh.style.width = 100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1]) + '%';
1467
1468 var offset_min = this.tooltip_min.getBoundingClientRect();
1469 var offset_max = this.tooltip_max.getBoundingClientRect();
1470
1471 if (this.options.tooltip_position === 'bottom') {
1472 if (offset_min.right > offset_max.left) {
1473 this._removeClass(this.tooltip_max, 'bs-tooltip-bottom');
1474 this._addClass(this.tooltip_max, 'bs-tooltip-top');
1475 this.tooltip_max.style.top = '';
1476 this.tooltip_max.style.bottom = 22 + 'px';
1477 } else {
1478 this._removeClass(this.tooltip_max, 'bs-tooltip-top');
1479 this._addClass(this.tooltip_max, 'bs-tooltip-bottom');
1480 this.tooltip_max.style.top = this.tooltip_min.style.top;
1481 this.tooltip_max.style.bottom = '';
1482 }
1483 } else {
1484 if (offset_min.right > offset_max.left) {
1485 this._removeClass(this.tooltip_max, 'bs-tooltip-top');
1486 this._addClass(this.tooltip_max, 'bs-tooltip-bottom');
1487 this.tooltip_max.style.top = 18 + 'px';
1488 } else {
1489 this._removeClass(this.tooltip_max, 'bs-tooltip-bottom');
1490 this._addClass(this.tooltip_max, 'bs-tooltip-top');
1491 this.tooltip_max.style.top = this.tooltip_min.style.top;
1492 }
1493 }
1494 }
1495 },
1496 _createHighlightRange: function _createHighlightRange(start, end) {
1497 if (this._isHighlightRange(start, end)) {
1498 if (start > end) {
1499 return { 'start': end, 'size': start - end };
1500 }
1501 return { 'start': start, 'size': end - start };
1502 }
1503 return null;
1504 },
1505 _isHighlightRange: function _isHighlightRange(start, end) {
1506 if (0 <= start && start <= 100 && 0 <= end && end <= 100) {
1507 return true;
1508 } else {
1509 return false;
1510 }
1511 },
1512 _resize: function _resize(ev) {
1513 /*jshint unused:false*/
1514 this._state.offset = this._offset(this.sliderElem);
1515 this._state.size = this.sliderElem[this.sizePos];
1516 this._layout();
1517 },
1518 _removeProperty: function _removeProperty(element, prop) {
1519 if (element.style.removeProperty) {
1520 element.style.removeProperty(prop);
1521 } else {
1522 element.style.removeAttribute(prop);
1523 }
1524 },
1525 _mousedown: function _mousedown(ev) {
1526 if (!this._state.enabled) {
1527 return false;
1528 }
1529
1530 if (ev.preventDefault) {
1531 ev.preventDefault();
1532 }
1533
1534 this._state.offset = this._offset(this.sliderElem);
1535 this._state.size = this.sliderElem[this.sizePos];
1536
1537 var percentage = this._getPercentage(ev);
1538
1539 if (this.options.range) {
1540 var diff1 = Math.abs(this._state.percentage[0] - percentage);
1541 var diff2 = Math.abs(this._state.percentage[1] - percentage);
1542 this._state.dragged = diff1 < diff2 ? 0 : 1;
1543 this._adjustPercentageForRangeSliders(percentage);
1544 } else {
1545 this._state.dragged = 0;
1546 }
1547
1548 this._state.percentage[this._state.dragged] = percentage;
1549
1550 if (this.touchCapable) {
1551 document.removeEventListener("touchmove", this.mousemove, false);
1552 document.removeEventListener("touchend", this.mouseup, false);
1553 }
1554
1555 if (this.mousemove) {
1556 document.removeEventListener("mousemove", this.mousemove, false);
1557 }
1558 if (this.mouseup) {
1559 document.removeEventListener("mouseup", this.mouseup, false);
1560 }
1561
1562 this.mousemove = this._mousemove.bind(this);
1563 this.mouseup = this._mouseup.bind(this);
1564
1565 if (this.touchCapable) {
1566 // Touch: Bind touch events:
1567 document.addEventListener("touchmove", this.mousemove, false);
1568 document.addEventListener("touchend", this.mouseup, false);
1569 }
1570 // Bind mouse events:
1571 document.addEventListener("mousemove", this.mousemove, false);
1572 document.addEventListener("mouseup", this.mouseup, false);
1573
1574 this._state.inDrag = true;
1575 var newValue = this._calculateValue();
1576
1577 this._trigger('slideStart', newValue);
1578
1579 this.setValue(newValue, false, true);
1580
1581 ev.returnValue = false;
1582
1583 if (this.options.focus) {
1584 this._triggerFocusOnHandle(this._state.dragged);
1585 }
1586
1587 return true;
1588 },
1589 _touchstart: function _touchstart(ev) {
1590 this._mousedown(ev);
1591 },
1592 _triggerFocusOnHandle: function _triggerFocusOnHandle(handleIdx) {
1593 if (handleIdx === 0) {
1594 this.handle1.focus();
1595 }
1596 if (handleIdx === 1) {
1597 this.handle2.focus();
1598 }
1599 },
1600 _keydown: function _keydown(handleIdx, ev) {
1601 if (!this._state.enabled) {
1602 return false;
1603 }
1604
1605 var dir;
1606 switch (ev.keyCode) {
1607 case 37: // left
1608 case 40:
1609 // down
1610 dir = -1;
1611 break;
1612 case 39: // right
1613 case 38:
1614 // up
1615 dir = 1;
1616 break;
1617 }
1618 if (!dir) {
1619 return;
1620 }
1621
1622 // use natural arrow keys instead of from min to max
1623 if (this.options.natural_arrow_keys) {
1624 var isHorizontal = this.options.orientation === 'horizontal';
1625 var isVertical = this.options.orientation === 'vertical';
1626 var isRTL = this.options.rtl;
1627 var isReversed = this.options.reversed;
1628
1629 if (isHorizontal) {
1630 if (isRTL) {
1631 if (!isReversed) {
1632 dir = -dir;
1633 }
1634 } else {
1635 if (isReversed) {
1636 dir = -dir;
1637 }
1638 }
1639 } else if (isVertical) {
1640 if (!isReversed) {
1641 dir = -dir;
1642 }
1643 }
1644 }
1645
1646 var val;
1647 if (this.ticksAreValid && this.options.lock_to_ticks) {
1648 var index = void 0;
1649 // Find tick index that handle 1/2 is currently on
1650 index = this.options.ticks.indexOf(this._state.value[handleIdx]);
1651 if (index === -1) {
1652 // Set default to first tick
1653 index = 0;
1654 window.console.warn('(lock_to_ticks) _keydown: index should not be -1');
1655 }
1656 index += dir;
1657 index = Math.max(0, Math.min(this.options.ticks.length - 1, index));
1658 val = this.options.ticks[index];
1659 } else {
1660 val = this._state.value[handleIdx] + dir * this.options.step;
1661 }
1662 var percentage = this._toPercentage(val);
1663 this._state.keyCtrl = handleIdx;
1664 if (this.options.range) {
1665 this._adjustPercentageForRangeSliders(percentage);
1666 var val1 = !this._state.keyCtrl ? val : this._state.value[0];
1667 var val2 = this._state.keyCtrl ? val : this._state.value[1];
1668 // Restrict values within limits
1669 val = [Math.max(this.options.min, Math.min(this.options.max, val1)), Math.max(this.options.min, Math.min(this.options.max, val2))];
1670 } else {
1671 val = Math.max(this.options.min, Math.min(this.options.max, val));
1672 }
1673
1674 this._trigger('slideStart', val);
1675
1676 this.setValue(val, true, true);
1677
1678 this._trigger('slideStop', val);
1679
1680 this._pauseEvent(ev);
1681 delete this._state.keyCtrl;
1682
1683 return false;
1684 },
1685 _pauseEvent: function _pauseEvent(ev) {
1686 if (ev.stopPropagation) {
1687 ev.stopPropagation();
1688 }
1689 if (ev.preventDefault) {
1690 ev.preventDefault();
1691 }
1692 ev.cancelBubble = true;
1693 ev.returnValue = false;
1694 },
1695 _mousemove: function _mousemove(ev) {
1696 if (!this._state.enabled) {
1697 return false;
1698 }
1699
1700 var percentage = this._getPercentage(ev);
1701 this._adjustPercentageForRangeSliders(percentage);
1702 this._state.percentage[this._state.dragged] = percentage;
1703
1704 var val = this._calculateValue(true);
1705 this.setValue(val, true, true);
1706
1707 return false;
1708 },
1709 _touchmove: function _touchmove(ev) {
1710 if (ev.changedTouches === undefined) {
1711 return;
1712 }
1713
1714 // Prevent page from scrolling and only drag the slider
1715 if (ev.preventDefault) {
1716 ev.preventDefault();
1717 }
1718 },
1719 _adjustPercentageForRangeSliders: function _adjustPercentageForRangeSliders(percentage) {
1720 if (this.options.range) {
1721 var precision = this._getNumDigitsAfterDecimalPlace(percentage);
1722 precision = precision ? precision - 1 : 0;
1723 var percentageWithAdjustedPrecision = this._applyToFixedAndParseFloat(percentage, precision);
1724 if (this._state.dragged === 0 && this._applyToFixedAndParseFloat(this._state.percentage[1], precision) < percentageWithAdjustedPrecision) {
1725 this._state.percentage[0] = this._state.percentage[1];
1726 this._state.dragged = 1;
1727 } else if (this._state.dragged === 1 && this._applyToFixedAndParseFloat(this._state.percentage[0], precision) > percentageWithAdjustedPrecision) {
1728 this._state.percentage[1] = this._state.percentage[0];
1729 this._state.dragged = 0;
1730 } else if (this._state.keyCtrl === 0 && this._toPercentage(this._state.value[1]) < percentage) {
1731 this._state.percentage[0] = this._state.percentage[1];
1732 this._state.keyCtrl = 1;
1733 this.handle2.focus();
1734 } else if (this._state.keyCtrl === 1 && this._toPercentage(this._state.value[0]) > percentage) {
1735 this._state.percentage[1] = this._state.percentage[0];
1736 this._state.keyCtrl = 0;
1737 this.handle1.focus();
1738 }
1739 }
1740 },
1741 _mouseup: function _mouseup(ev) {
1742 if (!this._state.enabled) {
1743 return false;
1744 }
1745
1746 var percentage = this._getPercentage(ev);
1747 this._adjustPercentageForRangeSliders(percentage);
1748 this._state.percentage[this._state.dragged] = percentage;
1749
1750 if (this.touchCapable) {
1751 // Touch: Unbind touch event handlers:
1752 document.removeEventListener("touchmove", this.mousemove, false);
1753 document.removeEventListener("touchend", this.mouseup, false);
1754 }
1755 // Unbind mouse event handlers:
1756 document.removeEventListener("mousemove", this.mousemove, false);
1757 document.removeEventListener("mouseup", this.mouseup, false);
1758
1759 this._state.inDrag = false;
1760 if (this._state.over === false) {
1761 this._hideTooltip();
1762 }
1763 var val = this._calculateValue(true);
1764
1765 this.setValue(val, false, true);
1766 this._trigger('slideStop', val);
1767
1768 // No longer need 'dragged' after mouse up
1769 this._state.dragged = null;
1770
1771 return false;
1772 },
1773 _setValues: function _setValues(index, val) {
1774 var comp = 0 === index ? 0 : 100;
1775 if (this._state.percentage[index] !== comp) {
1776 val.data[index] = this._toValue(this._state.percentage[index]);
1777 val.data[index] = this._applyPrecision(val.data[index]);
1778 }
1779 },
1780 _calculateValue: function _calculateValue(snapToClosestTick) {
1781 var val = {};
1782 if (this.options.range) {
1783 val.data = [this.options.min, this.options.max];
1784 this._setValues(0, val);
1785 this._setValues(1, val);
1786 if (snapToClosestTick) {
1787 val.data[0] = this._snapToClosestTick(val.data[0]);
1788 val.data[1] = this._snapToClosestTick(val.data[1]);
1789 }
1790 } else {
1791 val.data = this._toValue(this._state.percentage[0]);
1792 val.data = parseFloat(val.data);
1793 val.data = this._applyPrecision(val.data);
1794 if (snapToClosestTick) {
1795 val.data = this._snapToClosestTick(val.data);
1796 }
1797 }
1798
1799 return val.data;
1800 },
1801 _snapToClosestTick: function _snapToClosestTick(val) {
1802 var min = [val, Infinity];
1803 for (var i = 0; i < this.options.ticks.length; i++) {
1804 var diff = Math.abs(this.options.ticks[i] - val);
1805 if (diff <= min[1]) {
1806 min = [this.options.ticks[i], diff];
1807 }
1808 }
1809 if (min[1] <= this.options.ticks_snap_bounds) {
1810 return min[0];
1811 }
1812 return val;
1813 },
1814
1815 _applyPrecision: function _applyPrecision(val) {
1816 var precision = this.options.precision || this._getNumDigitsAfterDecimalPlace(this.options.step);
1817 return this._applyToFixedAndParseFloat(val, precision);
1818 },
1819 _getNumDigitsAfterDecimalPlace: function _getNumDigitsAfterDecimalPlace(num) {
1820 var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
1821 if (!match) {
1822 return 0;
1823 }
1824 return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
1825 },
1826 _applyToFixedAndParseFloat: function _applyToFixedAndParseFloat(num, toFixedInput) {
1827 var truncatedNum = num.toFixed(toFixedInput);
1828 return parseFloat(truncatedNum);
1829 },
1830 /*
1831 Credits to Mike Samuel for the following method!
1832 Source: http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number
1833 */
1834 _getPercentage: function _getPercentage(ev) {
1835 if (this.touchCapable && (ev.type === 'touchstart' || ev.type === 'touchmove' || ev.type === 'touchend')) {
1836 ev = ev.changedTouches[0];
1837 }
1838
1839 var eventPosition = ev[this.mousePos];
1840 var sliderOffset = this._state.offset[this.stylePos];
1841 var distanceToSlide = eventPosition - sliderOffset;
1842 if (this.stylePos === 'right') {
1843 distanceToSlide = -distanceToSlide;
1844 }
1845 // Calculate what percent of the length the slider handle has slid
1846 var percentage = distanceToSlide / this._state.size * 100;
1847 percentage = Math.round(percentage / this._state.percentage[2]) * this._state.percentage[2];
1848 if (this.options.reversed) {
1849 percentage = 100 - percentage;
1850 }
1851
1852 // Make sure the percent is within the bounds of the slider.
1853 // 0% corresponds to the 'min' value of the slide
1854 // 100% corresponds to the 'max' value of the slide
1855 return Math.max(0, Math.min(100, percentage));
1856 },
1857 _validateInputValue: function _validateInputValue(val) {
1858 if (!isNaN(+val)) {
1859 return +val;
1860 } else if (Array.isArray(val)) {
1861 this._validateArray(val);
1862 return val;
1863 } else {
1864 throw new Error(ErrorMsgs.formatInvalidInputErrorMsg(val));
1865 }
1866 },
1867 _validateArray: function _validateArray(val) {
1868 for (var i = 0; i < val.length; i++) {
1869 var input = val[i];
1870 if (typeof input !== 'number') {
1871 throw new Error(ErrorMsgs.formatInvalidInputErrorMsg(input));
1872 }
1873 }
1874 },
1875 _setDataVal: function _setDataVal(val) {
1876 this.element.setAttribute('data-value', val);
1877 this.element.setAttribute('value', val);
1878 this.element.value = val;
1879 },
1880 _trigger: function _trigger(evt, val) {
1881 val = val || val === 0 ? val : undefined;
1882
1883 var callbackFnArray = this.eventToCallbackMap[evt];
1884 if (callbackFnArray && callbackFnArray.length) {
1885 for (var i = 0; i < callbackFnArray.length; i++) {
1886 var callbackFn = callbackFnArray[i];
1887 callbackFn(val);
1888 }
1889 }
1890
1891 /* If JQuery exists, trigger JQuery events */
1892 if ($) {
1893 this._triggerJQueryEvent(evt, val);
1894 }
1895 },
1896 _triggerJQueryEvent: function _triggerJQueryEvent(evt, val) {
1897 var eventData = {
1898 type: evt,
1899 value: val
1900 };
1901 this.$element.trigger(eventData);
1902 this.$sliderElem.trigger(eventData);
1903 },
1904 _unbindJQueryEventHandlers: function _unbindJQueryEventHandlers() {
1905 this.$element.off();
1906 this.$sliderElem.off();
1907 },
1908 _setText: function _setText(element, text) {
1909 if (typeof element.textContent !== "undefined") {
1910 element.textContent = text;
1911 } else if (typeof element.innerText !== "undefined") {
1912 element.innerText = text;
1913 }
1914 },
1915 _removeClass: function _removeClass(element, classString) {
1916 var classes = classString.split(" ");
1917 var newClasses = element.className;
1918
1919 for (var i = 0; i < classes.length; i++) {
1920 var classTag = classes[i];
1921 var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)");
1922 newClasses = newClasses.replace(regex, " ");
1923 }
1924
1925 element.className = newClasses.trim();
1926 },
1927 _addClass: function _addClass(element, classString) {
1928 var classes = classString.split(" ");
1929 var newClasses = element.className;
1930
1931 for (var i = 0; i < classes.length; i++) {
1932 var classTag = classes[i];
1933 var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)");
1934 var ifClassExists = regex.test(newClasses);
1935
1936 if (!ifClassExists) {
1937 newClasses += " " + classTag;
1938 }
1939 }
1940
1941 element.className = newClasses.trim();
1942 },
1943 _offsetLeft: function _offsetLeft(obj) {
1944 return obj.getBoundingClientRect().left;
1945 },
1946 _offsetRight: function _offsetRight(obj) {
1947 return obj.getBoundingClientRect().right;
1948 },
1949 _offsetTop: function _offsetTop(obj) {
1950 var offsetTop = obj.offsetTop;
1951 while ((obj = obj.offsetParent) && !isNaN(obj.offsetTop)) {
1952 offsetTop += obj.offsetTop;
1953 if (obj.tagName !== 'BODY') {
1954 offsetTop -= obj.scrollTop;
1955 }
1956 }
1957 return offsetTop;
1958 },
1959 _offset: function _offset(obj) {
1960 return {
1961 left: this._offsetLeft(obj),
1962 right: this._offsetRight(obj),
1963 top: this._offsetTop(obj)
1964 };
1965 },
1966 _css: function _css(elementRef, styleName, value) {
1967 if ($) {
1968 $.style(elementRef, styleName, value);
1969 } else {
1970 var style = styleName.replace(/^-ms-/, "ms-").replace(/-([\da-z])/gi, function (all, letter) {
1971 return letter.toUpperCase();
1972 });
1973 elementRef.style[style] = value;
1974 }
1975 },
1976 _toValue: function _toValue(percentage) {
1977 return this.options.scale.toValue.apply(this, [percentage]);
1978 },
1979 _toPercentage: function _toPercentage(value) {
1980 return this.options.scale.toPercentage.apply(this, [value]);
1981 },
1982 _setTooltipPosition: function _setTooltipPosition() {
1983 var tooltips = [this.tooltip, this.tooltip_min, this.tooltip_max];
1984 if (this.options.orientation === 'vertical') {
1985 var tooltipPos;
1986 if (this.options.tooltip_position) {
1987 tooltipPos = this.options.tooltip_position;
1988 } else {
1989 if (this.options.rtl) {
1990 tooltipPos = 'left';
1991 } else {
1992 tooltipPos = 'right';
1993 }
1994 }
1995 var oppositeSide = tooltipPos === 'left' ? 'right' : 'left';
1996 tooltips.forEach(function (tooltip) {
1997 this._addClass(tooltip, 'bs-tooltip-' + tooltipPos);
1998 tooltip.style[oppositeSide] = '100%';
1999 }.bind(this));
2000 } else if (this.options.tooltip_position === 'bottom') {
2001 tooltips.forEach(function (tooltip) {
2002 this._addClass(tooltip, 'bs-tooltip-bottom');
2003 tooltip.style.top = 22 + 'px';
2004 }.bind(this));
2005 } else {
2006 tooltips.forEach(function (tooltip) {
2007 this._addClass(tooltip, 'bs-tooltip-top');
2008 tooltip.style.top = -this.tooltip.outerHeight - 14 + 'px';
2009 }.bind(this));
2010 }
2011 },
2012 _getClosestTickIndex: function _getClosestTickIndex(val) {
2013 var difference = Math.abs(val - this.options.ticks[0]);
2014 var index = 0;
2015 for (var i = 0; i < this.options.ticks.length; ++i) {
2016 var d = Math.abs(val - this.options.ticks[i]);
2017 if (d < difference) {
2018 difference = d;
2019 index = i;
2020 }
2021 }
2022 return index;
2023 },
2024 /**
2025 * Attempts to find the index in `ticks[]` the slider values are set at.
2026 * The indexes can be -1 to indicate the slider value is not set at a value in `ticks[]`.
2027 */
2028 _setTickIndex: function _setTickIndex() {
2029 if (this.ticksAreValid) {
2030 this._state.tickIndex = [this.options.ticks.indexOf(this._state.value[0]), this.options.ticks.indexOf(this._state.value[1])];
2031 }
2032 }
2033 };
2034
2035 /*********************************
2036 Attach to global namespace
2037 *********************************/
2038 if ($ && $.fn) {
2039 if (!$.fn.slider) {
2040 $.bridget(NAMESPACE_MAIN, Slider);
2041 autoRegisterNamespace = NAMESPACE_MAIN;
2042 } else {
2043 if (windowIsDefined) {
2044 window.console.warn("bootstrap-slider.js - WARNING: $.fn.slider namespace is already bound. Use the $.fn.bootstrapSlider namespace instead.");
2045 }
2046 autoRegisterNamespace = NAMESPACE_ALTERNATE;
2047 }
2048 $.bridget(NAMESPACE_ALTERNATE, Slider);
2049
2050 // Auto-Register data-provide="slider" Elements
2051 $(function () {
2052 $("input[data-provide=slider]")[autoRegisterNamespace]();
2053 });
2054 }
2055 })($);
2056
2057 return Slider;
2058 });