Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / pybind / mgr / dashboard / static / AdminLTE-2.3.7 / plugins / bootstrap-slider / bootstrap-slider.js
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 }));