vendor: 116,026 bytes, lines 1-2663
1/*! angularjs-slider - v6.5.0 - 2 (c) Rafal Zajac <[email protected]>, Valentin Hervieu <[email protected]>, Jussi Saarivirta <[email protected]>, Angelin Sirbu <[email protected]> - 3 https://github.com/angular-slider/angularjs-slider - 4 2018-02-07 */ 5/*jslint unparam: true */ 6/*global angular: false, console: false, define, module */ 7;(function(root, factory) { 8 'use strict' 9 /* istanbul ignore next */ 10 if (typeof define === 'function' && define.amd) { 11 // AMD. Register as an anonymous module. 12 define(['angular'], factory) 13 } else if (typeof module === 'object' && module.exports) { 14 // Node. Does not work with strict CommonJS, but 15 // only CommonJS-like environments that support module.exports, 16 // like Node. 17 // to support bundler like browserify 18 var angularObj = angular || require('angular') 19 if ((!angularObj || !angularObj.module) && typeof angular != 'undefined') { 20 angularObj = angular 21 } 22 module.exports = factory(angularObj) 23 } else { 24 // Browser globals (root is window) 25 factory(root.angular) 26 } 27})(this, function(angular) { 28 'use strict' 29 var module = angular 30 .module('rzModule', []) 31 .factory('RzSliderOptions', function() { 32 var defaultOptions = { 33 floor: 0, 34 ceil: null, //defaults to rz-slider-model 35 step: 1, 36 precision: 0, 37 minRange: null, 38 maxRange: null, 39 pushRange: false, 40 minLimit: null, 41 maxLimit: null, 42 id: null, 43 translate: null, 44 getLegend: null, 45 stepsArray: null, 46 bindIndexForStepsArray: false, 47 draggableRange: false, 48 draggableRangeOnly: false, 49 showSelectionBar: false, 50 showSelectionBarEnd: false, 51 showSelectionBarFromValue: null, 52 showOuterSelectionBars: false, 53 hidePointerLabels: false, 54 hideLimitLabels: false, 55 autoHideLimitLabels: true, 56 readOnly: false, 57 disabled: false, 58 interval: 350, 59 showTicks: false, 60 showTicksValues: false, 61 ticksArray: null, 62 ticksTooltip: null, 63 ticksValuesTooltip: null, 64 vertical: false, 65 getSelectionBarColor: null, 66 getTickColor: null, 67 getPointerColor: null, 68 keyboardSupport: true, 69 scale: 1, 70 enforceStep: true, 71 enforceRange: false, 72 noSwitching: false, 73 onlyBindHandles: false, 74 onStart: null, 75 onChange: null, 76 onEnd: null, 77 rightToLeft: false, 78 reversedControls: false, 79 boundPointerLabels: true, 80 mergeRangeLabelsIfSame: false, 81 labelOverlapSeparator: ' - ', 82 customTemplateScope: null, 83 logScale: false, 84 customValueToPosition: null, 85 customPositionToValue: null, 86 selectionBarGradient: null, 87 ariaLabel: null, 88 ariaLabelledBy: null, 89 ariaLabelHigh: null, 90 ariaLabelledByHigh: null, 91 } 92 var globalOptions = {} 93 94 var factory = {} 95 /** 96 * `options({})` allows global configuration of all sliders in the 97 * application. 98 * 99 * var app = angular.module( 'App', ['rzModule'], function( RzSliderOptions ) { 100 * // show ticks for all sliders 101 * RzSliderOptions.options( { showTicks: true } ); 102 * }); 103 */ 104 factory.options = function(value) { 105 angular.extend(globalOptions, value) 106 } 107 108 factory.getOptions = function(options) { 109 return angular.extend({}, defaultOptions, globalOptions, options) 110 } 111 112 return factory 113 }) 114 .factory('rzThrottle', ['$timeout', function($timeout) { 115 /** 116 * rzThrottle 117 * 118 * Taken from underscore project 119 * 120 * @param {Function} func 121 * @param {number} wait 122 * @param {ThrottleOptions} options 123 * @returns {Function} 124 */ 125 return function(func, wait, options) { 126 'use strict' 127 /* istanbul ignore next */ 128 var getTime = 129 Date.now || 130 function() { 131 return new Date().getTime() 132 } 133 var context, args, result 134 var timeout = null 135 var previous = 0 136 options = options || {} 137 var later = function() { 138 previous = getTime() 139 timeout = null 140 result = func.apply(context, args) 141 context = args = null 142 } 143 return function() { 144 var now = getTime() 145 var remaining = wait - (now - previous) 146 context = this 147 args = arguments 148 if (remaining <= 0) { 149 $timeout.cancel(timeout) 150 timeout = null 151 previous = now 152 result = func.apply(context, args) 153 context = args = null 154 } else if (!timeout && options.trailing !== false) { 155 timeout = $timeout(later, remaining) 156 } 157 return result 158 } 159 } 160 }]) 161 .factory('RzSlider', ['$timeout', '$document', '$window', '$compile', 'RzSliderOptions', 'rzThrottle', function( 162 $timeout, 163 $document, 164 $window, 165 $compile, 166 RzSliderOptions, 167 rzThrottle 168 ) { 169 'use strict' 170 171 /** 172 * Slider 173 * 174 * @param {ngScope} scope The AngularJS scope 175 * @param {Element} sliderElem The slider directive element wrapped in jqLite 176 * @constructor 177 */ 178 var Slider = function(scope, sliderElem) { 179 /** 180 * The slider's scope 181 * 182 * @type {ngScope} 183 */ 184 this.scope = scope 185 186 /** 187 * The slider inner low value (linked to rzSliderModel) 188 * @type {number} 189 */ 190 this.lowValue = 0 191 192 /** 193 * The slider inner high value (linked to rzSliderHigh) 194 * @type {number} 195 */ 196 this.highValue = 0 197 198 /** 199 * Slider element wrapped in jqLite 200 * 201 * @type {jqLite} 202 */ 203 this.sliderElem = sliderElem 204 205 /** 206 * Slider type 207 * 208 * @type {boolean} Set to true for range slider 209 */ 210 this.range = 211 this.scope.rzSliderModel !== undefined && 212 this.scope.rzSliderHigh !== undefined 213 214 /** 215 * Values recorded when first dragging the bar 216 * 217 * @type {Object} 218 */ 219 this.dragging = { 220 active: false, 221 value: 0, 222 difference: 0, 223 position: 0, 224 lowLimit: 0, 225 highLimit: 0, 226 } 227 228 /** 229 * property that handle position (defaults to left for horizontal) 230 * @type {string} 231 */ 232 this.positionProperty = 'left' 233 234 /** 235 * property that handle dimension (defaults to width for horizontal) 236 * @type {string} 237 */ 238 this.dimensionProperty = 'width' 239 240 /** 241 * Half of the width or height of the slider handles 242 * 243 * @type {number} 244 */ 245 this.handleHalfDim = 0 246 247 /** 248 * Maximum position the slider handle can have 249 * 250 * @type {number} 251 */ 252 this.maxPos = 0 253 254 /** 255 * Precision 256 * 257 * @type {number} 258 */ 259 this.precision = 0 260 261 /** 262 * Step 263 * 264 * @type {number} 265 */ 266 this.step = 1 267 268 /** 269 * The name of the handle we are currently tracking 270 * 271 * @type {string} 272 */ 273 this.tracking = '' 274 275 /** 276 * Minimum value (floor) of the model 277 * 278 * @type {number} 279 */ 280 this.minValue = 0 281 282 /** 283 * Maximum value (ceiling) of the model 284 * 285 * @type {number} 286 */ 287 this.maxValue = 0 288 289 /** 290 * The delta between min and max value 291 * 292 * @type {number} 293 */ 294 this.valueRange = 0 295 296 /** 297 * If showTicks/showTicksValues options are number. 298 * In this case, ticks values should be displayed below the slider. 299 * @type {boolean} 300 */ 301 this.intermediateTicks = false 302 303 /** 304 * Set to true if init method already executed 305 * 306 * @type {boolean} 307 */ 308 this.initHasRun = false 309 310 /** 311 * Used to call onStart on the first keydown event 312 * 313 * @type {boolean} 314 */ 315 this.firstKeyDown = false 316 317 /** 318 * Internal flag to prevent watchers to be called when the sliders value are modified internally. 319 * @type {boolean} 320 */ 321 this.internalChange = false 322 323 /** 324 * Internal flag to keep track of the visibility of combo label 325 * @type {boolean} 326 */ 327 this.cmbLabelShown = false 328 329 /** 330 * Internal variable to keep track of the focus element 331 */ 332 this.currentFocusElement = null 333 334 // Slider DOM elements wrapped in jqLite 335 this.fullBar = null // The whole slider bar 336 this.selBar = null // Highlight between two handles 337 this.minH = null // Left slider handle 338 this.maxH = null // Right slider handle 339 this.flrLab = null // Floor label 340 this.ceilLab = null // Ceiling label 341 this.minLab = null // Label above the low value 342 this.maxLab = null // Label above the high value 343 this.cmbLab = null // Combined label 344 this.ticks = null // The ticks 345 346 // Initialize slider 347 this.init() 348 } 349 350 // Add instance methods 351 Slider.prototype = { 352 /** 353 * Initialize slider 354 * 355 * @returns {undefined} 356 */ 357 init: function() { 358 var thrLow, 359 thrHigh, 360 self = this 361 362 var calcDimFn = function() { 363 self.calcViewDimensions() 364 } 365 366 this.applyOptions() 367 this.syncLowValue() 368 if (this.range) this.syncHighValue() 369 this.initElemHandles() 370 this.manageElementsStyle() 371 this.setDisabledState() 372 this.calcViewDimensions() 373 this.setMinAndMax() 374 this.addAccessibility() 375 this.updateCeilLab() 376 this.updateFloorLab() 377 this.initHandles() 378 this.manageEventsBindings() 379 380 // Recalculate slider view dimensions 381 this.scope.$on('reCalcViewDimensions', calcDimFn) 382 383 // Recalculate stuff if view port dimensions have changed 384 angular.element($window).on('resize', calcDimFn) 385 386 this.initHasRun = true 387 388 // Watch for changes to the model 389 thrLow = rzThrottle(function() { 390 self.onLowHandleChange() 391 }, self.options.interval) 392 393 thrHigh = rzThrottle(function() { 394 self.onHighHandleChange() 395 }, self.options.interval) 396 397 this.scope.$on('rzSliderForceRender', function() { 398 self.resetLabelsValue() 399 thrLow() 400 if (self.range) { 401 thrHigh() 402 } 403 self.resetSlider() 404 }) 405 406 // Watchers (order is important because in case of simultaneous change, 407 // watchers will be called in the same order) 408 this.scope.$watchCollection('rzSliderOptions()', function( 409 newValue, 410 oldValue 411 ) { 412 if (newValue === oldValue) return 413 self.applyOptions() // need to be called before synchronizing the values 414 self.syncLowValue() 415 if (self.range) self.syncHighValue() 416 self.resetSlider() 417 }) 418 419 this.scope.$watch('rzSliderModel', function(newValue, oldValue) { 420 if (self.internalChange) return 421 if (newValue === oldValue) return 422 thrLow() 423 }) 424 425 this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { 426 if (self.internalChange) return 427 if (newValue === oldValue) return 428 if (newValue != null) thrHigh() 429 if ( 430 (self.range && newValue == null) || 431 (!self.range && newValue != null) 432 ) { 433 self.applyOptions() 434 self.resetSlider() 435 } 436 }) 437 438 this.scope.$on('$destroy', function() { 439 self.unbindEvents() 440 angular.element($window).off('resize', calcDimFn) 441 self.currentFocusElement = null 442 }) 443 }, 444 445 findStepIndex: function(modelValue) { 446 var index = 0 447 for (var i = 0; i < this.options.stepsArray.length; i++) { 448 var step = this.options.stepsArray[i] 449 if (step === modelValue) { 450 index = i 451 break 452 } else if (angular.isDate(step)) { 453 if (step.getTime() === modelValue.getTime()) { 454 index = i 455 break 456 } 457 } else if (angular.isObject(step)) { 458 if ( 459 (angular.isDate(step.value) && 460 step.value.getTime() === modelValue.getTime()) || 461 step.value === modelValue 462 ) { 463 index = i 464 break 465 } 466 } 467 } 468 return index 469 }, 470 471 syncLowValue: function() { 472 if (this.options.stepsArray) { 473 if (!this.options.bindIndexForStepsArray) 474 this.lowValue = this.findStepIndex(this.scope.rzSliderModel) 475 else this.lowValue = this.scope.rzSliderModel 476 } else this.lowValue = this.scope.rzSliderModel 477 }, 478 479 syncHighValue: function() { 480 if (this.options.stepsArray) { 481 if (!this.options.bindIndexForStepsArray) 482 this.highValue = this.findStepIndex(this.scope.rzSliderHigh) 483 else this.highValue = this.scope.rzSliderHigh 484 } else this.highValue = this.scope.rzSliderHigh 485 }, 486 487 getStepValue: function(sliderValue) { 488 var step = this.options.stepsArray[sliderValue] 489 if (angular.isDate(step)) return step 490 if (angular.isObject(step)) return step.value 491 return step 492 }, 493 494 applyLowValue: function() { 495 if (this.options.stepsArray) { 496 if (!this.options.bindIndexForStepsArray) 497 this.scope.rzSliderModel = this.getStepValue(this.lowValue) 498 else this.scope.rzSliderModel = this.lowValue 499 } else this.scope.rzSliderModel = this.lowValue 500 }, 501 502 applyHighValue: function() { 503 if (this.options.stepsArray) { 504 if (!this.options.bindIndexForStepsArray) 505 this.scope.rzSliderHigh = this.getStepValue(this.highValue) 506 else this.scope.rzSliderHigh = this.highValue 507 } else this.scope.rzSliderHigh = this.highValue 508 }, 509 510 /* 511 * Reflow the slider when the low handle changes (called with throttle) 512 */ 513 onLowHandleChange: function() { 514 this.syncLowValue() 515 if (this.range) this.syncHighValue() 516 this.setMinAndMax() 517 this.updateLowHandle(this.valueToPosition(this.lowValue)) 518 this.updateSelectionBar() 519 this.updateTicksScale() 520 this.updateAriaAttributes() 521 if (this.range) { 522 this.updateCmbLabel() 523 } 524 }, 525 526 /* 527 * Reflow the slider when the high handle changes (called with throttle) 528 */ 529 onHighHandleChange: function() { 530 this.syncLowValue() 531 this.syncHighValue() 532 this.setMinAndMax() 533 this.updateHighHandle(this.valueToPosition(this.highValue)) 534 this.updateSelectionBar() 535 this.updateTicksScale() 536 this.updateCmbLabel() 537 this.updateAriaAttributes() 538 }, 539 540 /** 541 * Read the user options and apply them to the slider model 542 */ 543 applyOptions: function() { 544 var sliderOptions 545 if (this.scope.rzSliderOptions) 546 sliderOptions = this.scope.rzSliderOptions() 547 else sliderOptions = {} 548 549 this.options = RzSliderOptions.getOptions(sliderOptions) 550 551 if (this.options.step <= 0) this.options.step = 1 552 553 this.range = 554 this.scope.rzSliderModel !== undefined && 555 this.scope.rzSliderHigh !== undefined 556 this.options.draggableRange = 557 this.range && this.options.draggableRange 558 this.options.draggableRangeOnly = 559 this.range && this.options.draggableRangeOnly 560 if (this.options.draggableRangeOnly) { 561 this.options.draggableRange = true 562 } 563 564 this.options.showTicks = 565 this.options.showTicks || 566 this.options.showTicksValues || 567 !!this.options.ticksArray 568 this.scope.showTicks = this.options.showTicks //scope is used in the template 569 if ( 570 angular.isNumber(this.options.showTicks) || 571 this.options.ticksArray 572 ) 573 this.intermediateTicks = true 574 575 this.options.showSelectionBar = 576 this.options.showSelectionBar || 577 this.options.showSelectionBarEnd || 578 this.options.showSelectionBarFromValue !== null 579 580 if (this.options.stepsArray) { 581 this.parseStepsArray() 582 } else { 583 if (this.options.translate) this.customTrFn = this.options.translate 584 else 585 this.customTrFn = function(value) { 586 return String(value) 587 } 588 589 this.getLegend = this.options.getLegend 590 } 591 592 if (this.options.vertical) { 593 this.positionProperty = 'bottom' 594 this.dimensionProperty = 'height' 595 } 596 597 if (this.options.customTemplateScope) 598 this.scope.custom = this.options.customTemplateScope 599 }, 600 601 parseStepsArray: function() { 602 this.options.floor = 0 603 this.options.ceil = this.options.stepsArray.length - 1 604 this.options.step = 1 605 606 if (this.options.translate) { 607 this.customTrFn = this.options.translate 608 } else { 609 this.customTrFn = function(modelValue) { 610 if (this.options.bindIndexForStepsArray) 611 return this.getStepValue(modelValue) 612 return modelValue 613 } 614 } 615 616 this.getLegend = function(index) { 617 var step = this.options.stepsArray[index] 618 if (angular.isObject(step)) return step.legend 619 return null 620 } 621 }, 622 623 /** 624 * Resets slider 625 * 626 * @returns {undefined} 627 */ 628 resetSlider: function() { 629 this.manageElementsStyle() 630 this.addAccessibility() 631 this.setMinAndMax() 632 this.updateCeilLab() 633 this.updateFloorLab() 634 this.unbindEvents() 635 this.manageEventsBindings() 636 this.setDisabledState() 637 this.calcViewDimensions() 638 this.refocusPointerIfNeeded() 639 }, 640 641 refocusPointerIfNeeded: function() { 642 if (this.currentFocusElement) { 643 this.onPointerFocus( 644 this.currentFocusElement.pointer, 645 this.currentFocusElement.ref 646 ) 647 this.focusElement(this.currentFocusElement.pointer) 648 } 649 }, 650 651 /** 652 * Set the slider children to variables for easy access 653 * 654 * Run only once during initialization 655 * 656 * @returns {undefined} 657 */ 658 initElemHandles: function() { 659 // Assign all slider elements to object properties for easy access 660 angular.forEach( 661 this.sliderElem.children(), 662 function(elem, index) { 663 var jElem = angular.element(elem) 664 665 switch (index) { 666 case 0: 667 this.leftOutSelBar = jElem 668 break 669 case 1: 670 this.rightOutSelBar = jElem 671 break 672 case 2: 673 this.fullBar = jElem 674 break 675 case 3: 676 this.selBar = jElem 677 break 678 case 4: 679 this.minH = jElem 680 break 681 case 5: 682 this.maxH = jElem 683 break 684 case 6: 685 this.flrLab = jElem 686 break 687 case 7: 688 this.ceilLab = jElem 689 break 690 case 8: 691 this.minLab = jElem 692 break 693 case 9: 694 this.maxLab = jElem 695 break 696 case 10: 697 this.cmbLab = jElem 698 break 699 case 11: 700 this.ticks = jElem 701 break 702 } 703 }, 704 this 705 ) 706 707 // Initialize position cache properties 708 this.selBar.rzsp = 0 709 this.minH.rzsp = 0 710 this.maxH.rzsp = 0 711 this.flrLab.rzsp = 0 712 this.ceilLab.rzsp = 0 713 this.minLab.rzsp = 0 714 this.maxLab.rzsp = 0 715 this.cmbLab.rzsp = 0 716 }, 717 718 /** 719 * Update each elements style based on options 720 */ 721 manageElementsStyle: function() { 722 if (!this.range) this.maxH.css('display', 'none') 723 else this.maxH.css('display', '') 724 725 this.alwaysHide( 726 this.flrLab, 727 this.options.showTicksValues || this.options.hideLimitLabels 728 ) 729 this.alwaysHide( 730 this.ceilLab, 731 this.options.showTicksValues || this.options.hideLimitLabels 732 ) 733 734 var hideLabelsForTicks = 735 this.options.showTicksValues && !this.intermediateTicks 736 this.alwaysHide( 737 this.minLab, 738 hideLabelsForTicks || this.options.hidePointerLabels 739 ) 740 this.alwaysHide( 741 this.maxLab, 742 hideLabelsForTicks || !this.range || this.options.hidePointerLabels 743 ) 744 this.alwaysHide( 745 this.cmbLab, 746 hideLabelsForTicks || !this.range || this.options.hidePointerLabels 747 ) 748 this.alwaysHide( 749 this.selBar, 750 !this.range && !this.options.showSelectionBar 751 ) 752 this.alwaysHide( 753 this.leftOutSelBar, 754 !this.range || !this.options.showOuterSelectionBars 755 ) 756 this.alwaysHide( 757 this.rightOutSelBar, 758 !this.range || !this.options.showOuterSelectionBars 759 ) 760 761 if (this.range && this.options.showOuterSelectionBars) { 762 this.fullBar.addClass('rz-transparent') 763 } 764 765 if (this.options.vertical) this.sliderElem.addClass('rz-vertical') 766 767 if (this.options.draggableRange) this.selBar.addClass('rz-draggable') 768 else this.selBar.removeClass('rz-draggable') 769 770 if (this.intermediateTicks && this.options.showTicksValues) 771 this.ticks.addClass('rz-ticks-values-under') 772 }, 773 774 alwaysHide: function(el, hide) { 775 el.rzAlwaysHide = hide 776 if (hide) this.hideEl(el) 777 else this.showEl(el) 778 }, 779 780 /** 781 * Manage the events bindings based on readOnly and disabled options 782 * 783 * @returns {undefined} 784 */ 785 manageEventsBindings: function() { 786 if (this.options.disabled || this.options.readOnly) 787 this.unbindEvents() 788 else this.bindEvents() 789 }, 790 791 /** 792 * Set the disabled state based on rzSliderDisabled 793 * 794 * @returns {undefined} 795 */ 796 setDisabledState: function() { 797 if (this.options.disabled) { 798 this.sliderElem.attr('disabled', 'disabled') 799 } else { 800 this.sliderElem.attr('disabled', null) 801 } 802 }, 803 804 /** 805 * Reset label values 806 * 807 * @return {undefined} 808 */ 809 resetLabelsValue: function() { 810 this.minLab.rzsv = undefined 811 this.maxLab.rzsv = undefined 812 }, 813 814 /** 815 * Initialize slider handles positions and labels 816 * 817 * Run only once during initialization and every time view port changes size 818 * 819 * @returns {undefined} 820 */ 821 initHandles: function() { 822 this.updateLowHandle(this.valueToPosition(this.lowValue)) 823 824 /* 825 the order here is important since the selection bar should be 826 updated after the high handle but before the combined label 827 */ 828 if (this.range) 829 this.updateHighHandle(this.valueToPosition(this.highValue)) 830 this.updateSelectionBar() 831 if (this.range) this.updateCmbLabel() 832 833 this.updateTicksScale() 834 }, 835 836 /** 837 * Translate value to human readable format 838 * 839 * @param {number|string} value 840 * @param {jqLite} label 841 * @param {String} which 842 * @param {boolean} [useCustomTr] 843 * @returns {undefined} 844 */ 845 translateFn: function(value, label, which, useCustomTr) { 846 useCustomTr = useCustomTr === undefined ? true : useCustomTr 847 848 var valStr = '', 849 getDimension = false, 850 noLabelInjection = label.hasClass('no-label-injection') 851 852 if (useCustomTr) { 853 if (this.options.stepsArray && !this.options.bindIndexForStepsArray) 854 value = this.getStepValue(value) 855 valStr = String(this.customTrFn(value, this.options.id, which)) 856 } else { 857 valStr = String(value) 858 } 859 860 if ( 861 label.rzsv === undefined || 862 label.rzsv.length !== valStr.length || 863 (label.rzsv.length > 0 && label.rzsd === 0) 864 ) { 865 getDimension = true 866 label.rzsv = valStr 867 } 868 869 if (!noLabelInjection) { 870 label.html(valStr) 871 } 872 this.scope[which + 'Label'] = valStr 873 874 // Update width only when length of the label have changed 875 if (getDimension) { 876 this.getDimension(label) 877 } 878 }, 879 880 /** 881 * Set maximum and minimum values for the slider and ensure the model and high 882 * value match these limits 883 * @returns {undefined} 884 */ 885 setMinAndMax: function() { 886 this.step = +this.options.step 887 this.precision = +this.options.precision 888 889 this.minValue = this.options.floor 890 if (this.options.logScale && this.minValue === 0) 891 throw Error("Can't use floor=0 with logarithmic scale") 892 893 if (this.options.enforceStep) { 894 this.lowValue = this.roundStep(this.lowValue) 895 if (this.range) this.highValue = this.roundStep(this.highValue) 896 } 897 898 if (this.options.ceil != null) this.maxValue = this.options.ceil 899 else 900 this.maxValue = this.options.ceil = this.range 901 ? this.highValue 902 : this.lowValue 903 904 if (this.options.enforceRange) { 905 this.lowValue = this.sanitizeValue(this.lowValue) 906 if (this.range) this.highValue = this.sanitizeValue(this.highValue) 907 } 908 909 this.applyLowValue() 910 if (this.range) this.applyHighValue() 911 912 this.valueRange = this.maxValue - this.minValue 913 }, 914 915 /** 916 * Adds accessibility attributes 917 * 918 * Run only once during initialization 919 * 920 * @returns {undefined} 921 */ 922 addAccessibility: function() { 923 this.minH.attr('role', 'slider') 924 this.updateAriaAttributes() 925 if ( 926 this.options.keyboardSupport && 927 !(this.options.readOnly || this.options.disabled) 928 ) 929 this.minH.attr('tabindex', '0') 930 else this.minH.attr('tabindex', '') 931 if (this.options.vertical) 932 this.minH.attr('aria-orientation', 'vertical') 933 if (this.options.ariaLabel) 934 this.minH.attr('aria-label', this.options.ariaLabel) 935 else if (this.options.ariaLabelledBy) 936 this.minH.attr('aria-labelledby', this.options.ariaLabelledBy) 937 938 if (this.range) { 939 this.maxH.attr('role', 'slider') 940 if ( 941 this.options.keyboardSupport && 942 !(this.options.readOnly || this.options.disabled) 943 ) 944 this.maxH.attr('tabindex', '0') 945 else this.maxH.attr('tabindex', '') 946 if (this.options.vertical) 947 this.maxH.attr('aria-orientation', 'vertical') 948 if (this.options.ariaLabelHigh) 949 this.maxH.attr('aria-label', this.options.ariaLabelHigh) 950 else if (this.options.ariaLabelledByHigh) 951 this.maxH.attr('aria-labelledby', this.options.ariaLabelledByHigh) 952 } 953 }, 954 955 /** 956 * Updates aria attributes according to current values 957 */ 958 updateAriaAttributes: function() { 959 this.minH.attr({ 960 'aria-valuenow': this.scope.rzSliderModel, 961 'aria-valuetext': this.customTrFn( 962 this.scope.rzSliderModel, 963 this.options.id, 964 'model' 965 ), 966 'aria-valuemin': this.minValue, 967 'aria-valuemax': this.maxValue, 968 }) 969 if (this.range) { 970 this.maxH.attr({ 971 'aria-valuenow': this.scope.rzSliderHigh, 972 'aria-valuetext': this.customTrFn( 973 this.scope.rzSliderHigh, 974 this.options.id, 975 'high' 976 ), 977 'aria-valuemin': this.minValue, 978 'aria-valuemax': this.maxValue, 979 }) 980 } 981 }, 982 983 /** 984 * Calculate dimensions that are dependent on view port size 985 * 986 * Run once during initialization and every time view port changes size. 987 * 988 * @returns {undefined} 989 */ 990 calcViewDimensions: function() { 991 var handleWidth = this.getDimension(this.minH) 992 993 this.handleHalfDim = handleWidth / 2 994 this.barDimension = this.getDimension(this.fullBar) 995 996 this.maxPos = this.barDimension - handleWidth 997 998 this.getDimension(this.sliderElem) 999 this.sliderElem.rzsp = this.sliderElem[0].getBoundingClientRect()[ 1000 this.positionProperty 1001 ] 1002 1003 if (this.initHasRun) { 1004 this.updateFloorLab() 1005 this.updateCeilLab() 1006 this.initHandles() 1007 var self = this 1008 $timeout(function() { 1009 self.updateTicksScale() 1010 }) 1011 } 1012 }, 1013 1014 /** 1015 * Update the ticks position 1016 * 1017 * @returns {undefined} 1018 */ 1019 updateTicksScale: function() { 1020 if (!this.options.showTicks) return 1021 1022 var ticksArray = this.options.ticksArray || this.getTicksArray(), 1023 translate = this.options.vertical ? 'translateY' : 'translateX', 1024 self = this 1025 1026 if (this.options.rightToLeft) ticksArray.reverse() 1027 1028 this.scope.ticks = ticksArray.map(function(value) { 1029 var position = self.valueToPosition(value) 1030 1031 if (self.options.vertical) position = self.maxPos - position 1032 1033 var translation = translate + '(' + Math.round(position) + 'px)' 1034 var tick = { 1035 selected: self.isTickSelected(value), 1036 style: { 1037 '-webkit-transform': translation, 1038 '-moz-transform': translation, 1039 '-o-transform': translation, 1040 '-ms-transform': translation, 1041 transform: translation, 1042 }, 1043 } 1044 if (tick.selected && self.options.getSelectionBarColor) { 1045 tick.style['background-color'] = self.getSelectionBarColor() 1046 } 1047 if (!tick.selected && self.options.getTickColor) { 1048 tick.style['background-color'] = self.getTickColor(value) 1049 } 1050 if (self.options.ticksTooltip) { 1051 tick.tooltip = self.options.ticksTooltip(value) 1052 tick.tooltipPlacement = self.options.vertical ? 'right' : 'top' 1053 } 1054 if ( 1055 self.options.showTicksValues === true || 1056 value % self.options.showTicksValues === 0 1057 ) { 1058 tick.value = self.getDisplayValue(value, 'tick-value') 1059 if (self.options.ticksValuesTooltip) { 1060 tick.valueTooltip = self.options.ticksValuesTooltip(value) 1061 tick.valueTooltipPlacement = self.options.vertical 1062 ? 'right' 1063 : 'top' 1064 } 1065 } 1066 if (self.getLegend) { 1067 var legend = self.getLegend(value, self.options.id) 1068 if (legend) tick.legend = legend 1069 } 1070 return tick 1071 }) 1072 }, 1073 1074 getTicksArray: function() { 1075 var step = this.step, 1076 ticksArray = [] 1077 if (this.intermediateTicks) step = this.options.showTicks 1078 for ( 1079 var value = this.minValue; 1080 value <= this.maxValue; 1081 value += step 1082 ) { 1083 ticksArray.push(value) 1084 } 1085 return ticksArray 1086 }, 1087 1088 isTickSelected: function(value) { 1089 if (!this.range) { 1090 if (this.options.showSelectionBarFromValue !== null) { 1091 var center = this.options.showSelectionBarFromValue 1092 if ( 1093 this.lowValue > center && 1094 value >= center && 1095 value <= this.lowValue 1096 ) 1097 return true 1098 else if ( 1099 this.lowValue < center && 1100 value <= center && 1101 value >= this.lowValue 1102 ) 1103 return true 1104 } else if (this.options.showSelectionBarEnd) { 1105 if (value >= this.lowValue) return true 1106 } else if (this.options.showSelectionBar && value <= this.lowValue) 1107 return true 1108 } 1109 if (this.range && value >= this.lowValue && value <= this.highValue) 1110 return true 1111 return false 1112 }, 1113 1114 /** 1115 * Update position of the floor label 1116 * 1117 * @returns {undefined} 1118 */ 1119 updateFloorLab: function() { 1120 this.translateFn(this.minValue, this.flrLab, 'floor') 1121 this.getDimension(this.flrLab) 1122 var position = this.options.rightToLeft 1123 ? this.barDimension - this.flrLab.rzsd 1124 : 0 1125 this.setPosition(this.flrLab, position) 1126 }, 1127 1128 /** 1129 * Update position of the ceiling label 1130 * 1131 * @returns {undefined} 1132 */ 1133 updateCeilLab: function() { 1134 this.translateFn(this.maxValue, this.ceilLab, 'ceil') 1135 this.getDimension(this.ceilLab) 1136 var position = this.options.rightToLeft 1137 ? 0 1138 : this.barDimension - this.ceilLab.rzsd 1139 this.setPosition(this.ceilLab, position) 1140 }, 1141 1142 /** 1143 * Update slider handles and label positions 1144 * 1145 * @param {string} which 1146 * @param {number} newPos 1147 */ 1148 updateHandles: function(which, newPos) { 1149 if (which === 'lowValue') this.updateLowHandle(newPos) 1150 else this.updateHighHandle(newPos) 1151 1152 this.updateSelectionBar() 1153 this.updateTicksScale() 1154 if (this.range) this.updateCmbLabel() 1155 }, 1156 1157 /** 1158 * Helper function to work out the position for handle labels depending on RTL or not 1159 * 1160 * @param {string} labelName maxLab or minLab 1161 * @param newPos 1162 * 1163 * @returns {number} 1164 */ 1165 getHandleLabelPos: function(labelName, newPos) { 1166 var labelRzsd = this[labelName].rzsd, 1167 nearHandlePos = newPos - labelRzsd / 2 + this.handleHalfDim, 1168 endOfBarPos = this.barDimension - labelRzsd 1169 1170 if (!this.options.boundPointerLabels) return nearHandlePos 1171 1172 if ( 1173 (this.options.rightToLeft && labelName === 'minLab') || 1174 (!this.options.rightToLeft && labelName === 'maxLab') 1175 ) { 1176 return Math.min(nearHandlePos, endOfBarPos) 1177 } else { 1178 return Math.min(Math.max(nearHandlePos, 0), endOfBarPos) 1179 } 1180 }, 1181 1182 /** 1183 * Update low slider handle position and label 1184 * 1185 * @param {number} newPos 1186 * @returns {undefined} 1187 */ 1188 updateLowHandle: function(newPos) { 1189 this.setPosition(this.minH, newPos) 1190 this.translateFn(this.lowValue, this.minLab, 'model') 1191 this.setPosition( 1192 this.minLab, 1193 this.getHandleLabelPos('minLab', newPos) 1194 ) 1195 1196 if (this.options.getPointerColor) { 1197 var pointercolor = this.getPointerColor('min') 1198 this.scope.minPointerStyle = { 1199 backgroundColor: pointercolor, 1200 } 1201 } 1202 1203 if (this.options.autoHideLimitLabels) { 1204 this.shFloorCeil() 1205 } 1206 }, 1207 1208 /** 1209 * Update high slider handle position and label 1210 * 1211 * @param {number} newPos 1212 * @returns {undefined} 1213 */ 1214 updateHighHandle: function(newPos) { 1215 this.setPosition(this.maxH, newPos) 1216 this.translateFn(this.highValue, this.maxLab, 'high') 1217 this.setPosition( 1218 this.maxLab, 1219 this.getHandleLabelPos('maxLab', newPos) 1220 ) 1221 1222 if (this.options.getPointerColor) { 1223 var pointercolor = this.getPointerColor('max') 1224 this.scope.maxPointerStyle = { 1225 backgroundColor: pointercolor, 1226 } 1227 } 1228 if (this.options.autoHideLimitLabels) { 1229 this.shFloorCeil() 1230 } 1231 }, 1232 1233 /** 1234 * Show/hide floor/ceiling label 1235 * 1236 * @returns {undefined} 1237 */ 1238 shFloorCeil: function() { 1239 // Show based only on hideLimitLabels if pointer labels are hidden 1240 if (this.options.hidePointerLabels) { 1241 return 1242 } 1243 var flHidden = false, 1244 clHidden = false, 1245 isMinLabAtFloor = this.isLabelBelowFloorLab(this.minLab), 1246 isMinLabAtCeil = this.isLabelAboveCeilLab(this.minLab), 1247 isMaxLabAtCeil = this.isLabelAboveCeilLab(this.maxLab), 1248 isCmbLabAtFloor = this.isLabelBelowFloorLab(this.cmbLab), 1249 isCmbLabAtCeil = this.isLabelAboveCeilLab(this.cmbLab) 1250 1251 if (isMinLabAtFloor) { 1252 flHidden = true 1253 this.hideEl(this.flrLab) 1254 } else { 1255 flHidden = false 1256 this.showEl(this.flrLab) 1257 } 1258 1259 if (isMinLabAtCeil) { 1260 clHidden = true 1261 this.hideEl(this.ceilLab) 1262 } else { 1263 clHidden = false 1264 this.showEl(this.ceilLab) 1265 } 1266 1267 if (this.range) { 1268 var hideCeil = this.cmbLabelShown ? isCmbLabAtCeil : isMaxLabAtCeil 1269 var hideFloor = this.cmbLabelShown 1270 ? isCmbLabAtFloor 1271 : isMinLabAtFloor 1272 1273 if (hideCeil) { 1274 this.hideEl(this.ceilLab) 1275 } else if (!clHidden) { 1276 this.showEl(this.ceilLab) 1277 } 1278 1279 // Hide or show floor label 1280 if (hideFloor) { 1281 this.hideEl(this.flrLab) 1282 } else if (!flHidden) { 1283 this.showEl(this.flrLab) 1284 } 1285 } 1286 }, 1287 1288 isLabelBelowFloorLab: function(label) { 1289 var isRTL = this.options.rightToLeft, 1290 pos = label.rzsp, 1291 dim = label.rzsd, 1292 floorPos = this.flrLab.rzsp, 1293 floorDim = this.flrLab.rzsd 1294 return isRTL 1295 ? pos + dim >= floorPos - 2 1296 : pos <= floorPos + floorDim + 2 1297 }, 1298 1299 isLabelAboveCeilLab: function(label) { 1300 var isRTL = this.options.rightToLeft, 1301 pos = label.rzsp, 1302 dim = label.rzsd, 1303 ceilPos = this.ceilLab.rzsp, 1304 ceilDim = this.ceilLab.rzsd 1305 return isRTL ? pos <= ceilPos + ceilDim + 2 : pos + dim >= ceilPos - 2 1306 }, 1307 1308 /** 1309 * Update slider selection bar, combined label and range label 1310 * 1311 * @returns {undefined} 1312 */ 1313 updateSelectionBar: function() { 1314 var position = 0, 1315 dimension = 0, 1316 isSelectionBarFromRight = this.options.rightToLeft 1317 ? !this.options.showSelectionBarEnd 1318 : this.options.showSelectionBarEnd, 1319 positionForRange = this.options.rightToLeft 1320 ? this.maxH.rzsp + this.handleHalfDim 1321 : this.minH.rzsp + this.handleHalfDim 1322 1323 if (this.range) { 1324 dimension = Math.abs(this.maxH.rzsp - this.minH.rzsp) 1325 position = positionForRange 1326 } else { 1327 if (this.options.showSelectionBarFromValue !== null) { 1328 var center = this.options.showSelectionBarFromValue, 1329 centerPosition = this.valueToPosition(center), 1330 isModelGreaterThanCenter = this.options.rightToLeft 1331 ? this.lowValue <= center 1332 : this.lowValue > center 1333 if (isModelGreaterThanCenter) { 1334 dimension = this.minH.rzsp - centerPosition 1335 position = centerPosition + this.handleHalfDim 1336 } else { 1337 dimension = centerPosition - this.minH.rzsp 1338 position = this.minH.rzsp + this.handleHalfDim 1339 } 1340 } else if (isSelectionBarFromRight) { 1341 dimension = 1342 Math.abs(this.maxPos - this.minH.rzsp) + this.handleHalfDim 1343 position = this.minH.rzsp + this.handleHalfDim 1344 } else { 1345 dimension = this.minH.rzsp + this.handleHalfDim 1346 position = 0 1347 } 1348 } 1349 this.setDimension(this.selBar, dimension) 1350 this.setPosition(this.selBar, position) 1351 if (this.range && this.options.showOuterSelectionBars) { 1352 if (this.options.rightToLeft) { 1353 this.setDimension(this.rightOutSelBar, position) 1354 this.setPosition(this.rightOutSelBar, 0) 1355 this.setDimension( 1356 this.leftOutSelBar, 1357 this.getDimension(this.fullBar) - (position + dimension) 1358 ) 1359 this.setPosition(this.leftOutSelBar, position + dimension) 1360 } else { 1361 this.setDimension(this.leftOutSelBar, position) 1362 this.setPosition(this.leftOutSelBar, 0) 1363 this.setDimension( 1364 this.rightOutSelBar, 1365 this.getDimension(this.fullBar) - (position + dimension) 1366 ) 1367 this.setPosition(this.rightOutSelBar, position + dimension) 1368 } 1369 } 1370 if (this.options.getSelectionBarColor) { 1371 var color = this.getSelectionBarColor() 1372 this.scope.barStyle = { 1373 backgroundColor: color, 1374 } 1375 } else if (this.options.selectionBarGradient) { 1376 var offset = 1377 this.options.showSelectionBarFromValue !== null 1378 ? this.valueToPosition(this.options.showSelectionBarFromValue) 1379 : 0, 1380 reversed = (offset - position > 0) ^ isSelectionBarFromRight, 1381 direction = this.options.vertical 1382 ? reversed ? 'bottom' : 'top' 1383 : reversed ? 'left' : 'right' 1384 this.scope.barStyle = { 1385 backgroundImage: 1386 'linear-gradient(to ' + 1387 direction + 1388 ', ' + 1389 this.options.selectionBarGradient.from + 1390 ' 0%,' + 1391 this.options.selectionBarGradient.to + 1392 ' 100%)', 1393 } 1394 if (this.options.vertical) { 1395 this.scope.barStyle.backgroundPosition = 1396 'center ' + 1397 (offset + 1398 dimension + 1399 position + 1400 (reversed ? -this.handleHalfDim : 0)) + 1401 'px' 1402 this.scope.barStyle.backgroundSize = 1403 '100% ' + (this.barDimension - this.handleHalfDim) + 'px' 1404 } else { 1405 this.scope.barStyle.backgroundPosition = 1406 offset - 1407 position + 1408 (reversed ? this.handleHalfDim : 0) + 1409 'px center' 1410 this.scope.barStyle.backgroundSize = 1411 this.barDimension - this.handleHalfDim + 'px 100%' 1412 } 1413 } 1414 }, 1415 1416 /** 1417 * Wrapper around the getSelectionBarColor of the user to pass to 1418 * correct parameters 1419 */ 1420 getSelectionBarColor: function() { 1421 if (this.range) 1422 return this.options.getSelectionBarColor( 1423 this.scope.rzSliderModel, 1424 this.scope.rzSliderHigh 1425 ) 1426 return this.options.getSelectionBarColor(this.scope.rzSliderModel) 1427 }, 1428 1429 /** 1430 * Wrapper around the getPointerColor of the user to pass to 1431 * correct parameters 1432 */ 1433 getPointerColor: function(pointerType) { 1434 if (pointerType === 'max') { 1435 return this.options.getPointerColor( 1436 this.scope.rzSliderHigh, 1437 pointerType 1438 ) 1439 } 1440 return this.options.getPointerColor( 1441 this.scope.rzSliderModel, 1442 pointerType 1443 ) 1444 }, 1445 1446 /** 1447 * Wrapper around the getTickColor of the user to pass to 1448 * correct parameters 1449 */ 1450 getTickColor: function(value) { 1451 return this.options.getTickColor(value) 1452 }, 1453 1454 /** 1455 * Update combined label position and value 1456 * 1457 * @returns {undefined} 1458 */ 1459 updateCmbLabel: function() { 1460 var isLabelOverlap = null 1461 if (this.options.rightToLeft) { 1462 isLabelOverlap = 1463 this.minLab.rzsp - this.minLab.rzsd - 10 <= this.maxLab.rzsp 1464 } else { 1465 isLabelOverlap = 1466 this.minLab.rzsp + this.minLab.rzsd + 10 >= this.maxLab.rzsp 1467 } 1468 1469 if (isLabelOverlap) { 1470 var lowTr = this.getDisplayValue(this.lowValue, 'model'), 1471 highTr = this.getDisplayValue(this.highValue, 'high'), 1472 labelVal = '' 1473 if (this.options.mergeRangeLabelsIfSame && lowTr === highTr) { 1474 labelVal = lowTr 1475 } else { 1476 labelVal = this.options.rightToLeft 1477 ? highTr + this.options.labelOverlapSeparator + lowTr 1478 : lowTr + this.options.labelOverlapSeparator + highTr 1479 } 1480 1481 this.translateFn(labelVal, this.cmbLab, 'cmb', false) 1482 var pos = this.options.boundPointerLabels 1483 ? Math.min( 1484 Math.max( 1485 this.selBar.rzsp + 1486 this.selBar.rzsd / 2 - 1487 this.cmbLab.rzsd / 2, 1488 0 1489 ), 1490 this.barDimension - this.cmbLab.rzsd 1491 ) 1492 : this.selBar.rzsp + this.selBar.rzsd / 2 - this.cmbLab.rzsd / 2 1493 1494 this.setPosition(this.cmbLab, pos) 1495 this.cmbLabelShown = true 1496 this.hideEl(this.minLab) 1497 this.hideEl(this.maxLab) 1498 this.showEl(this.cmbLab) 1499 } else { 1500 this.cmbLabelShown = false 1501 this.updateHighHandle(this.valueToPosition(this.highValue)) 1502 this.updateLowHandle(this.valueToPosition(this.lowValue)) 1503 this.showEl(this.maxLab) 1504 this.showEl(this.minLab) 1505 this.hideEl(this.cmbLab) 1506 } 1507 if (this.options.autoHideLimitLabels) { 1508 this.shFloorCeil() 1509 } 1510 }, 1511 1512 /** 1513 * Return the translated value if a translate function is provided else the original value 1514 * @param value 1515 * @param which if it's min or max handle 1516 * @returns {*} 1517 */ 1518 getDisplayValue: function(value, which) { 1519 if (this.options.stepsArray && !this.options.bindIndexForStepsArray) { 1520 value = this.getStepValue(value) 1521 } 1522 return this.customTrFn(value, this.options.id, which) 1523 }, 1524 1525 /** 1526 * Round value to step and precision based on minValue 1527 * 1528 * @param {number} value 1529 * @param {number} customStep a custom step to override the defined step 1530 * @returns {number} 1531 */ 1532 roundStep: function(value, customStep) { 1533 var step = customStep ? customStep : this.step, 1534 steppedDifference = parseFloat( 1535 (value - this.minValue) / step 1536 ).toPrecision(12) 1537 steppedDifference = Math.round(+steppedDifference) * step 1538 var newValue = (this.minValue + steppedDifference).toFixed( 1539 this.precision 1540 ) 1541 return +newValue 1542 }, 1543 1544 /** 1545 * Hide element 1546 * 1547 * @param element 1548 * @returns {jqLite} The jqLite wrapped DOM element 1549 */ 1550 hideEl: function(element) { 1551 return element.css({ 1552 visibility: 'hidden', 1553 }) 1554 }, 1555 1556 /** 1557 * Show element 1558 * 1559 * @param element The jqLite wrapped DOM element 1560 * @returns {jqLite} The jqLite 1561 */ 1562 showEl: function(element) { 1563 if (!!element.rzAlwaysHide) { 1564 return element 1565 } 1566 1567 return element.css({ 1568 visibility: 'visible', 1569 }) 1570 }, 1571 1572 /** 1573 * Set element left/top position depending on whether slider is horizontal or vertical 1574 * 1575 * @param {jqLite} elem The jqLite wrapped DOM element 1576 * @param {number} pos 1577 * @returns {number} 1578 */ 1579 setPosition: function(elem, pos) { 1580 elem.rzsp = pos 1581 var css = {} 1582 css[this.positionProperty] = Math.round(pos) + 'px' 1583 elem.css(css) 1584 return pos 1585 }, 1586 1587 /** 1588 * Get element width/height depending on whether slider is horizontal or vertical 1589 * 1590 * @param {jqLite} elem The jqLite wrapped DOM element 1591 * @returns {number} 1592 */ 1593 getDimension: function(elem) { 1594 var val = elem[0].getBoundingClientRect() 1595 if (this.options.vertical) 1596 elem.rzsd = (val.bottom - val.top) * this.options.scale 1597 else elem.rzsd = (val.right - val.left) * this.options.scale 1598 return elem.rzsd 1599 }, 1600 1601 /** 1602 * Set element width/height depending on whether slider is horizontal or vertical 1603 * 1604 * @param {jqLite} elem The jqLite wrapped DOM element 1605 * @param {number} dim 1606 * @returns {number} 1607 */ 1608 setDimension: function(elem, dim) { 1609 elem.rzsd = dim 1610 var css = {} 1611 css[this.dimensionProperty] = Math.round(dim) + 'px' 1612 elem.css(css) 1613 return dim 1614 }, 1615 1616 /** 1617 * Returns a value that is within slider range 1618 * 1619 * @param {number} val 1620 * @returns {number} 1621 */ 1622 sanitizeValue: function(val) { 1623 return Math.min(Math.max(val, this.minValue), this.maxValue) 1624 }, 1625 1626 /** 1627 * Translate value to pixel position 1628 * 1629 * @param {number} val 1630 * @returns {number} 1631 */ 1632 valueToPosition: function(val) { 1633 var fn = this.linearValueToPosition 1634 if (this.options.customValueToPosition) 1635 fn = this.options.customValueToPosition 1636 else if (this.options.logScale) fn = this.logValueToPosition 1637 1638 val = this.sanitizeValue(val) 1639 var percent = fn(val, this.minValue, this.maxValue) || 0 1640 if (this.options.rightToLeft) percent = 1 - percent 1641 return percent * this.maxPos 1642 }, 1643 1644 linearValueToPosition: function(val, minVal, maxVal) { 1645 var range = maxVal - minVal 1646 return (val - minVal) / range 1647 }, 1648 1649 logValueToPosition: function(val, minVal, maxVal) { 1650 val = Math.log(val) 1651 minVal = Math.log(minVal) 1652 maxVal = Math.log(maxVal) 1653 var range = maxVal - minVal 1654 return (val - minVal) / range 1655 }, 1656 1657 /** 1658 * Translate position to model value 1659 * 1660 * @param {number} position 1661 * @returns {number} 1662 */ 1663 positionToValue: function(position) { 1664 var percent = position / this.maxPos 1665 if (this.options.rightToLeft) percent = 1 - percent 1666 var fn = this.linearPositionToValue 1667 if (this.options.customPositionToValue) 1668 fn = this.options.customPositionToValue 1669 else if (this.options.logScale) fn = this.logPositionToValue 1670 return fn(percent, this.minValue, this.maxValue) || 0 1671 }, 1672 1673 linearPositionToValue: function(percent, minVal, maxVal) { 1674 return percent * (maxVal - minVal) + minVal 1675 }, 1676 1677 logPositionToValue: function(percent, minVal, maxVal) { 1678 minVal = Math.log(minVal) 1679 maxVal = Math.log(maxVal) 1680 var value = percent * (maxVal - minVal) + minVal 1681 return Math.exp(value) 1682 }, 1683 1684 getEventAttr: function(event, attr) { 1685 return event.originalEvent === undefined 1686 ? event[attr] 1687 : event.originalEvent[attr] 1688 }, 1689 1690 // Events 1691 /** 1692 * Get the X-coordinate or Y-coordinate of an event 1693 * 1694 * @param {Object} event The event 1695 * @param targetTouchId The identifier of the touch with the X/Y coordinates 1696 * @returns {number} 1697 */ 1698 getEventXY: function(event, targetTouchId) { 1699 /* http://stackoverflow.com/a/12336075/282882 */ 1700 //noinspection JSLint 1701 var clientXY = this.options.vertical ? 'clientY' : 'clientX' 1702 if (event[clientXY] !== undefined) { 1703 return event[clientXY] 1704 } 1705 1706 var touches = this.getEventAttr(event, 'touches') 1707 1708 if (targetTouchId !== undefined) { 1709 for (var i = 0; i < touches.length; i++) { 1710 if (touches[i].identifier === targetTouchId) { 1711 return touches[i][clientXY] 1712 } 1713 } 1714 } 1715 1716 // If no target touch or the target touch was not found in the event 1717 // returns the coordinates of the first touch 1718 return touches[0][clientXY] 1719 }, 1720 1721 /** 1722 * Compute the event position depending on whether the slider is horizontal or vertical 1723 * @param event 1724 * @param targetTouchId If targetTouchId is provided it will be considered the position of that 1725 * @returns {number} 1726 */ 1727 getEventPosition: function(event, targetTouchId) { 1728 var sliderPos = this.sliderElem.rzsp, 1729 eventPos = 0 1730 if (this.options.vertical) 1731 eventPos = -this.getEventXY(event, targetTouchId) + sliderPos 1732 else eventPos = this.getEventXY(event, targetTouchId) - sliderPos 1733 return eventPos * this.options.scale - this.handleHalfDim // #346 handleHalfDim is already scaled 1734 }, 1735 1736 /** 1737 * Get event names for move and event end 1738 * 1739 * @param {Event} event The event 1740 * 1741 * @return {{moveEvent: string, endEvent: string}} 1742 */ 1743 getEventNames: function(event) { 1744 var eventNames = { 1745 moveEvent: '', 1746 endEvent: '', 1747 } 1748 1749 if (this.getEventAttr(event, 'touches')) { 1750 eventNames.moveEvent = 'touchmove' 1751 eventNames.endEvent = 'touchend' 1752 } else { 1753 eventNames.moveEvent = 'mousemove' 1754 eventNames.endEvent = 'mouseup' 1755 } 1756 1757 return eventNames 1758 }, 1759 1760 /** 1761 * Get the handle closest to an event. 1762 * 1763 * @param event {Event} The event 1764 * @returns {jqLite} The handle closest to the event. 1765 */ 1766 getNearestHandle: function(event) { 1767 if (!this.range) { 1768 return this.minH 1769 } 1770 var position = this.getEventPosition(event), 1771 distanceMin = Math.abs(position - this.minH.rzsp), 1772 distanceMax = Math.abs(position - this.maxH.rzsp) 1773 if (distanceMin < distanceMax) return this.minH 1774 else if (distanceMin > distanceMax) return this.maxH 1775 else if (!this.options.rightToLeft) 1776 //if event is at the same distance from min/max then if it's at left of minH, we return minH else maxH 1777 return position < this.minH.rzsp ? this.minH : this.maxH 1778 else 1779 //reverse in rtl 1780 return position > this.minH.rzsp ? this.minH : this.maxH 1781 }, 1782 1783 /** 1784 * Wrapper function to focus an angular element 1785 * 1786 * @param el {AngularElement} the element to focus 1787 */ 1788 focusElement: function(el) { 1789 var DOM_ELEMENT = 0 1790 el[DOM_ELEMENT].focus() 1791 }, 1792 1793 /** 1794 * Bind mouse and touch events to slider handles 1795 * 1796 * @returns {undefined} 1797 */ 1798 bindEvents: function() { 1799 var barTracking, barStart, barMove 1800 1801 if (this.options.draggableRange) { 1802 barTracking = 'rzSliderDrag' 1803 barStart = this.onDragStart 1804 barMove = this.onDragMove 1805 } else { 1806 barTracking = 'lowValue' 1807 barStart = this.onStart 1808 barMove = this.onMove 1809 } 1810 1811 if (!this.options.onlyBindHandles) { 1812 this.selBar.on( 1813 'mousedown', 1814 angular.bind(this, barStart, null, barTracking) 1815 ) 1816 this.selBar.on( 1817 'mousedown', 1818 angular.bind(this, barMove, this.selBar) 1819 ) 1820 } 1821 1822 if (this.options.draggableRangeOnly) { 1823 this.minH.on( 1824 'mousedown', 1825 angular.bind(this, barStart, null, barTracking) 1826 ) 1827 this.maxH.on( 1828 'mousedown', 1829 angular.bind(this, barStart, null, barTracking) 1830 ) 1831 } else { 1832 this.minH.on( 1833 'mousedown', 1834 angular.bind(this, this.onStart, this.minH, 'lowValue') 1835 ) 1836 if (this.range) { 1837 this.maxH.on( 1838 'mousedown', 1839 angular.bind(this, this.onStart, this.maxH, 'highValue') 1840 ) 1841 } 1842 if (!this.options.onlyBindHandles) { 1843 this.fullBar.on( 1844 'mousedown', 1845 angular.bind(this, this.onStart, null, null) 1846 ) 1847 this.fullBar.on( 1848 'mousedown', 1849 angular.bind(this, this.onMove, this.fullBar) 1850 ) 1851 this.ticks.on( 1852 'mousedown', 1853 angular.bind(this, this.onStart, null, null) 1854 ) 1855 this.ticks.on( 1856 'mousedown', 1857 angular.bind(this, this.onTickClick, this.ticks) 1858 ) 1859 } 1860 } 1861 1862 if (!this.options.onlyBindHandles) { 1863 this.selBar.on( 1864 'touchstart', 1865 angular.bind(this, barStart, null, barTracking) 1866 ) 1867 this.selBar.on( 1868 'touchstart', 1869 angular.bind(this, barMove, this.selBar) 1870 ) 1871 } 1872 if (this.options.draggableRangeOnly) { 1873 this.minH.on( 1874 'touchstart', 1875 angular.bind(this, barStart, null, barTracking) 1876 ) 1877 this.maxH.on( 1878 'touchstart', 1879 angular.bind(this, barStart, null, barTracking) 1880 ) 1881 } else { 1882 this.minH.on( 1883 'touchstart', 1884 angular.bind(this, this.onStart, this.minH, 'lowValue') 1885 ) 1886 if (this.range) { 1887 this.maxH.on( 1888 'touchstart', 1889 angular.bind(this, this.onStart, this.maxH, 'highValue') 1890 ) 1891 } 1892 if (!this.options.onlyBindHandles) { 1893 this.fullBar.on( 1894 'touchstart', 1895 angular.bind(this, this.onStart, null, null) 1896 ) 1897 this.fullBar.on( 1898 'touchstart', 1899 angular.bind(this, this.onMove, this.fullBar) 1900 ) 1901 this.ticks.on( 1902 'touchstart', 1903 angular.bind(this, this.onStart, null, null) 1904 ) 1905 this.ticks.on( 1906 'touchstart', 1907 angular.bind(this, this.onTickClick, this.ticks) 1908 ) 1909 } 1910 } 1911 1912 if (this.options.keyboardSupport) { 1913 this.minH.on( 1914 'focus', 1915 angular.bind(this, this.onPointerFocus, this.minH, 'lowValue') 1916 ) 1917 if (this.range) { 1918 this.maxH.on( 1919 'focus', 1920 angular.bind(this, this.onPointerFocus, this.maxH, 'highValue') 1921 ) 1922 } 1923 } 1924 }, 1925 1926 /** 1927 * Unbind mouse and touch events to slider handles 1928 * 1929 * @returns {undefined} 1930 */ 1931 unbindEvents: function() { 1932 this.minH.off() 1933 this.maxH.off() 1934 this.fullBar.off() 1935 this.selBar.off() 1936 this.ticks.off() 1937 }, 1938 1939 /** 1940 * onStart event handler 1941 * 1942 * @param {?Object} pointer The jqLite wrapped DOM element; if null, the closest handle is used 1943 * @param {?string} ref The name of the handle being changed; if null, the closest handle's value is modified 1944 * @param {Event} event The event 1945 * @returns {undefined} 1946 */ 1947 onStart: function(pointer, ref, event) { 1948 var ehMove, 1949 ehEnd, 1950 eventNames = this.getEventNames(event) 1951 1952 event.preventDefault() 1953 1954 // We have to do this in case the HTML where the sliders are on 1955 // have been animated into view. 1956 this.calcViewDimensions() 1957 1958 if (pointer) { 1959 this.tracking = ref 1960 } else { 1961 pointer = this.getNearestHandle(event) 1962 this.tracking = pointer === this.minH ? 'lowValue' : 'highValue' 1963 } 1964 1965 pointer.addClass('rz-active') 1966 1967 if (this.options.keyboardSupport) this.focusElement(pointer) 1968 1969 ehMove = angular.bind( 1970 this, 1971 this.dragging.active ? this.onDragMove : this.onMove, 1972 pointer 1973 ) 1974 ehEnd = angular.bind(this, this.onEnd, ehMove) 1975 1976 $document.on(eventNames.moveEvent, ehMove) 1977 $document.on(eventNames.endEvent, ehEnd) 1978 this.endHandlerToBeRemovedOnEnd = ehEnd 1979 1980 this.callOnStart() 1981 1982 var changedTouches = this.getEventAttr(event, 'changedTouches') 1983 if (changedTouches) { 1984 // Store the touch identifier 1985 if (!this.touchId) { 1986 this.isDragging = true 1987 this.touchId = changedTouches[0].identifier 1988 } 1989 } 1990 }, 1991 1992 /** 1993 * onMove event handler 1994 * 1995 * @param {jqLite} pointer 1996 * @param {Event} event The event 1997 * @param {boolean} fromTick if the event occured on a tick or not 1998 * @returns {undefined} 1999 */ 2000 onMove: function(pointer, event, fromTick) { 2001 var changedTouches = this.getEventAttr(event, 'changedTouches') 2002 var touchForThisSlider 2003 if (changedTouches) { 2004 for (var i = 0; i < changedTouches.length; i++) { 2005 if (changedTouches[i].identifier === this.touchId) { 2006 touchForThisSlider = changedTouches[i] 2007 break 2008 } 2009 } 2010 } 2011 2012 if (changedTouches && !touchForThisSlider) { 2013 return 2014 } 2015 2016 var newPos = this.getEventPosition( 2017 event, 2018 touchForThisSlider ? touchForThisSlider.identifier : undefined 2019 ), 2020 newValue, 2021 ceilValue = this.options.rightToLeft 2022 ? this.minValue 2023 : this.maxValue, 2024 flrValue = this.options.rightToLeft ? this.maxValue : this.minValue 2025 2026 if (newPos <= 0) { 2027 newValue = flrValue 2028 } else if (newPos >= this.maxPos) { 2029 newValue = ceilValue 2030 } else { 2031 newValue = this.positionToValue(newPos) 2032 if (fromTick && angular.isNumber(this.options.showTicks)) 2033 newValue = this.roundStep(newValue, this.options.showTicks) 2034 else newValue = this.roundStep(newValue) 2035 } 2036 this.positionTrackingHandle(newValue) 2037 }, 2038 2039 /** 2040 * onEnd event handler 2041 * 2042 * @param {Event} event The event 2043 * @param {Function} ehMove The bound move event handler 2044 * @returns {undefined} 2045 */ 2046 onEnd: function(ehMove, event) { 2047 var changedTouches = this.getEventAttr(event, 'changedTouches') 2048 if (changedTouches && changedTouches[0].identifier !== this.touchId) { 2049 return 2050 } 2051 this.isDragging = false 2052 this.touchId = null 2053 2054 if (!this.options.keyboardSupport) { 2055 this.minH.removeClass('rz-active') 2056 this.maxH.removeClass('rz-active') 2057 this.tracking = '' 2058 } 2059 this.dragging.active = false 2060 2061 var eventName = this.getEventNames(event) 2062 $document.off(eventName.moveEvent, ehMove) 2063 $document.off(eventName.endEvent, this.endHandlerToBeRemovedOnEnd) 2064 this.endHandlerToBeRemovedOnEnd = null 2065 this.callOnEnd() 2066 }, 2067 2068 onTickClick: function(pointer, event) { 2069 this.onMove(pointer, event, true) 2070 }, 2071 2072 onPointerFocus: function(pointer, ref) { 2073 this.tracking = ref 2074 pointer.one('blur', angular.bind(this, this.onPointerBlur, pointer)) 2075 pointer.on('keydown', angular.bind(this, this.onKeyboardEvent)) 2076 pointer.on('keyup', angular.bind(this, this.onKeyUp)) 2077 this.firstKeyDown = true 2078 pointer.addClass('rz-active') 2079 2080 this.currentFocusElement = { 2081 pointer: pointer, 2082 ref: ref, 2083 } 2084 }, 2085 2086 onKeyUp: function() { 2087 this.firstKeyDown = true 2088 this.callOnEnd() 2089 }, 2090 2091 onPointerBlur: function(pointer) { 2092 pointer.off('keydown') 2093 pointer.off('keyup') 2094 pointer.removeClass('rz-active') 2095 if (!this.isDragging) { 2096 this.tracking = '' 2097 this.currentFocusElement = null 2098 } 2099 }, 2100 2101 /** 2102 * Key actions helper function 2103 * 2104 * @param {number} currentValue value of the slider 2105 * 2106 * @returns {?Object} action value mappings 2107 */ 2108 getKeyActions: function(currentValue) { 2109 var increaseStep = currentValue + this.step, 2110 decreaseStep = currentValue - this.step, 2111 increasePage = currentValue + this.valueRange / 10, 2112 decreasePage = currentValue - this.valueRange / 10 2113 2114 if (this.options.reversedControls) { 2115 increaseStep = currentValue - this.step 2116 decreaseStep = currentValue + this.step 2117 increasePage = currentValue - this.valueRange / 10 2118 decreasePage = currentValue + this.valueRange / 10 2119 } 2120 2121 //Left to right default actions 2122 var actions = { 2123 UP: increaseStep, 2124 DOWN: decreaseStep, 2125 LEFT: decreaseStep, 2126 RIGHT: increaseStep, 2127 PAGEUP: increasePage, 2128 PAGEDOWN: decreasePage, 2129 HOME: this.options.reversedControls ? this.maxValue : this.minValue, 2130 END: this.options.reversedControls ? this.minValue : this.maxValue, 2131 } 2132 //right to left means swapping right and left arrows 2133 if (this.options.rightToLeft) { 2134 actions.LEFT = increaseStep 2135 actions.RIGHT = decreaseStep 2136 // right to left and vertical means we also swap up and down 2137 if (this.options.vertical) { 2138 actions.UP = decreaseStep 2139 actions.DOWN = increaseStep 2140 } 2141 } 2142 return actions 2143 }, 2144 2145 onKeyboardEvent: function(event) { 2146 var currentValue = this[this.tracking], 2147 keyCode = event.keyCode || event.which, 2148 keys = { 2149 38: 'UP', 2150 40: 'DOWN', 2151 37: 'LEFT', 2152 39: 'RIGHT', 2153 33: 'PAGEUP', 2154 34: 'PAGEDOWN', 2155 36: 'HOME', 2156 35: 'END', 2157 }, 2158 actions = this.getKeyActions(currentValue), 2159 key = keys[keyCode], 2160 action = actions[key] 2161 if (action == null || this.tracking === '') return 2162 event.preventDefault() 2163 2164 if (this.firstKeyDown) { 2165 this.firstKeyDown = false 2166 this.callOnStart() 2167 } 2168 2169 var self = this 2170 $timeout(function() { 2171 var newValue = self.roundStep(self.sanitizeValue(action)) 2172 if (!self.options.draggableRangeOnly) { 2173 self.positionTrackingHandle(newValue) 2174 } else { 2175 var difference = self.highValue - self.lowValue, 2176 newMinValue, 2177 newMaxValue 2178 if (self.tracking === 'lowValue') { 2179 newMinValue = newValue 2180 newMaxValue = newValue + difference 2181 if (newMaxValue > self.maxValue) { 2182 newMaxValue = self.maxValue 2183 newMinValue = newMaxValue - difference 2184 } 2185 } else { 2186 newMaxValue = newValue 2187 newMinValue = newValue - difference 2188 if (newMinValue < self.minValue) { 2189 newMinValue = self.minValue 2190 newMaxValue = newMinValue + difference 2191 } 2192 } 2193 self.positionTrackingBar(newMinValue, newMaxValue) 2194 } 2195 }) 2196 }, 2197 2198 /** 2199 * onDragStart event handler 2200 * 2201 * Handles dragging of the middle bar. 2202 * 2203 * @param {Object} pointer The jqLite wrapped DOM element 2204 * @param {string} ref One of the refLow, refHigh values 2205 * @param {Event} event The event 2206 * @returns {undefined} 2207 */ 2208 onDragStart: function(pointer, ref, event) { 2209 var position = this.getEventPosition(event) 2210 this.dragging = { 2211 active: true, 2212 value: this.positionToValue(position), 2213 difference: this.highValue - this.lowValue, 2214 lowLimit: this.options.rightToLeft 2215 ? this.minH.rzsp - position 2216 : position - this.minH.rzsp, 2217 highLimit: this.options.rightToLeft 2218 ? position - this.maxH.rzsp 2219 : this.maxH.rzsp - position, 2220 } 2221 2222 this.onStart(pointer, ref, event) 2223 }, 2224 2225 /** 2226 * getValue helper function 2227 * 2228 * gets max or min value depending on whether the newPos is outOfBounds above or below the bar and rightToLeft 2229 * 2230 * @param {string} type 'max' || 'min' The value we are calculating 2231 * @param {number} newPos The new position 2232 * @param {boolean} outOfBounds Is the new position above or below the max/min? 2233 * @param {boolean} isAbove Is the new position above the bar if out of bounds? 2234 * 2235 * @returns {number} 2236 */ 2237 getValue: function(type, newPos, outOfBounds, isAbove) { 2238 var isRTL = this.options.rightToLeft, 2239 value = null 2240 2241 if (type === 'min') { 2242 if (outOfBounds) { 2243 if (isAbove) { 2244 value = isRTL 2245 ? this.minValue 2246 : this.maxValue - this.dragging.difference 2247 } else { 2248 value = isRTL 2249 ? this.maxValue - this.dragging.difference 2250 : this.minValue 2251 } 2252 } else { 2253 value = isRTL 2254 ? this.positionToValue(newPos + this.dragging.lowLimit) 2255 : this.positionToValue(newPos - this.dragging.lowLimit) 2256 } 2257 } else { 2258 if (outOfBounds) { 2259 if (isAbove) { 2260 value = isRTL 2261 ? this.minValue + this.dragging.difference 2262 : this.maxValue 2263 } else { 2264 value = isRTL 2265 ? this.maxValue 2266 : this.minValue + this.dragging.difference 2267 } 2268 } else { 2269 if (isRTL) { 2270 value = 2271 this.positionToValue(newPos + this.dragging.lowLimit) + 2272 this.dragging.difference 2273 } else { 2274 value = 2275 this.positionToValue(newPos - this.dragging.lowLimit) + 2276 this.dragging.difference 2277 } 2278 } 2279 } 2280 return this.roundStep(value) 2281 }, 2282 2283 /** 2284 * onDragMove event handler 2285 * 2286 * Handles dragging of the middle bar. 2287 * 2288 * @param {jqLite} pointer 2289 * @param {Event} event The event 2290 * @returns {undefined} 2291 */ 2292 onDragMove: function(pointer, event) { 2293 var newPos = this.getEventPosition(event), 2294 newMinValue, 2295 newMaxValue, 2296 ceilLimit, 2297 flrLimit, 2298 isUnderFlrLimit, 2299 isOverCeilLimit, 2300 flrH, 2301 ceilH 2302 2303 if (this.options.rightToLeft) { 2304 ceilLimit = this.dragging.lowLimit 2305 flrLimit = this.dragging.highLimit 2306 flrH = this.maxH 2307 ceilH = this.minH 2308 } else { 2309 ceilLimit = this.dragging.highLimit 2310 flrLimit = this.dragging.lowLimit 2311 flrH = this.minH 2312 ceilH = this.maxH 2313 } 2314 isUnderFlrLimit = newPos <= flrLimit 2315 isOverCeilLimit = newPos >= this.maxPos - ceilLimit 2316 2317 if (isUnderFlrLimit) { 2318 if (flrH.rzsp === 0) return 2319 newMinValue = this.getValue('min', newPos, true, false) 2320 newMaxValue = this.getValue('max', newPos, true, false) 2321 } else if (isOverCeilLimit) { 2322 if (ceilH.rzsp === this.maxPos) return 2323 newMaxValue = this.getValue('max', newPos, true, true) 2324 newMinValue = this.getValue('min', newPos, true, true) 2325 } else { 2326 newMinValue = this.getValue('min', newPos, false) 2327 newMaxValue = this.getValue('max', newPos, false) 2328 } 2329 this.positionTrackingBar(newMinValue, newMaxValue) 2330 }, 2331 2332 /** 2333 * Set the new value and position for the entire bar 2334 * 2335 * @param {number} newMinValue the new minimum value 2336 * @param {number} newMaxValue the new maximum value 2337 */ 2338 positionTrackingBar: function(newMinValue, newMaxValue) { 2339 if ( 2340 this.options.minLimit != null && 2341 newMinValue < this.options.minLimit 2342 ) { 2343 newMinValue = this.options.minLimit 2344 newMaxValue = newMinValue + this.dragging.difference 2345 } 2346 if ( 2347 this.options.maxLimit != null && 2348 newMaxValue > this.options.maxLimit 2349 ) { 2350 newMaxValue = this.options.maxLimit 2351 newMinValue = newMaxValue - this.dragging.difference 2352 } 2353 2354 this.lowValue = newMinValue 2355 this.highValue = newMaxValue 2356 this.applyLowValue() 2357 if (this.range) this.applyHighValue() 2358 this.applyModel(true) 2359 this.updateHandles('lowValue', this.valueToPosition(newMinValue)) 2360 this.updateHandles('highValue', this.valueToPosition(newMaxValue)) 2361 }, 2362 2363 /** 2364 * Set the new value and position to the current tracking handle 2365 * 2366 * @param {number} newValue new model value 2367 */ 2368 positionTrackingHandle: function(newValue) { 2369 var valueChanged = false 2370 newValue = this.applyMinMaxLimit(newValue) 2371 if (this.range) { 2372 if (this.options.pushRange) { 2373 newValue = this.applyPushRange(newValue) 2374 valueChanged = true 2375 } else { 2376 if (this.options.noSwitching) { 2377 if (this.tracking === 'lowValue' && newValue > this.highValue) 2378 newValue = this.applyMinMaxRange(this.highValue) 2379 else if ( 2380 this.tracking === 'highValue' && 2381 newValue < this.lowValue 2382 ) 2383 newValue = this.applyMinMaxRange(this.lowValue) 2384 } 2385 newValue = this.applyMinMaxRange(newValue) 2386 /* This is to check if we need to switch the min and max handles */ 2387 if (this.tracking === 'lowValue' && newValue > this.highValue) { 2388 this.lowValue = this.highValue 2389 this.applyLowValue() 2390 this.applyModel() 2391 this.updateHandles(this.tracking, this.maxH.rzsp) 2392 this.updateAriaAttributes() 2393 this.tracking = 'highValue' 2394 this.minH.removeClass('rz-active') 2395 this.maxH.addClass('rz-active') 2396 if (this.options.keyboardSupport) this.focusElement(this.maxH) 2397 valueChanged = true 2398 } else if ( 2399 this.tracking === 'highValue' && 2400 newValue < this.lowValue 2401 ) { 2402 this.highValue = this.lowValue 2403 this.applyHighValue() 2404 this.applyModel() 2405 this.updateHandles(this.tracking, this.minH.rzsp) 2406 this.updateAriaAttributes() 2407 this.tracking = 'lowValue' 2408 this.maxH.removeClass('rz-active') 2409 this.minH.addClass('rz-active') 2410 if (this.options.keyboardSupport) this.focusElement(this.minH) 2411 valueChanged = true 2412 } 2413 } 2414 } 2415 2416 if (this[this.tracking] !== newValue) { 2417 this[this.tracking] = newValue 2418 if (this.tracking === 'lowValue') this.applyLowValue() 2419 else this.applyHighValue() 2420 this.applyModel() 2421 this.updateHandles(this.tracking, this.valueToPosition(newValue)) 2422 this.updateAriaAttributes() 2423 valueChanged = true 2424 } 2425 2426 if (valueChanged) this.applyModel(true) 2427 }, 2428 2429 applyMinMaxLimit: function(newValue) { 2430 if (this.options.minLimit != null && newValue < this.options.minLimit) 2431 return this.options.minLimit 2432 if (this.options.maxLimit != null && newValue > this.options.maxLimit) 2433 return this.options.maxLimit 2434 return newValue 2435 }, 2436 2437 applyMinMaxRange: function(newValue) { 2438 var oppositeValue = 2439 this.tracking === 'lowValue' ? this.highValue : this.lowValue, 2440 difference = Math.abs(newValue - oppositeValue) 2441 if (this.options.minRange != null) { 2442 if (difference < this.options.minRange) { 2443 if (this.tracking === 'lowValue') 2444 return this.highValue - this.options.minRange 2445 else return this.lowValue + this.options.minRange 2446 } 2447 } 2448 if (this.options.maxRange != null) { 2449 if (difference > this.options.maxRange) { 2450 if (this.tracking === 'lowValue') 2451 return this.highValue - this.options.maxRange 2452 else return this.lowValue + this.options.maxRange 2453 } 2454 } 2455 return newValue 2456 }, 2457 2458 applyPushRange: function(newValue) { 2459 var difference = 2460 this.tracking === 'lowValue' 2461 ? this.highValue - newValue 2462 : newValue - this.lowValue, 2463 minRange = 2464 this.options.minRange !== null 2465 ? this.options.minRange 2466 : this.options.step, 2467 maxRange = this.options.maxRange 2468 // if smaller than minRange 2469 if (difference < minRange) { 2470 if (this.tracking === 'lowValue') { 2471 this.highValue = Math.min(newValue + minRange, this.maxValue) 2472 newValue = this.highValue - minRange 2473 this.applyHighValue() 2474 this.updateHandles( 2475 'highValue', 2476 this.valueToPosition(this.highValue) 2477 ) 2478 } else { 2479 this.lowValue = Math.max(newValue - minRange, this.minValue) 2480 newValue = this.lowValue + minRange 2481 this.applyLowValue() 2482 this.updateHandles( 2483 'lowValue', 2484 this.valueToPosition(this.lowValue) 2485 ) 2486 } 2487 this.updateAriaAttributes() 2488 } else if (maxRange !== null && difference > maxRange) { 2489 // if greater than maxRange 2490 if (this.tracking === 'lowValue') { 2491 this.highValue = newValue + maxRange 2492 this.applyHighValue() 2493 this.updateHandles( 2494 'highValue', 2495 this.valueToPosition(this.highValue) 2496 ) 2497 } else { 2498 this.lowValue = newValue - maxRange 2499 this.applyLowValue() 2500 this.updateHandles( 2501 'lowValue', 2502 this.valueToPosition(this.lowValue) 2503 ) 2504 } 2505 this.updateAriaAttributes() 2506 } 2507 return newValue 2508 }, 2509 2510 /** 2511 * Apply the model values using scope.$apply. 2512 * We wrap it with the internalChange flag to avoid the watchers to be called 2513 */ 2514 applyModel: function(callOnChange) { 2515 this.internalChange = true 2516 this.scope.$apply() 2517 callOnChange && this.callOnChange() 2518 this.internalChange = false 2519 }, 2520 2521 /** 2522 * Call the onStart callback if defined 2523 * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope. 2524 * 2525 * @returns {undefined} 2526 */ 2527 callOnStart: function() { 2528 if (this.options.onStart) { 2529 var self = this, 2530 pointerType = this.tracking === 'lowValue' ? 'min' : 'max' 2531 this.scope.$evalAsync(function() { 2532 self.options.onStart( 2533 self.options.id, 2534 self.scope.rzSliderModel, 2535 self.scope.rzSliderHigh, 2536 pointerType 2537 ) 2538 }) 2539 } 2540 }, 2541 2542 /** 2543 * Call the onChange callback if defined 2544 * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope. 2545 * 2546 * @returns {undefined} 2547 */ 2548 callOnChange: function() { 2549 if (this.options.onChange) { 2550 var self = this, 2551 pointerType = this.tracking === 'lowValue' ? 'min' : 'max' 2552 this.scope.$evalAsync(function() { 2553 self.options.onChange( 2554 self.options.id, 2555 self.scope.rzSliderModel, 2556 self.scope.rzSliderHigh, 2557 pointerType 2558 ) 2559 }) 2560 } 2561 }, 2562 2563 /** 2564 * Call the onEnd callback if defined 2565 * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope. 2566 * 2567 * @returns {undefined} 2568 */ 2569 callOnEnd: function() { 2570 if (this.options.onEnd) { 2571 var self = this, 2572 pointerType = this.tracking === 'lowValue' ? 'min' : 'max' 2573 this.scope.$evalAsync(function() { 2574 self.options.onEnd( 2575 self.options.id, 2576 self.scope.rzSliderModel, 2577 self.scope.rzSliderHigh, 2578 pointerType 2579 ) 2580 }) 2581 } 2582 this.scope.$emit('slideEnded') 2583 }, 2584 } 2585 2586 return Slider 2587 }]) 2588 .directive('rzslider', ['RzSlider', function(RzSlider) { 2589 'use strict' 2590 2591 return { 2592 restrict: 'AE', 2593 replace: true, 2594 scope: { 2595 rzSliderModel: '=?', 2596 rzSliderHigh: '=?', 2597 rzSliderOptions: '&?', 2598 rzSliderTplUrl: '@', 2599 }, 2600 2601 /** 2602 * Return template URL 2603 * 2604 * @param {jqLite} elem 2605 * @param {Object} attrs 2606 * @return {string} 2607 */ 2608 templateUrl: function(elem, attrs) { 2609 //noinspection JSUnresolvedVariable 2610 return attrs.rzSliderTplUrl || 'rzSliderTpl.html' 2611 }, 2612 2613 link: function(scope, elem) { 2614 scope.slider = new RzSlider(scope, elem) //attach on scope so we can test it 2615 }, 2616 } 2617 }]) 2618 2619 // IDE assist 2620 2621 /** 2622 * @name ngScope 2623 * 2624 * @property {number} rzSliderModel 2625 * @property {number} rzSliderHigh 2626 * @property {Object} rzSliderOptions 2627 */ 2628 2629 /** 2630 * @name jqLite 2631 * 2632 * @property {number|undefined} rzsp rzslider label position position 2633 * @property {number|undefined} rzsd rzslider element dimension 2634 * @property {string|undefined} rzsv rzslider label value/text 2635 * @property {Function} css 2636 * @property {Function} text 2637 */ 2638 2639 /** 2640 * @name Event 2641 * @property {Array} touches 2642 * @property {Event} originalEvent 2643 */ 2644 2645 /** 2646 * @name ThrottleOptions 2647 * 2648 * @property {boolean} leading 2649 * @property {boolean} trailing 2650 */ 2651 2652 module.run(['$templateCache', function($templateCache) { 2653 'use strict'; 2654 2655 $templateCache.put('rzSliderTpl.html', 2656 "<div class=rzslider><span class=\"rz-bar-wrapper rz-left-out-selection\"><span class=rz-bar></span></span> <span class=\"rz-bar-wrapper rz-right-out-selection\"><span class=rz-bar></span></span> <span class=rz-bar-wrapper><span class=rz-bar></span></span> <span class=rz-bar-wrapper><span class=\"rz-bar rz-selection\" ng-style=barStyle></span></span> <span class=\"rz-pointer rz-pointer-min\" ng-style=minPointerStyle></span> <span class=\"rz-pointer rz-pointer-max\" ng-style=maxPointerStyle></span> <span class=\"rz-bubble rz-limit rz-floor\"></span> <span class=\"rz-bubble rz-limit rz-ceil\"></span> <span class=\"rz-bubble rz-model-value\"></span> <span class=\"rz-bubble rz-model-high\"></span> <span class=rz-bubble></span><ul ng-show=showTicks class=rz-ticks><li ng-repeat=\"t in ticks track by $index\" class=rz-tick ng-class=\"{'rz-selected': t.selected}\" ng-style=t.style ng-attr-uib-tooltip=\"{{ t.tooltip }}\" ng-attr-tooltip-placement={{t.tooltipPlacement}} ng-attr-tooltip-append-to-body=\"{{ t.tooltip ? true : undefined}}\"><span ng-if=\"t.value != null\" class=rz-tick-value ng-attr-uib-tooltip=\"{{ t.valueTooltip }}\" ng-attr-tooltip-placement={{t.valueTooltipPlacement}}>{{ t.value }}</span> <span ng-if=\"t.legend != null\" class=rz-tick-legend>{{ t.legend }}</span></li></ul></div>" 2657 ); 2658 2659 }]); 2660 2661 return module.name 2662}) 2663;
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.