]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/static/AdminLTE-2.3.7/plugins/bootstrap-slider/bootstrap-slider.js
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / pybind / mgr / dashboard / static / AdminLTE-2.3.7 / plugins / bootstrap-slider / bootstrap-slider.js
CommitLineData
31f18b77
FG
1/*! =========================================================
2 * bootstrap-slider.js
3 *
4 * Maintainers:
5 * Kyle Kemp
6 * - Twitter: @seiyria
7 * - Github: seiyria
8 * Rohit Kalkur
9 * - Twitter: @Rovolutionary
10 * - Github: rovolution
11 *
12 * =========================================================
13 *
14 * Licensed under the Apache License, Version 2.0 (the "License");
15 * you may not use this file except in compliance with the License.
16 * You may obtain a copy of the License at
17 *
18 * http://www.apache.org/licenses/LICENSE-2.0
19 *
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
25 * ========================================================= */
26
27
28/**
29 * Bridget makes jQuery widgets
30 * v1.0.1
31 * MIT license
32 */
33
34(function(root, factory) {
35 if(typeof define === "function" && define.amd) {
36 define(["jquery"], factory);
37 }
38 else if(typeof module === "object" && module.exports) {
39 var jQuery;
40 try {
41 jQuery = require("jquery");
42 }
43 catch (err) {
44 jQuery = null;
45 }
46 module.exports = factory(jQuery);
47 }
48 else {
49 root.Slider = factory(root.jQuery);
50 }
51}(this, function($) {
52 // Reference to Slider constructor
53 var Slider;
54
55
56 (function( $ ) {
57
58 'use strict';
59
60 // -------------------------- utils -------------------------- //
61
62 var slice = Array.prototype.slice;
63
64 function noop() {}
65
66 // -------------------------- definition -------------------------- //
67
68 function defineBridget( $ ) {
69
70 // bail if no jQuery
71 if ( !$ ) {
72 return;
73 }
74
75 // -------------------------- addOptionMethod -------------------------- //
76
77 /**
78 * adds option method -> $().plugin('option', {...})
79 * @param {Function} PluginClass - constructor class
80 */
81 function addOptionMethod( PluginClass ) {
82 // don't overwrite original option method
83 if ( PluginClass.prototype.option ) {
84 return;
85 }
86
87 // option setter
88 PluginClass.prototype.option = function( opts ) {
89 // bail out if not an object
90 if ( !$.isPlainObject( opts ) ){
91 return;
92 }
93 this.options = $.extend( true, this.options, opts );
94 };
95 }
96
97
98 // -------------------------- plugin bridge -------------------------- //
99
100 // helper function for logging errors
101 // $.error breaks jQuery chaining
102 var logError = typeof console === 'undefined' ? noop :
103 function( message ) {
104 console.error( message );
105 };
106
107 /**
108 * jQuery plugin bridge, access methods like $elem.plugin('method')
109 * @param {String} namespace - plugin name
110 * @param {Function} PluginClass - constructor class
111 */
112 function bridge( namespace, PluginClass ) {
113 // add to jQuery fn namespace
114 $.fn[ namespace ] = function( options ) {
115 if ( typeof options === 'string' ) {
116 // call plugin method when first argument is a string
117 // get arguments for method
118 var args = slice.call( arguments, 1 );
119
120 for ( var i=0, len = this.length; i < len; i++ ) {
121 var elem = this[i];
122 var instance = $.data( elem, namespace );
123 if ( !instance ) {
124 logError( "cannot call methods on " + namespace + " prior to initialization; " +
125 "attempted to call '" + options + "'" );
126 continue;
127 }
128 if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) {
129 logError( "no such method '" + options + "' for " + namespace + " instance" );
130 continue;
131 }
132
133 // trigger method with arguments
134 var returnValue = instance[ options ].apply( instance, args);
135
136 // break look and return first value if provided
137 if ( returnValue !== undefined && returnValue !== instance) {
138 return returnValue;
139 }
140 }
141 // return this if no return value
142 return this;
143 } else {
144 var objects = this.map( function() {
145 var instance = $.data( this, namespace );
146 if ( instance ) {
147 // apply options & init
148 instance.option( options );
149 instance._init();
150 } else {
151 // initialize new instance
152 instance = new PluginClass( this, options );
153 $.data( this, namespace, instance );
154 }
155 return $(this);
156 });
157
158 if(!objects || objects.length > 1) {
159 return objects;
160 } else {
161 return objects[0];
162 }
163 }
164 };
165
166 }
167
168 // -------------------------- bridget -------------------------- //
169
170 /**
171 * converts a Prototypical class into a proper jQuery plugin
172 * the class must have a ._init method
173 * @param {String} namespace - plugin name, used in $().pluginName
174 * @param {Function} PluginClass - constructor class
175 */
176 $.bridget = function( namespace, PluginClass ) {
177 addOptionMethod( PluginClass );
178 bridge( namespace, PluginClass );
179 };
180
181 return $.bridget;
182
183 }
184
185 // get jquery from browser global
186 defineBridget( $ );
187
188 })( $ );
189
190
191 /*************************************************
192
193 BOOTSTRAP-SLIDER SOURCE CODE
194
195 **************************************************/
196
197 (function($) {
198
199 var ErrorMsgs = {
200 formatInvalidInputErrorMsg : function(input) {
201 return "Invalid input value '" + input + "' passed in";
202 },
203 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"
204 };
205
206 var SliderScale = {
207 linear: {
208 toValue: function(percentage) {
209 var rawValue = percentage/100 * (this.options.max - this.options.min);
210 if (this.options.ticks_positions.length > 0) {
211 var minv, maxv, minp, maxp = 0;
212 for (var i = 0; i < this.options.ticks_positions.length; i++) {
213 if (percentage <= this.options.ticks_positions[i]) {
214 minv = (i > 0) ? this.options.ticks[i-1] : 0;
215 minp = (i > 0) ? this.options.ticks_positions[i-1] : 0;
216 maxv = this.options.ticks[i];
217 maxp = this.options.ticks_positions[i];
218
219 break;
220 }
221 }
222 if (i > 0) {
223 var partialPercentage = (percentage - minp) / (maxp - minp);
224 rawValue = minv + partialPercentage * (maxv - minv);
225 }
226 }
227
228 var value = this.options.min + Math.round(rawValue / this.options.step) * this.options.step;
229 if (value < this.options.min) {
230 return this.options.min;
231 } else if (value > this.options.max) {
232 return this.options.max;
233 } else {
234 return value;
235 }
236 },
237 toPercentage: function(value) {
238 if (this.options.max === this.options.min) {
239 return 0;
240 }
241
242 if (this.options.ticks_positions.length > 0) {
243 var minv, maxv, minp, maxp = 0;
244 for (var i = 0; i < this.options.ticks.length; i++) {
245 if (value <= this.options.ticks[i]) {
246 minv = (i > 0) ? this.options.ticks[i-1] : 0;
247 minp = (i > 0) ? this.options.ticks_positions[i-1] : 0;
248 maxv = this.options.ticks[i];
249 maxp = this.options.ticks_positions[i];
250
251 break;
252 }
253 }
254 if (i > 0) {
255 var partialPercentage = (value - minv) / (maxv - minv);
256 return minp + partialPercentage * (maxp - minp);
257 }
258 }
259
260 return 100 * (value - this.options.min) / (this.options.max - this.options.min);
261 }
262 },
263
264 logarithmic: {
265 /* Based on http://stackoverflow.com/questions/846221/logarithmic-slider */
266 toValue: function(percentage) {
267 var min = (this.options.min === 0) ? 0 : Math.log(this.options.min);
268 var max = Math.log(this.options.max);
269 var value = Math.exp(min + (max - min) * percentage / 100);
270 value = this.options.min + Math.round((value - this.options.min) / this.options.step) * this.options.step;
271 /* Rounding to the nearest step could exceed the min or
272 * max, so clip to those values. */
273 if (value < this.options.min) {
274 return this.options.min;
275 } else if (value > this.options.max) {
276 return this.options.max;
277 } else {
278 return value;
279 }
280 },
281 toPercentage: function(value) {
282 if (this.options.max === this.options.min) {
283 return 0;
284 } else {
285 var max = Math.log(this.options.max);
286 var min = this.options.min === 0 ? 0 : Math.log(this.options.min);
287 var v = value === 0 ? 0 : Math.log(value);
288 return 100 * (v - min) / (max - min);
289 }
290 }
291 }
292 };
293
294
295 /*************************************************
296
297 CONSTRUCTOR
298
299 **************************************************/
300 Slider = function(element, options) {
301 createNewSlider.call(this, element, options);
302 return this;
303 };
304
305 function createNewSlider(element, options) {
306
307 /*
308 The internal state object is used to store data about the current 'state' of slider.
309
310 This includes values such as the `value`, `enabled`, etc...
311 */
312 this._state = {
313 value: null,
314 enabled: null,
315 offset: null,
316 size: null,
317 percentage: null,
318 inDrag: false,
319 over: false
320 };
321
322
323 if(typeof element === "string") {
324 this.element = document.querySelector(element);
325 } else if(element instanceof HTMLElement) {
326 this.element = element;
327 }
328
329 /*************************************************
330
331 Process Options
332
333 **************************************************/
334 options = options ? options : {};
335 var optionTypes = Object.keys(this.defaultOptions);
336
337 for(var i = 0; i < optionTypes.length; i++) {
338 var optName = optionTypes[i];
339
340 // First check if an option was passed in via the constructor
341 var val = options[optName];
342 // If no data attrib, then check data atrributes
343 val = (typeof val !== 'undefined') ? val : getDataAttrib(this.element, optName);
344 // Finally, if nothing was specified, use the defaults
345 val = (val !== null) ? val : this.defaultOptions[optName];
346
347 // Set all options on the instance of the Slider
348 if(!this.options) {
349 this.options = {};
350 }
351 this.options[optName] = val;
352 }
353
354 /*
355 Validate `tooltip_position` against 'orientation`
356 - if `tooltip_position` is incompatible with orientation, swith it to a default compatible with specified `orientation`
357 -- default for "vertical" -> "right"
358 -- default for "horizontal" -> "left"
359 */
360 if(this.options.orientation === "vertical" && (this.options.tooltip_position === "top" || this.options.tooltip_position === "bottom")) {
361
362 this.options.tooltip_position = "right";
363
364 }
365 else if(this.options.orientation === "horizontal" && (this.options.tooltip_position === "left" || this.options.tooltip_position === "right")) {
366
367 this.options.tooltip_position = "top";
368
369 }
370
371 function getDataAttrib(element, optName) {
372 var dataName = "data-slider-" + optName.replace(/_/g, '-');
373 var dataValString = element.getAttribute(dataName);
374
375 try {
376 return JSON.parse(dataValString);
377 }
378 catch(err) {
379 return dataValString;
380 }
381 }
382
383 /*************************************************
384
385 Create Markup
386
387 **************************************************/
388
389 var origWidth = this.element.style.width;
390 var updateSlider = false;
391 var parent = this.element.parentNode;
392 var sliderTrackSelection;
393 var sliderTrackLow, sliderTrackHigh;
394 var sliderMinHandle;
395 var sliderMaxHandle;
396
397 if (this.sliderElem) {
398 updateSlider = true;
399 } else {
400 /* Create elements needed for slider */
401 this.sliderElem = document.createElement("div");
402 this.sliderElem.className = "slider";
403
404 /* Create slider track elements */
405 var sliderTrack = document.createElement("div");
406 sliderTrack.className = "slider-track";
407
408 sliderTrackLow = document.createElement("div");
409 sliderTrackLow.className = "slider-track-low";
410
411 sliderTrackSelection = document.createElement("div");
412 sliderTrackSelection.className = "slider-selection";
413
414 sliderTrackHigh = document.createElement("div");
415 sliderTrackHigh.className = "slider-track-high";
416
417 sliderMinHandle = document.createElement("div");
418 sliderMinHandle.className = "slider-handle min-slider-handle";
419 sliderMinHandle.setAttribute('role', 'slider');
420 sliderMinHandle.setAttribute('aria-valuemin', this.options.min);
421 sliderMinHandle.setAttribute('aria-valuemax', this.options.max);
422
423 sliderMaxHandle = document.createElement("div");
424 sliderMaxHandle.className = "slider-handle max-slider-handle";
425 sliderMaxHandle.setAttribute('role', 'slider');
426 sliderMaxHandle.setAttribute('aria-valuemin', this.options.min);
427 sliderMaxHandle.setAttribute('aria-valuemax', this.options.max);
428
429 sliderTrack.appendChild(sliderTrackLow);
430 sliderTrack.appendChild(sliderTrackSelection);
431 sliderTrack.appendChild(sliderTrackHigh);
432
433 /* Add aria-labelledby to handle's */
434 var isLabelledbyArray = Array.isArray(this.options.labelledby);
435 if (isLabelledbyArray && this.options.labelledby[0]) {
436 sliderMinHandle.setAttribute('aria-labelledby', this.options.labelledby[0]);
437 }
438 if (isLabelledbyArray && this.options.labelledby[1]) {
439 sliderMaxHandle.setAttribute('aria-labelledby', this.options.labelledby[1]);
440 }
441 if (!isLabelledbyArray && this.options.labelledby) {
442 sliderMinHandle.setAttribute('aria-labelledby', this.options.labelledby);
443 sliderMaxHandle.setAttribute('aria-labelledby', this.options.labelledby);
444 }
445
446 /* Create ticks */
447 this.ticks = [];
448 if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {
449 for (i = 0; i < this.options.ticks.length; i++) {
450 var tick = document.createElement('div');
451 tick.className = 'slider-tick';
452
453 this.ticks.push(tick);
454 sliderTrack.appendChild(tick);
455 }
456
457 sliderTrackSelection.className += " tick-slider-selection";
458 }
459
460 sliderTrack.appendChild(sliderMinHandle);
461 sliderTrack.appendChild(sliderMaxHandle);
462
463 this.tickLabels = [];
464 if (Array.isArray(this.options.ticks_labels) && this.options.ticks_labels.length > 0) {
465 this.tickLabelContainer = document.createElement('div');
466 this.tickLabelContainer.className = 'slider-tick-label-container';
467
468 for (i = 0; i < this.options.ticks_labels.length; i++) {
469 var label = document.createElement('div');
470 var noTickPositionsSpecified = this.options.ticks_positions.length === 0;
471 var tickLabelsIndex = (this.options.reversed && noTickPositionsSpecified) ? (this.options.ticks_labels.length - (i + 1)) : i;
472 label.className = 'slider-tick-label';
473 label.innerHTML = this.options.ticks_labels[tickLabelsIndex];
474
475 this.tickLabels.push(label);
476 this.tickLabelContainer.appendChild(label);
477 }
478 }
479
480
481 var createAndAppendTooltipSubElements = function(tooltipElem) {
482 var arrow = document.createElement("div");
483 arrow.className = "tooltip-arrow";
484
485 var inner = document.createElement("div");
486 inner.className = "tooltip-inner";
487
488 tooltipElem.appendChild(arrow);
489 tooltipElem.appendChild(inner);
490
491 };
492
493 /* Create tooltip elements */
494 var sliderTooltip = document.createElement("div");
495 sliderTooltip.className = "tooltip tooltip-main";
496 sliderTooltip.setAttribute('role', 'presentation');
497 createAndAppendTooltipSubElements(sliderTooltip);
498
499 var sliderTooltipMin = document.createElement("div");
500 sliderTooltipMin.className = "tooltip tooltip-min";
501 sliderTooltipMin.setAttribute('role', 'presentation');
502 createAndAppendTooltipSubElements(sliderTooltipMin);
503
504 var sliderTooltipMax = document.createElement("div");
505 sliderTooltipMax.className = "tooltip tooltip-max";
506 sliderTooltipMax.setAttribute('role', 'presentation');
507 createAndAppendTooltipSubElements(sliderTooltipMax);
508
509
510 /* Append components to sliderElem */
511 this.sliderElem.appendChild(sliderTrack);
512 this.sliderElem.appendChild(sliderTooltip);
513 this.sliderElem.appendChild(sliderTooltipMin);
514 this.sliderElem.appendChild(sliderTooltipMax);
515
516 if (this.tickLabelContainer) {
517 this.sliderElem.appendChild(this.tickLabelContainer);
518 }
519
520 /* Append slider element to parent container, right before the original <input> element */
521 parent.insertBefore(this.sliderElem, this.element);
522
523 /* Hide original <input> element */
524 this.element.style.display = "none";
525 }
526 /* If JQuery exists, cache JQ references */
527 if($) {
528 this.$element = $(this.element);
529 this.$sliderElem = $(this.sliderElem);
530 }
531
532 /*************************************************
533
534 Setup
535
536 **************************************************/
537 this.eventToCallbackMap = {};
538 this.sliderElem.id = this.options.id;
539
540 this.touchCapable = 'ontouchstart' in window || (window.DocumentTouch && document instanceof window.DocumentTouch);
541
542 this.tooltip = this.sliderElem.querySelector('.tooltip-main');
543 this.tooltipInner = this.tooltip.querySelector('.tooltip-inner');
544
545 this.tooltip_min = this.sliderElem.querySelector('.tooltip-min');
546 this.tooltipInner_min = this.tooltip_min.querySelector('.tooltip-inner');
547
548 this.tooltip_max = this.sliderElem.querySelector('.tooltip-max');
549 this.tooltipInner_max= this.tooltip_max.querySelector('.tooltip-inner');
550
551 if (SliderScale[this.options.scale]) {
552 this.options.scale = SliderScale[this.options.scale];
553 }
554
555 if (updateSlider === true) {
556 // Reset classes
557 this._removeClass(this.sliderElem, 'slider-horizontal');
558 this._removeClass(this.sliderElem, 'slider-vertical');
559 this._removeClass(this.tooltip, 'hide');
560 this._removeClass(this.tooltip_min, 'hide');
561 this._removeClass(this.tooltip_max, 'hide');
562
563 // Undo existing inline styles for track
564 ["left", "top", "width", "height"].forEach(function(prop) {
565 this._removeProperty(this.trackLow, prop);
566 this._removeProperty(this.trackSelection, prop);
567 this._removeProperty(this.trackHigh, prop);
568 }, this);
569
570 // Undo inline styles on handles
571 [this.handle1, this.handle2].forEach(function(handle) {
572 this._removeProperty(handle, 'left');
573 this._removeProperty(handle, 'top');
574 }, this);
575
576 // Undo inline styles and classes on tooltips
577 [this.tooltip, this.tooltip_min, this.tooltip_max].forEach(function(tooltip) {
578 this._removeProperty(tooltip, 'left');
579 this._removeProperty(tooltip, 'top');
580 this._removeProperty(tooltip, 'margin-left');
581 this._removeProperty(tooltip, 'margin-top');
582
583 this._removeClass(tooltip, 'right');
584 this._removeClass(tooltip, 'top');
585 }, this);
586 }
587
588 if(this.options.orientation === 'vertical') {
589 this._addClass(this.sliderElem,'slider-vertical');
590 this.stylePos = 'top';
591 this.mousePos = 'pageY';
592 this.sizePos = 'offsetHeight';
593 } else {
594 this._addClass(this.sliderElem, 'slider-horizontal');
595 this.sliderElem.style.width = origWidth;
596 this.options.orientation = 'horizontal';
597 this.stylePos = 'left';
598 this.mousePos = 'pageX';
599 this.sizePos = 'offsetWidth';
600
601 }
602 this._setTooltipPosition();
603 /* In case ticks are specified, overwrite the min and max bounds */
604 if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {
605 this.options.max = Math.max.apply(Math, this.options.ticks);
606 this.options.min = Math.min.apply(Math, this.options.ticks);
607 }
608
609 if (Array.isArray(this.options.value)) {
610 this.options.range = true;
611 this._state.value = this.options.value;
612 }
613 else if (this.options.range) {
614 // User wants a range, but value is not an array
615 this._state.value = [this.options.value, this.options.max];
616 }
617 else {
618 this._state.value = this.options.value;
619 }
620
621 this.trackLow = sliderTrackLow || this.trackLow;
622 this.trackSelection = sliderTrackSelection || this.trackSelection;
623 this.trackHigh = sliderTrackHigh || this.trackHigh;
624
625 if (this.options.selection === 'none') {
626 this._addClass(this.trackLow, 'hide');
627 this._addClass(this.trackSelection, 'hide');
628 this._addClass(this.trackHigh, 'hide');
629 }
630
631 this.handle1 = sliderMinHandle || this.handle1;
632 this.handle2 = sliderMaxHandle || this.handle2;
633
634 if (updateSlider === true) {
635 // Reset classes
636 this._removeClass(this.handle1, 'round triangle');
637 this._removeClass(this.handle2, 'round triangle hide');
638
639 for (i = 0; i < this.ticks.length; i++) {
640 this._removeClass(this.ticks[i], 'round triangle hide');
641 }
642 }
643
644 var availableHandleModifiers = ['round', 'triangle', 'custom'];
645 var isValidHandleType = availableHandleModifiers.indexOf(this.options.handle) !== -1;
646 if (isValidHandleType) {
647 this._addClass(this.handle1, this.options.handle);
648 this._addClass(this.handle2, this.options.handle);
649
650 for (i = 0; i < this.ticks.length; i++) {
651 this._addClass(this.ticks[i], this.options.handle);
652 }
653 }
654
655 this._state.offset = this._offset(this.sliderElem);
656 this._state.size = this.sliderElem[this.sizePos];
657 this.setValue(this._state.value);
658
659 /******************************************
660
661 Bind Event Listeners
662
663 ******************************************/
664
665 // Bind keyboard handlers
666 this.handle1Keydown = this._keydown.bind(this, 0);
667 this.handle1.addEventListener("keydown", this.handle1Keydown, false);
668
669 this.handle2Keydown = this._keydown.bind(this, 1);
670 this.handle2.addEventListener("keydown", this.handle2Keydown, false);
671
672 this.mousedown = this._mousedown.bind(this);
673 if (this.touchCapable) {
674 // Bind touch handlers
675 this.sliderElem.addEventListener("touchstart", this.mousedown, false);
676 }
677 this.sliderElem.addEventListener("mousedown", this.mousedown, false);
678
679
680 // Bind tooltip-related handlers
681 if(this.options.tooltip === 'hide') {
682 this._addClass(this.tooltip, 'hide');
683 this._addClass(this.tooltip_min, 'hide');
684 this._addClass(this.tooltip_max, 'hide');
685 }
686 else if(this.options.tooltip === 'always') {
687 this._showTooltip();
688 this._alwaysShowTooltip = true;
689 }
690 else {
691 this.showTooltip = this._showTooltip.bind(this);
692 this.hideTooltip = this._hideTooltip.bind(this);
693
694 this.sliderElem.addEventListener("mouseenter", this.showTooltip, false);
695 this.sliderElem.addEventListener("mouseleave", this.hideTooltip, false);
696
697 this.handle1.addEventListener("focus", this.showTooltip, false);
698 this.handle1.addEventListener("blur", this.hideTooltip, false);
699
700 this.handle2.addEventListener("focus", this.showTooltip, false);
701 this.handle2.addEventListener("blur", this.hideTooltip, false);
702 }
703
704 if(this.options.enabled) {
705 this.enable();
706 } else {
707 this.disable();
708 }
709 }
710
711
712
713 /*************************************************
714
715 INSTANCE PROPERTIES/METHODS
716
717 - Any methods bound to the prototype are considered
718 part of the plugin's `public` interface
719
720 **************************************************/
721 Slider.prototype = {
722 _init: function() {}, // NOTE: Must exist to support bridget
723
724 constructor: Slider,
725
726 defaultOptions: {
727 id: "",
728 min: 0,
729 max: 10,
730 step: 1,
731 precision: 0,
732 orientation: 'horizontal',
733 value: 5,
734 range: false,
735 selection: 'before',
736 tooltip: 'show',
737 tooltip_split: false,
738 handle: 'round',
739 reversed: false,
740 enabled: true,
741 formatter: function(val) {
742 if (Array.isArray(val)) {
743 return val[0] + " : " + val[1];
744 } else {
745 return val;
746 }
747 },
748 natural_arrow_keys: false,
749 ticks: [],
750 ticks_positions: [],
751 ticks_labels: [],
752 ticks_snap_bounds: 0,
753 scale: 'linear',
754 focus: false,
755 tooltip_position: null,
756 labelledby: null
757 },
758
759 getElement: function() {
760 return this.sliderElem;
761 },
762
763 getValue: function() {
764 if (this.options.range) {
765 return this._state.value;
766 }
767 else {
768 return this._state.value[0];
769 }
770 },
771
772 setValue: function(val, triggerSlideEvent, triggerChangeEvent) {
773 if (!val) {
774 val = 0;
775 }
776 var oldValue = this.getValue();
777 this._state.value = this._validateInputValue(val);
778 var applyPrecision = this._applyPrecision.bind(this);
779
780 if (this.options.range) {
781 this._state.value[0] = applyPrecision(this._state.value[0]);
782 this._state.value[1] = applyPrecision(this._state.value[1]);
783
784 this._state.value[0] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[0]));
785 this._state.value[1] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[1]));
786 }
787 else {
788 this._state.value = applyPrecision(this._state.value);
789 this._state.value = [ Math.max(this.options.min, Math.min(this.options.max, this._state.value))];
790 this._addClass(this.handle2, 'hide');
791 if (this.options.selection === 'after') {
792 this._state.value[1] = this.options.max;
793 } else {
794 this._state.value[1] = this.options.min;
795 }
796 }
797
798 if (this.options.max > this.options.min) {
799 this._state.percentage = [
800 this._toPercentage(this._state.value[0]),
801 this._toPercentage(this._state.value[1]),
802 this.options.step * 100 / (this.options.max - this.options.min)
803 ];
804 } else {
805 this._state.percentage = [0, 0, 100];
806 }
807
808 this._layout();
809 var newValue = this.options.range ? this._state.value : this._state.value[0];
810
811 if(triggerSlideEvent === true) {
812 this._trigger('slide', newValue);
813 }
814 if( (oldValue !== newValue) && (triggerChangeEvent === true) ) {
815 this._trigger('change', {
816 oldValue: oldValue,
817 newValue: newValue
818 });
819 }
820 this._setDataVal(newValue);
821
822 return this;
823 },
824
825 destroy: function(){
826 // Remove event handlers on slider elements
827 this._removeSliderEventHandlers();
828
829 // Remove the slider from the DOM
830 this.sliderElem.parentNode.removeChild(this.sliderElem);
831 /* Show original <input> element */
832 this.element.style.display = "";
833
834 // Clear out custom event bindings
835 this._cleanUpEventCallbacksMap();
836
837 // Remove data values
838 this.element.removeAttribute("data");
839
840 // Remove JQuery handlers/data
841 if($) {
842 this._unbindJQueryEventHandlers();
843 this.$element.removeData('slider');
844 }
845 },
846
847 disable: function() {
848 this._state.enabled = false;
849 this.handle1.removeAttribute("tabindex");
850 this.handle2.removeAttribute("tabindex");
851 this._addClass(this.sliderElem, 'slider-disabled');
852 this._trigger('slideDisabled');
853
854 return this;
855 },
856
857 enable: function() {
858 this._state.enabled = true;
859 this.handle1.setAttribute("tabindex", 0);
860 this.handle2.setAttribute("tabindex", 0);
861 this._removeClass(this.sliderElem, 'slider-disabled');
862 this._trigger('slideEnabled');
863
864 return this;
865 },
866
867 toggle: function() {
868 if(this._state.enabled) {
869 this.disable();
870 } else {
871 this.enable();
872 }
873 return this;
874 },
875
876 isEnabled: function() {
877 return this._state.enabled;
878 },
879
880 on: function(evt, callback) {
881 this._bindNonQueryEventHandler(evt, callback);
882 return this;
883 },
884
885 off: function(evt, callback) {
886 if($) {
887 this.$element.off(evt, callback);
888 this.$sliderElem.off(evt, callback);
889 } else {
890 this._unbindNonQueryEventHandler(evt, callback);
891 }
892 },
893
894 getAttribute: function(attribute) {
895 if(attribute) {
896 return this.options[attribute];
897 } else {
898 return this.options;
899 }
900 },
901
902 setAttribute: function(attribute, value) {
903 this.options[attribute] = value;
904 return this;
905 },
906
907 refresh: function() {
908 this._removeSliderEventHandlers();
909 createNewSlider.call(this, this.element, this.options);
910 if($) {
911 // Bind new instance of slider to the element
912 $.data(this.element, 'slider', this);
913 }
914 return this;
915 },
916
917 relayout: function() {
918 this._layout();
919 return this;
920 },
921
922 /******************************+
923
924 HELPERS
925
926 - Any method that is not part of the public interface.
927 - Place it underneath this comment block and write its signature like so:
928
929 _fnName : function() {...}
930
931 ********************************/
932 _removeSliderEventHandlers: function() {
933 // Remove keydown event listeners
934 this.handle1.removeEventListener("keydown", this.handle1Keydown, false);
935 this.handle2.removeEventListener("keydown", this.handle2Keydown, false);
936
937 if (this.showTooltip) {
938 this.handle1.removeEventListener("focus", this.showTooltip, false);
939 this.handle2.removeEventListener("focus", this.showTooltip, false);
940 }
941 if (this.hideTooltip) {
942 this.handle1.removeEventListener("blur", this.hideTooltip, false);
943 this.handle2.removeEventListener("blur", this.hideTooltip, false);
944 }
945
946 // Remove event listeners from sliderElem
947 if (this.showTooltip) {
948 this.sliderElem.removeEventListener("mouseenter", this.showTooltip, false);
949 }
950 if (this.hideTooltip) {
951 this.sliderElem.removeEventListener("mouseleave", this.hideTooltip, false);
952 }
953 this.sliderElem.removeEventListener("touchstart", this.mousedown, false);
954 this.sliderElem.removeEventListener("mousedown", this.mousedown, false);
955 },
956 _bindNonQueryEventHandler: function(evt, callback) {
957 if(this.eventToCallbackMap[evt] === undefined) {
958 this.eventToCallbackMap[evt] = [];
959 }
960 this.eventToCallbackMap[evt].push(callback);
961 },
962 _unbindNonQueryEventHandler: function(evt, callback) {
963 var callbacks = this.eventToCallbackMap[evt];
964 if(callbacks !== undefined) {
965 for (var i = 0; i < callbacks.length; i++) {
966 if (callbacks[i] === callback) {
967 callbacks.splice(i, 1);
968 break;
969 }
970 }
971 }
972 },
973 _cleanUpEventCallbacksMap: function() {
974 var eventNames = Object.keys(this.eventToCallbackMap);
975 for(var i = 0; i < eventNames.length; i++) {
976 var eventName = eventNames[i];
977 this.eventToCallbackMap[eventName] = null;
978 }
979 },
980 _showTooltip: function() {
981 if (this.options.tooltip_split === false ){
982 this._addClass(this.tooltip, 'in');
983 this.tooltip_min.style.display = 'none';
984 this.tooltip_max.style.display = 'none';
985 } else {
986 this._addClass(this.tooltip_min, 'in');
987 this._addClass(this.tooltip_max, 'in');
988 this.tooltip.style.display = 'none';
989 }
990 this._state.over = true;
991 },
992 _hideTooltip: function() {
993 if (this._state.inDrag === false && this.alwaysShowTooltip !== true) {
994 this._removeClass(this.tooltip, 'in');
995 this._removeClass(this.tooltip_min, 'in');
996 this._removeClass(this.tooltip_max, 'in');
997 }
998 this._state.over = false;
999 },
1000 _layout: function() {
1001 var positionPercentages;
1002
1003 if(this.options.reversed) {
1004 positionPercentages = [ 100 - this._state.percentage[0], this.options.range ? 100 - this._state.percentage[1] : this._state.percentage[1]];
1005 }
1006 else {
1007 positionPercentages = [ this._state.percentage[0], this._state.percentage[1] ];
1008 }
1009
1010 this.handle1.style[this.stylePos] = positionPercentages[0]+'%';
1011 this.handle1.setAttribute('aria-valuenow', this._state.value[0]);
1012
1013 this.handle2.style[this.stylePos] = positionPercentages[1]+'%';
1014 this.handle2.setAttribute('aria-valuenow', this._state.value[1]);
1015
1016 /* Position ticks and labels */
1017 if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {
1018
1019 var styleSize = this.options.orientation === 'vertical' ? 'height' : 'width';
1020 var styleMargin = this.options.orientation === 'vertical' ? 'marginTop' : 'marginLeft';
1021 var labelSize = this._state.size / (this.options.ticks.length - 1);
1022
1023 if (this.tickLabelContainer) {
1024 var extraMargin = 0;
1025 if (this.options.ticks_positions.length === 0) {
1026 if (this.options.orientation !== 'vertical') {
1027 this.tickLabelContainer.style[styleMargin] = -labelSize/2 + 'px';
1028 }
1029
1030 extraMargin = this.tickLabelContainer.offsetHeight;
1031 } else {
1032 /* Chidren are position absolute, calculate height by finding the max offsetHeight of a child */
1033 for (i = 0 ; i < this.tickLabelContainer.childNodes.length; i++) {
1034 if (this.tickLabelContainer.childNodes[i].offsetHeight > extraMargin) {
1035 extraMargin = this.tickLabelContainer.childNodes[i].offsetHeight;
1036 }
1037 }
1038 }
1039 if (this.options.orientation === 'horizontal') {
1040 this.sliderElem.style.marginBottom = extraMargin + 'px';
1041 }
1042 }
1043 for (var i = 0; i < this.options.ticks.length; i++) {
1044
1045 var percentage = this.options.ticks_positions[i] || this._toPercentage(this.options.ticks[i]);
1046
1047 if (this.options.reversed) {
1048 percentage = 100 - percentage;
1049 }
1050
1051 this.ticks[i].style[this.stylePos] = percentage + '%';
1052
1053 /* Set class labels to denote whether ticks are in the selection */
1054 this._removeClass(this.ticks[i], 'in-selection');
1055 if (!this.options.range) {
1056 if (this.options.selection === 'after' && percentage >= positionPercentages[0]){
1057 this._addClass(this.ticks[i], 'in-selection');
1058 } else if (this.options.selection === 'before' && percentage <= positionPercentages[0]) {
1059 this._addClass(this.ticks[i], 'in-selection');
1060 }
1061 } else if (percentage >= positionPercentages[0] && percentage <= positionPercentages[1]) {
1062 this._addClass(this.ticks[i], 'in-selection');
1063 }
1064
1065 if (this.tickLabels[i]) {
1066 this.tickLabels[i].style[styleSize] = labelSize + 'px';
1067
1068 if (this.options.orientation !== 'vertical' && this.options.ticks_positions[i] !== undefined) {
1069 this.tickLabels[i].style.position = 'absolute';
1070 this.tickLabels[i].style[this.stylePos] = percentage + '%';
1071 this.tickLabels[i].style[styleMargin] = -labelSize/2 + 'px';
1072 } else if (this.options.orientation === 'vertical') {
1073 this.tickLabels[i].style['marginLeft'] = this.sliderElem.offsetWidth + 'px';
1074 this.tickLabelContainer.style['marginTop'] = this.sliderElem.offsetWidth / 2 * -1 + 'px';
1075 }
1076 }
1077 }
1078 }
1079
1080 var formattedTooltipVal;
1081
1082 if (this.options.range) {
1083 formattedTooltipVal = this.options.formatter(this._state.value);
1084 this._setText(this.tooltipInner, formattedTooltipVal);
1085 this.tooltip.style[this.stylePos] = (positionPercentages[1] + positionPercentages[0])/2 + '%';
1086
1087 if (this.options.orientation === 'vertical') {
1088 this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px');
1089 } else {
1090 this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px');
1091 }
1092
1093 if (this.options.orientation === 'vertical') {
1094 this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px');
1095 } else {
1096 this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px');
1097 }
1098
1099 var innerTooltipMinText = this.options.formatter(this._state.value[0]);
1100 this._setText(this.tooltipInner_min, innerTooltipMinText);
1101
1102 var innerTooltipMaxText = this.options.formatter(this._state.value[1]);
1103 this._setText(this.tooltipInner_max, innerTooltipMaxText);
1104
1105 this.tooltip_min.style[this.stylePos] = positionPercentages[0] + '%';
1106
1107 if (this.options.orientation === 'vertical') {
1108 this._css(this.tooltip_min, 'margin-top', -this.tooltip_min.offsetHeight / 2 + 'px');
1109 } else {
1110 this._css(this.tooltip_min, 'margin-left', -this.tooltip_min.offsetWidth / 2 + 'px');
1111 }
1112
1113 this.tooltip_max.style[this.stylePos] = positionPercentages[1] + '%';
1114
1115 if (this.options.orientation === 'vertical') {
1116 this._css(this.tooltip_max, 'margin-top', -this.tooltip_max.offsetHeight / 2 + 'px');
1117 } else {
1118 this._css(this.tooltip_max, 'margin-left', -this.tooltip_max.offsetWidth / 2 + 'px');
1119 }
1120 } else {
1121 formattedTooltipVal = this.options.formatter(this._state.value[0]);
1122 this._setText(this.tooltipInner, formattedTooltipVal);
1123
1124 this.tooltip.style[this.stylePos] = positionPercentages[0] + '%';
1125 if (this.options.orientation === 'vertical') {
1126 this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px');
1127 } else {
1128 this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px');
1129 }
1130 }
1131
1132 if (this.options.orientation === 'vertical') {
1133 this.trackLow.style.top = '0';
1134 this.trackLow.style.height = Math.min(positionPercentages[0], positionPercentages[1]) +'%';
1135
1136 this.trackSelection.style.top = Math.min(positionPercentages[0], positionPercentages[1]) +'%';
1137 this.trackSelection.style.height = Math.abs(positionPercentages[0] - positionPercentages[1]) +'%';
1138
1139 this.trackHigh.style.bottom = '0';
1140 this.trackHigh.style.height = (100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1])) +'%';
1141 }
1142 else {
1143 this.trackLow.style.left = '0';
1144 this.trackLow.style.width = Math.min(positionPercentages[0], positionPercentages[1]) +'%';
1145
1146 this.trackSelection.style.left = Math.min(positionPercentages[0], positionPercentages[1]) +'%';
1147 this.trackSelection.style.width = Math.abs(positionPercentages[0] - positionPercentages[1]) +'%';
1148
1149 this.trackHigh.style.right = '0';
1150 this.trackHigh.style.width = (100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1])) +'%';
1151
1152 var offset_min = this.tooltip_min.getBoundingClientRect();
1153 var offset_max = this.tooltip_max.getBoundingClientRect();
1154
1155 if (offset_min.right > offset_max.left) {
1156 this._removeClass(this.tooltip_max, 'top');
1157 this._addClass(this.tooltip_max, 'bottom');
1158 this.tooltip_max.style.top = 18 + 'px';
1159 } else {
1160 this._removeClass(this.tooltip_max, 'bottom');
1161 this._addClass(this.tooltip_max, 'top');
1162 this.tooltip_max.style.top = this.tooltip_min.style.top;
1163 }
1164 }
1165 },
1166 _removeProperty: function(element, prop) {
1167 if (element.style.removeProperty) {
1168 element.style.removeProperty(prop);
1169 } else {
1170 element.style.removeAttribute(prop);
1171 }
1172 },
1173 _mousedown: function(ev) {
1174 if(!this._state.enabled) {
1175 return false;
1176 }
1177
1178 this._state.offset = this._offset(this.sliderElem);
1179 this._state.size = this.sliderElem[this.sizePos];
1180
1181 var percentage = this._getPercentage(ev);
1182
1183 if (this.options.range) {
1184 var diff1 = Math.abs(this._state.percentage[0] - percentage);
1185 var diff2 = Math.abs(this._state.percentage[1] - percentage);
1186 this._state.dragged = (diff1 < diff2) ? 0 : 1;
1187 } else {
1188 this._state.dragged = 0;
1189 }
1190
1191 this._state.percentage[this._state.dragged] = percentage;
1192 this._layout();
1193
1194 if (this.touchCapable) {
1195 document.removeEventListener("touchmove", this.mousemove, false);
1196 document.removeEventListener("touchend", this.mouseup, false);
1197 }
1198
1199 if(this.mousemove){
1200 document.removeEventListener("mousemove", this.mousemove, false);
1201 }
1202 if(this.mouseup){
1203 document.removeEventListener("mouseup", this.mouseup, false);
1204 }
1205
1206 this.mousemove = this._mousemove.bind(this);
1207 this.mouseup = this._mouseup.bind(this);
1208
1209 if (this.touchCapable) {
1210 // Touch: Bind touch events:
1211 document.addEventListener("touchmove", this.mousemove, false);
1212 document.addEventListener("touchend", this.mouseup, false);
1213 }
1214 // Bind mouse events:
1215 document.addEventListener("mousemove", this.mousemove, false);
1216 document.addEventListener("mouseup", this.mouseup, false);
1217
1218 this._state.inDrag = true;
1219 var newValue = this._calculateValue();
1220
1221 this._trigger('slideStart', newValue);
1222
1223 this._setDataVal(newValue);
1224 this.setValue(newValue, false, true);
1225
1226 this._pauseEvent(ev);
1227
1228 if (this.options.focus) {
1229 this._triggerFocusOnHandle(this._state.dragged);
1230 }
1231
1232 return true;
1233 },
1234 _triggerFocusOnHandle: function(handleIdx) {
1235 if(handleIdx === 0) {
1236 this.handle1.focus();
1237 }
1238 if(handleIdx === 1) {
1239 this.handle2.focus();
1240 }
1241 },
1242 _keydown: function(handleIdx, ev) {
1243 if(!this._state.enabled) {
1244 return false;
1245 }
1246
1247 var dir;
1248 switch (ev.keyCode) {
1249 case 37: // left
1250 case 40: // down
1251 dir = -1;
1252 break;
1253 case 39: // right
1254 case 38: // up
1255 dir = 1;
1256 break;
1257 }
1258 if (!dir) {
1259 return;
1260 }
1261
1262 // use natural arrow keys instead of from min to max
1263 if (this.options.natural_arrow_keys) {
1264 var ifVerticalAndNotReversed = (this.options.orientation === 'vertical' && !this.options.reversed);
1265 var ifHorizontalAndReversed = (this.options.orientation === 'horizontal' && this.options.reversed);
1266
1267 if (ifVerticalAndNotReversed || ifHorizontalAndReversed) {
1268 dir = -dir;
1269 }
1270 }
1271
1272 var val = this._state.value[handleIdx] + dir * this.options.step;
1273 if (this.options.range) {
1274 val = [ (!handleIdx) ? val : this._state.value[0],
1275 ( handleIdx) ? val : this._state.value[1]];
1276 }
1277
1278 this._trigger('slideStart', val);
1279 this._setDataVal(val);
1280 this.setValue(val, true, true);
1281
1282 this._setDataVal(val);
1283 this._trigger('slideStop', val);
1284 this._layout();
1285
1286 this._pauseEvent(ev);
1287
1288 return false;
1289 },
1290 _pauseEvent: function(ev) {
1291 if(ev.stopPropagation) {
1292 ev.stopPropagation();
1293 }
1294 if(ev.preventDefault) {
1295 ev.preventDefault();
1296 }
1297 ev.cancelBubble=true;
1298 ev.returnValue=false;
1299 },
1300 _mousemove: function(ev) {
1301 if(!this._state.enabled) {
1302 return false;
1303 }
1304
1305 var percentage = this._getPercentage(ev);
1306 this._adjustPercentageForRangeSliders(percentage);
1307 this._state.percentage[this._state.dragged] = percentage;
1308 this._layout();
1309
1310 var val = this._calculateValue(true);
1311 this.setValue(val, true, true);
1312
1313 return false;
1314 },
1315 _adjustPercentageForRangeSliders: function(percentage) {
1316 if (this.options.range) {
1317 var precision = this._getNumDigitsAfterDecimalPlace(percentage);
1318 precision = precision ? precision - 1 : 0;
1319 var percentageWithAdjustedPrecision = this._applyToFixedAndParseFloat(percentage, precision);
1320 if (this._state.dragged === 0 && this._applyToFixedAndParseFloat(this._state.percentage[1], precision) < percentageWithAdjustedPrecision) {
1321 this._state.percentage[0] = this._state.percentage[1];
1322 this._state.dragged = 1;
1323 } else if (this._state.dragged === 1 && this._applyToFixedAndParseFloat(this._state.percentage[0], precision) > percentageWithAdjustedPrecision) {
1324 this._state.percentage[1] = this._state.percentage[0];
1325 this._state.dragged = 0;
1326 }
1327 }
1328 },
1329 _mouseup: function() {
1330 if(!this._state.enabled) {
1331 return false;
1332 }
1333 if (this.touchCapable) {
1334 // Touch: Unbind touch event handlers:
1335 document.removeEventListener("touchmove", this.mousemove, false);
1336 document.removeEventListener("touchend", this.mouseup, false);
1337 }
1338 // Unbind mouse event handlers:
1339 document.removeEventListener("mousemove", this.mousemove, false);
1340 document.removeEventListener("mouseup", this.mouseup, false);
1341
1342 this._state.inDrag = false;
1343 if (this._state.over === false) {
1344 this._hideTooltip();
1345 }
1346 var val = this._calculateValue(true);
1347
1348 this._layout();
1349 this._setDataVal(val);
1350 this._trigger('slideStop', val);
1351
1352 return false;
1353 },
1354 _calculateValue: function(snapToClosestTick) {
1355 var val;
1356 if (this.options.range) {
1357 val = [this.options.min,this.options.max];
1358 if (this._state.percentage[0] !== 0){
1359 val[0] = this._toValue(this._state.percentage[0]);
1360 val[0] = this._applyPrecision(val[0]);
1361 }
1362 if (this._state.percentage[1] !== 100){
1363 val[1] = this._toValue(this._state.percentage[1]);
1364 val[1] = this._applyPrecision(val[1]);
1365 }
1366 } else {
1367 val = this._toValue(this._state.percentage[0]);
1368 val = parseFloat(val);
1369 val = this._applyPrecision(val);
1370 }
1371
1372 if (snapToClosestTick) {
1373 var min = [val, Infinity];
1374 for (var i = 0; i < this.options.ticks.length; i++) {
1375 var diff = Math.abs(this.options.ticks[i] - val);
1376 if (diff <= min[1]) {
1377 min = [this.options.ticks[i], diff];
1378 }
1379 }
1380 if (min[1] <= this.options.ticks_snap_bounds) {
1381 return min[0];
1382 }
1383 }
1384
1385 return val;
1386 },
1387 _applyPrecision: function(val) {
1388 var precision = this.options.precision || this._getNumDigitsAfterDecimalPlace(this.options.step);
1389 return this._applyToFixedAndParseFloat(val, precision);
1390 },
1391 _getNumDigitsAfterDecimalPlace: function(num) {
1392 var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
1393 if (!match) { return 0; }
1394 return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
1395 },
1396 _applyToFixedAndParseFloat: function(num, toFixedInput) {
1397 var truncatedNum = num.toFixed(toFixedInput);
1398 return parseFloat(truncatedNum);
1399 },
1400 /*
1401 Credits to Mike Samuel for the following method!
1402 Source: http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number
1403 */
1404 _getPercentage: function(ev) {
1405 if (this.touchCapable && (ev.type === 'touchstart' || ev.type === 'touchmove')) {
1406 ev = ev.touches[0];
1407 }
1408
1409 var eventPosition = ev[this.mousePos];
1410 var sliderOffset = this._state.offset[this.stylePos];
1411 var distanceToSlide = eventPosition - sliderOffset;
1412 // Calculate what percent of the length the slider handle has slid
1413 var percentage = (distanceToSlide / this._state.size) * 100;
1414 percentage = Math.round(percentage / this._state.percentage[2]) * this._state.percentage[2];
1415 if (this.options.reversed) {
1416 percentage = 100 - percentage;
1417 }
1418
1419 // Make sure the percent is within the bounds of the slider.
1420 // 0% corresponds to the 'min' value of the slide
1421 // 100% corresponds to the 'max' value of the slide
1422 return Math.max(0, Math.min(100, percentage));
1423 },
1424 _validateInputValue: function(val) {
1425 if (typeof val === 'number') {
1426 return val;
1427 } else if (Array.isArray(val)) {
1428 this._validateArray(val);
1429 return val;
1430 } else {
1431 throw new Error( ErrorMsgs.formatInvalidInputErrorMsg(val) );
1432 }
1433 },
1434 _validateArray: function(val) {
1435 for(var i = 0; i < val.length; i++) {
1436 var input = val[i];
1437 if (typeof input !== 'number') { throw new Error( ErrorMsgs.formatInvalidInputErrorMsg(input) ); }
1438 }
1439 },
1440 _setDataVal: function(val) {
1441 this.element.setAttribute('data-value', val);
1442 this.element.setAttribute('value', val);
1443 this.element.value = val;
1444 },
1445 _trigger: function(evt, val) {
1446 val = (val || val === 0) ? val : undefined;
1447
1448 var callbackFnArray = this.eventToCallbackMap[evt];
1449 if(callbackFnArray && callbackFnArray.length) {
1450 for(var i = 0; i < callbackFnArray.length; i++) {
1451 var callbackFn = callbackFnArray[i];
1452 callbackFn(val);
1453 }
1454 }
1455
1456 /* If JQuery exists, trigger JQuery events */
1457 if($) {
1458 this._triggerJQueryEvent(evt, val);
1459 }
1460 },
1461 _triggerJQueryEvent: function(evt, val) {
1462 var eventData = {
1463 type: evt,
1464 value: val
1465 };
1466 this.$element.trigger(eventData);
1467 this.$sliderElem.trigger(eventData);
1468 },
1469 _unbindJQueryEventHandlers: function() {
1470 this.$element.off();
1471 this.$sliderElem.off();
1472 },
1473 _setText: function(element, text) {
1474 if(typeof element.innerText !== "undefined") {
1475 element.innerText = text;
1476 } else if(typeof element.textContent !== "undefined") {
1477 element.textContent = text;
1478 }
1479 },
1480 _removeClass: function(element, classString) {
1481 var classes = classString.split(" ");
1482 var newClasses = element.className;
1483
1484 for(var i = 0; i < classes.length; i++) {
1485 var classTag = classes[i];
1486 var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)");
1487 newClasses = newClasses.replace(regex, " ");
1488 }
1489
1490 element.className = newClasses.trim();
1491 },
1492 _addClass: function(element, classString) {
1493 var classes = classString.split(" ");
1494 var newClasses = element.className;
1495
1496 for(var i = 0; i < classes.length; i++) {
1497 var classTag = classes[i];
1498 var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)");
1499 var ifClassExists = regex.test(newClasses);
1500
1501 if(!ifClassExists) {
1502 newClasses += " " + classTag;
1503 }
1504 }
1505
1506 element.className = newClasses.trim();
1507 },
1508 _offsetLeft: function(obj){
1509 return obj.getBoundingClientRect().left;
1510 },
1511 _offsetTop: function(obj){
1512 var offsetTop = obj.offsetTop;
1513 while((obj = obj.offsetParent) && !isNaN(obj.offsetTop)){
1514 offsetTop += obj.offsetTop;
1515 }
1516 return offsetTop;
1517 },
1518 _offset: function (obj) {
1519 return {
1520 left: this._offsetLeft(obj),
1521 top: this._offsetTop(obj)
1522 };
1523 },
1524 _css: function(elementRef, styleName, value) {
1525 if ($) {
1526 $.style(elementRef, styleName, value);
1527 } else {
1528 var style = styleName.replace(/^-ms-/, "ms-").replace(/-([\da-z])/gi, function (all, letter) {
1529 return letter.toUpperCase();
1530 });
1531 elementRef.style[style] = value;
1532 }
1533 },
1534 _toValue: function(percentage) {
1535 return this.options.scale.toValue.apply(this, [percentage]);
1536 },
1537 _toPercentage: function(value) {
1538 return this.options.scale.toPercentage.apply(this, [value]);
1539 },
1540 _setTooltipPosition: function(){
1541 var tooltips = [this.tooltip, this.tooltip_min, this.tooltip_max];
1542 if (this.options.orientation === 'vertical'){
1543 var tooltipPos = this.options.tooltip_position || 'right';
1544 var oppositeSide = (tooltipPos === 'left') ? 'right' : 'left';
1545 tooltips.forEach(function(tooltip){
1546 this._addClass(tooltip, tooltipPos);
1547 tooltip.style[oppositeSide] = '100%';
1548 }.bind(this));
1549 } else if(this.options.tooltip_position === 'bottom') {
1550 tooltips.forEach(function(tooltip){
1551 this._addClass(tooltip, 'bottom');
1552 tooltip.style.top = 22 + 'px';
1553 }.bind(this));
1554 } else {
1555 tooltips.forEach(function(tooltip){
1556 this._addClass(tooltip, 'top');
1557 tooltip.style.top = -this.tooltip.outerHeight - 14 + 'px';
1558 }.bind(this));
1559 }
1560 }
1561 };
1562
1563 /*********************************
1564
1565 Attach to global namespace
1566
1567 *********************************/
1568 if($) {
1569 var namespace = $.fn.slider ? 'bootstrapSlider' : 'slider';
1570 $.bridget(namespace, Slider);
1571 }
1572
1573 })( $ );
1574
1575 return Slider;
1576}));