PageSourceSearch

https://www.golfdater.com/themes/js/vendor/angular-slider.js?v=5.1.1

js golfdater.com collected 2026-10-07 13:32:13 UTC 79,592 bytes, 2,217 lines download raw bytes

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

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.