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