PageSourceSearch

https://www.geo.be/bower_components/angularjs-slider/dist/rzslider.js

js geo.be collected 2026-10-02 08:40:46 UTC 38,686 bytes, 1,481 lines download raw bytes

1/**
2 * Angular JS slider directive
3 *
4 * (c) Rafal Zajac <[email protected]>
5 * http://github.com/rzajac/angularjs-slider
6 *
7 * Version: v1.0.0
8 *
9 * Licensed under the MIT license
10 */
11
12/*jslint unparam: true */
13/*global angular: false, console: false, define, module */
14(function (root, factory) {
15  'use strict';
16  if (typeof define === 'function' && define.amd) {
17    // AMD. Register as an anonymous module.
18    define(['angular'], factory);
19  } else if (typeof module === 'object' && module.exports) {
20    // Node. Does not work with strict CommonJS, but
21    // only CommonJS-like environments that support module.exports,
22    // like Node.
23    // to support bundler like browserify
24    module.exports = factory(require('angular'));
25  } else {
26    // Browser globals (root is window)
27    factory(root.angular);
28  }
29
30}(this, function (angular) {
31  'use strict';
32var module = angular.module('rzModule', [])
33
34.value('throttle',
35  /**
36   * throttle
37   *
38   * Taken from underscore project
39   *
40   * @param {Function} func
41   * @param {number} wait
42   * @param {ThrottleOptions} options
43   * @returns {Function}
44   */
45function throttle(func, wait, options) {
46  'use strict';
47  var getTime = (Date.now || function() {
48    return new Date().getTime();
49  });
50  var context, args, result;
51  var timeout = null;
52  var previous = 0;
53  options = options || {};
54  var later = function() {
55    previous = options.leading === false ? 0 : getTime();
56    timeout = null;
57    result = func.apply(context, args);
58    context = args = null;
59  };
60  return function() {
61    var now = getTime();
62    if (!previous && options.leading === false) { previous = now; }
63    var remaining = wait - (now - previous);
64    context = this;
65    args = arguments;
66    if (remaining <= 0) {
67      clearTimeout(timeout);
68      timeout = null;
69      previous = now;
70      result = func.apply(context, args);
71      context = args = null;
72    } else if (!timeout && options.trailing !== false) {
73      timeout = setTimeout(later, remaining);
74    }
75    return result;
76  };
77})
78
79.factory('RzSlider', ['$timeout', '$document', '$window', 'throttle', function($timeout, $document, $window, throttle)
80{
81  'use strict';
82
83  /**
84   * Slider
85   *
86   * @param {ngScope} scope            The AngularJS scope
87   * @param {Element} sliderElem The slider directive element wrapped in jqLite
88   * @param {*} attributes       The slider directive attributes
89   * @constructor
90   */
91  var Slider = function(scope, sliderElem, attributes)
92  {
93    /**
94     * The slider's scope
95     *
96     * @type {ngScope}
97     */
98    this.scope = scope;
99
100    /**
101     * The slider attributes
102     *
103     * @type {Object}
104     */
105    this.attributes = attributes;
106
107    /**
108     * Slider element wrapped in jqLite
109     *
110     * @type {jqLite}
111     */
112    this.sliderElem = sliderElem;
113
114    /**
115     * Slider type
116     *
117     * @type {boolean} Set to true for range slider
118     */
119    this.range = attributes.rzSliderHigh !== undefined && attributes.rzSliderModel !== undefined;
120
121    /**
122     * Whether to allow draggable range
123     *
124     * @type {boolean} Set to true for draggable range slider
125     */
126    this.dragRange = this.range && attributes.rzSliderDraggableRange === 'true';
127
128    /**
129     * Values recorded when first dragging the bar
130     *
131     * @type {Object}
132     */
133    this.dragging = {
134      active: false,
135      value: 0,
136      difference: 0,
137      offset: 0,
138      lowDist: 0,
139      highDist: 0
140    };
141
142    /**
143     * Half of the width of the slider handles
144     *
145     * @type {number}
146     */
147    this.handleHalfWidth = 0;
148
149    /**
150     * Always show selection bar
151     *
152     * @type {boolean}
153     */
154    this.alwaysShowBar = !!attributes.rzSliderAlwaysShowBar;
155
156    /**
157     * Maximum left the slider handle can have
158     *
159     * @type {number}
160     */
161    this.maxLeft = 0;
162
163    /**
164     * Precision
165     *
166     * @type {number}
167     */
168    this.precision = 0;
169
170    /**
171     * Step
172     *
173     * @type {number}
174     */
175    this.step = 0;
176
177    /**
178     * The name of the handle we are currently tracking
179     *
180     * @type {string}
181     */
182    this.tracking = '';
183
184    /**
185     * Minimum value (floor) of the model
186     *
187     * @type {number}
188     */
189    this.minValue = 0;
190
191    /**
192     * Maximum value (ceiling) of the model
193     *
194     * @type {number}
195     */
196    this.maxValue = 0;
197
198    /**
199     * Hide limit labels
200     *
201     * @type {boolean}
202     */
203    this.hideLimitLabels = !!attributes.rzSliderHideLimitLabels;
204
205    /**
206     * Only present model values
207     *
208     * Do not allow to change values
209     *
210     * @type {boolean}
211     */
212    this.presentOnly = attributes.rzSliderPresentOnly === 'true';
213
214    /**
215     * Display ticks on each possible value.
216     *
217     * @type {boolean}
218     */
219    this.showTicks = attributes.rzSliderShowTicks || attributes.rzSliderShowTicksValue;
220
221    /**
222     * Display the value on each tick.
223     *
224     * @type {boolean}
225     */
226    this.showTicksValue = attributes.rzSliderShowTicksValue;
227
228    /**
229     * Disable the slider
230     *
231     * @type {boolean}
232     */
233    this.disabled = this.scope.rzSliderDisabled;
234
235    /**
236     * The delta between min and max value
237     *
238     * @type {number}
239     */
240    this.valueRange = 0;
241
242    /**
243     * Set to true if init method already executed
244     *
245     * @type {boolean}
246     */
247    this.initHasRun = false;
248
249    /**
250     * Custom translate function
251     *
252     * @type {function}
253     */
254    this.customTrFn = this.scope.rzSliderTranslate() || function(value) { return String(value); };
255
256    /**
257     * Array of de-registration functions to call on $destroy
258     *
259     * @type {Array.<Function>}
260     */
261    this.deRegFuncs = [];
262
263    // Slider DOM elements wrapped in jqLite
264    this.fullBar = null;  // The whole slider bar
265    this.selBar = null;   // Highlight between two handles
266    this.minH = null;     // Left slider handle
267    this.maxH = null;     // Right slider handle
268    this.flrLab = null;   // Floor label
269    this.ceilLab = null;  // Ceiling label
270    this.minLab =  null;  // Label above the low value
271    this.maxLab = null;   // Label above the high value
272    this.cmbLab = null;   // Combined label
273    this.ticks = null;  // The ticks
274
275    // Initialize slider
276    this.init();
277  };
278
279  // Add instance methods
280  Slider.prototype = {
281
282    /**
283     * Initialize slider
284     *
285     * @returns {undefined}
286     */
287    init: function()
288    {
289      var thrLow, thrHigh, unRegFn,
290        calcDimFn = angular.bind(this, this.calcViewDimensions),
291        self = this;
292
293      this.initElemHandles();
294      this.addAccessibility();
295      this.setDisabledState();
296      this.calcViewDimensions();
297      this.setMinAndMax();
298
299      this.precision = this.scope.rzSliderPrecision === undefined ? 0 : +this.scope.rzSliderPrecision;
300      this.step = this.scope.rzSliderStep === undefined ? 1 : +this.scope.rzSliderStep;
301
302      $timeout(function()
303      {
304        self.updateCeilLab();
305        self.updateFloorLab();
306        self.initHandles();
307        self.bindEvents();
308      });
309
310      // Recalculate slider view dimensions
311      unRegFn = this.scope.$on('reCalcViewDimensions', calcDimFn);
312      this.deRegFuncs.push(unRegFn);
313
314      // Recalculate stuff if view port dimensions have changed
315      angular.element($window).on('resize', calcDimFn);
316
317      this.initHasRun = true;
318
319      // Watch for changes to the model
320
321      thrLow = throttle(function()
322      {
323        self.setMinAndMax();
324        self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel));
325        self.updateSelectionBar();
326        self.updateTicksScale();
327
328        if(self.range)
329        {
330          self.updateCmbLabel();
331        }
332
333      }, 350, { leading: false });
334
335      thrHigh = throttle(function()
336      {
337        self.setMinAndMax();
338        self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh));
339        self.updateSelectionBar();
340        self.updateTicksScale();
341        self.updateCmbLabel();
342      }, 350, { leading: false });
343
344      this.scope.$on('rzSliderForceRender', function()
345      {
346        self.resetLabelsValue();
347        thrLow();
348        if(self.range) { thrHigh(); }
349        self.resetSlider();
350      });
351
352      // Watchers
353
354      unRegFn = this.scope.$watch('rzSliderModel', function(newValue, oldValue)
355      {
356        if(newValue === oldValue) { return; }
357        thrLow();
358      });
359      this.deRegFuncs.push(unRegFn);
360
361      unRegFn = this.scope.$watch('rzSliderHigh', function(newValue, oldValue)
362      {
363        if(newValue === oldValue) { return; }
364        thrHigh();
365      });
366      this.deRegFuncs.push(unRegFn);
367
368      this.scope.$watch('rzSliderFloor', function(newValue, oldValue)
369      {
370        if(newValue === oldValue) { return; }
371        self.resetSlider();
372      });
373      this.deRegFuncs.push(unRegFn);
374
375      unRegFn = this.scope.$watch('rzSliderCeil', function(newValue, oldValue)
376      {
377        if(newValue === oldValue) { return; }
378        self.resetSlider();
379      });
380      this.deRegFuncs.push(unRegFn);
381
382      unRegFn = this.scope.$watch('rzSliderShowTicks', function(newValue, oldValue)
383      {
384        if(newValue === oldValue) { return; }
385        self.resetSlider();
386      });
387      this.deRegFuncs.push(unRegFn);
388
389      unRegFn = this.scope.$watch('rzSliderShowTicksValue', function(newValue, oldValue)
390      {
391        if(newValue === oldValue) { return; }
392        self.resetSlider();
393      });
394      this.deRegFuncs.push(unRegFn);
395
396      unRegFn = this.scope.$watch('rzSliderDisabled', function(newValue, oldValue)
397      {
398        if(newValue === oldValue) { return; }
399        self.resetSlider();
400        if(self.disabled)
401          self.unbindEvents();
402        else
403          self.bindEvents();
404      });
405      this.deRegFuncs.push(unRegFn);
406
407      this.scope.$on('$destroy', function()
408      {
409        self.unbindEvents();
410        angular.element($window).off('resize', calcDimFn);
411        self.deRegFuncs.map(function(unbind) { unbind(); });
412      });
413    },
414
415    /**
416     * Resets slider
417     *
418     * @returns {undefined}
419     */
420    resetSlider: function()
421    {
422      this.setMinAndMax();
423      this.updateCeilLab();
424      this.updateFloorLab();
425      this.setDisabledState();
426      this.calcViewDimensions();
427    },
428
429    /**
430     * Set the disabled state based on rzSliderDisabled
431     *
432     * @returns {undefined}
433     */
434    setDisabledState: function()
435    {
436      this.disabled = this.scope.rzSliderDisabled;
437      if(this.disabled) {
438        this.sliderElem.attr('disabled', 'disabled');
439      }
440      else {
441        this.sliderElem.attr('disabled', null);
442      }
443
444    },
445
446    /**
447     * Reset label values
448     *
449     * @return {undefined}
450     */
451    resetLabelsValue: function()
452    {
453      this.minLab.rzsv = undefined;
454      this.maxLab.rzsv = undefined;
455    },
456
457    /**
458     * Initialize slider handles positions and labels
459     *
460     * Run only once during initialization and every time view port 
460changes size
461     *
462     * @returns {undefined}
463     */
464    initHandles: function()
465    {
466      this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel));
467
468      if(this.range)
469      {
470        this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh));
471        this.updateCmbLabel();
472      }
473
474      this.updateSelectionBar();
475      this.updateTicksScale();
476    },
477
478    /**
479     * Translate value to human readable format
480     *
481     * @param {number|string} value
482     * @param {jqLite} label
483     * @param {boolean} [useCustomTr]
484     * @returns {undefined}
485     */
486    translateFn: function(value, label, useCustomTr)
487    {
488      useCustomTr = useCustomTr === undefined ? true : useCustomTr;
489
490      var valStr = (useCustomTr ? this.customTrFn(value) : value).toString(),
491          getWidth = false;
492
493      if(label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0))
494      {
495        getWidth = true;
496        label.rzsv = valStr;
497      }
498
499      label.text(valStr);
500
501      // Update width only when length of the label have changed
502      if(getWidth) { this.getWidth(label); }
503    },
504
505    /**
506     * Set maximum and minimum values for the slider
507     *
508     * @returns {undefined}
509     */
510    setMinAndMax: function()
511    {
512      if(this.scope.rzSliderFloor)
513      {
514        this.minValue = +this.scope.rzSliderFloor;
515      }
516      else
517      {
518        this.minValue = this.scope.rzSliderFloor = 0;
519      }
520
521      if(this.scope.rzSliderCeil)
522      {
523        this.maxValue = +this.scope.rzSliderCeil;
524      }
525      else
526      {
527        this.maxValue = this.scope.rzSliderCeil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel;
528      }
529
530      if(this.scope.rzSliderStep)
531      {
532        this.step = +this.scope.rzSliderStep;
533      }
534
535      this.valueRange = this.maxValue - this.minValue;
536    },
537
538    /**
539     * Set the slider children to variables for easy access
540     *
541     * Run only once during initialization
542     *
543     * @returns {undefined}
544     */
545    initElemHandles: function()
546    {
547      // Assign all slider elements to object properties for easy access
548      angular.forEach(this.sliderElem.children(), function(elem, index)
549      {
550        var jElem = angular.element(elem);
551
552        switch(index)
553        {
554          case 0: this.fullBar = jElem; break;
555          case 1: this.selBar = jElem; break;
556          case 2: this.minH = jElem; break;
557          case 3: this.maxH = jElem; break;
558          case 4: this.flrLab = jElem; break;
559          case 5: this.ceilLab = jElem; break;
560          case 6: this.minLab = jElem; break;
561          case 7: this.maxLab = jElem; break;
562          case 8: this.cmbLab = jElem; break;
563          case 9: this.ticks = jElem; break;
564        }
565
566      }, this);
567
568      // Initialize offset cache properties
569      this.selBar.rzsl = 0;
570      this.minH.rzsl = 0;
571      this.maxH.rzsl = 0;
572      this.flrLab.rzsl = 0;
573      this.ceilLab.rzsl = 0;
574      this.minLab.rzsl = 0;
575      this.maxLab.rzsl = 0;
576      this.cmbLab.rzsl = 0;
577
578      // Hide limit labels
579      if(this.hideLimitLabels)
580      {
581        this.flrLab.rzAlwaysHide = true;
582        this.ceilLab.rzAlwaysHide = true;
583        this.hideEl(this.flrLab);
584        this.hideEl(this.ceilLab);
585      }
586
587      if(this.showTicksValue) {
588        this.flrLab.rzAlwaysHide = true;
589        this.ceilLab.rzAlwaysHide = true;
590        this.minLab.rzAlwaysHide = true;
591        this.maxLab.rzAlwaysHide = true;
592        this.hideEl(this.flrLab);
593        this.hideEl(this.ceilLab);
594        this.hideEl(this.minLab);
595        this.hideEl(this.maxLab);
596      }
597
598      // Remove stuff not needed in single slider
599      if(this.range === false)
600      {
601        this.cmbLab.remove();
602        this.maxLab.remove();
603
604        // Hide max handle
605        this.maxH.rzAlwaysHide = true;
606        this.maxH[0].style.zIndex = '-1000';
607        this.hideEl(this.maxH);
608      }
609
610      // Show selection bar for single slider or not
611      if(this.range === false && this.alwaysShowBar === false)
612      {
613        this.maxH.remove();
614        this.selBar.remove();
615      }
616
617      // If using draggable range, use appropriate cursor for this.selBar.
618      if (this.dragRange)
619      {
620        this.selBar.css('cursor', 'move');
621        this.selBar.addClass('rz-draggable');
622      }
623    },
624    
625    /**
626     * Adds accessibility atributes
627     *
628     * Run only once during initialization
629     *
630     * @returns {undefined}
631     */
632    addAccessibility: function ()
633    {
634      this.sliderElem.attr("role", "slider");
635    },
636
637    /**
638     * Calculate dimensions that are dependent on view port size
639     *
640     * Run once during initialization and every time view port changes size.
641     *
642     * @returns {undefined}
643     */
644    calcViewDimensions: function ()
645    {
646      var handleWidth = this.getWidth(this.minH);
647
648      this.handleHalfWidth = handleWidth / 2;
649      this.barWidth = this.getWidth(this.fullBar);
650
651      this.maxLeft = this.barWidth - handleWidth;
652
653      this.getWidth(this.sliderElem);
654      this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left;
655
656      if(this.initHasRun)
657      {
658        this.updateCeilLab();
659        this.initHandles();
660      }
661    },
662
663    /**
664     * Update the ticks position
665     *
666     * @returns {undefined}
667     */
668    updateTicksScale: function() {
669      if(!this.showTicks) return;
670      if(!this.step) return; //if step is 0, the following loop will be endless.
671
672      var positions = '',
673          ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1;
674      for (var i = 0; i < ticksCount; i++) {
675        var selectedClass = this.isTickSelected(i) ? 'selected': '';
676        positions += '<li class="tick '+ selectedClass +'">';
677        if(this.showTicksValue)
678          positions += '<span class="tick-value">'+ this.getDisplayValue(i) +'</span>';
679        positions += '</li>';
680      }
681      this.ticks.html(positions);
682    },
683
684    isTickSelected: function(value) {
685      var tickLeft = this.valueToOffset(value);
686      if(!this.range && this.alwaysShowBar && value <= this.scope.rzSliderModel)
687        return true;
688      if(this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh)
689        return true;
690      return false;
691    },
692
693    /**
694     * Update position of the ceiling label
695     *
696     * @returns {undefined}
697     */
698    updateCeilLab: function()
699    {
700      this.translateFn(this.scope.rzSliderCeil, this.ceilLab);
701      this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw);
702      this.getWidth(this.ceilLab);
703    },
704
705    /**
706     * Update position of the floor label
707     *
708     * @returns {undefined}
709     */
710    updateFloorLab: function()
711    {
712      this.translateFn(this.scope.rzSliderFloor, this.flrLab);
713      this.getWidth(this.flrLab);
714    },
715
716    /**
717     * Call the onStart callback if defined
718     *
719     * @returns {undefined}
720     */
721    callOnStart: function() {
722      if(this.scope.rzSliderOnStart) {
723        var self = this;
724        $timeout(function() {
725            self.scope.rzSliderOnStart();
726        });
727      }
728    },
729
730    /**
731     * Call the onChange callback if defined
732     *
733     * @returns {undefined}
734     */
735    callOnChange: function() {
736      if(this.scope.rzSliderOnChange) {
737        var self = this;
738        $timeout(function() {
739            self.scope.rzSliderOnChange();
740        });
741      }
742    },
743
744    /**
745     * Call the onEnd callback if defined
746     *
747     * @returns {undefined}
748     */
749    callOnEnd: function() {
750      if(this.scope.rzSliderOnEnd) {
751        var self = this;
752        $timeout(function() {
753            self.scope.rzSliderOnEnd();
754        });
755      }
756    },
757
758    /**
759     * Update slider handles and label positions
760     *
761     * @param {string} which
762     * @param {number} newOffset
763     */
764    updateHandles: function(which, newOffset)
765    {
766      if(which === 'rzSliderModel')
767      {
768        this.updateLowHandle(newOffset);
769        this.updateSelectionBar();
770        this.updateTicksScale();
771
772        if(this.range)
773        {
774          this.updateCmbLabel();
775        }
776        return;
777      }
778
779      if(which === 'rzSliderHigh')
780      {
781        this.updateHighHandle(newOffset);
782        this.updateSelectionBar();
783        this.updateTicksScale();
784
785        if(this.range)
786        {
787          this.updateCmbLabel();
788        }
789        return;
790      }
791
792      // Update both
793      this.updateLowHandle(newOffset);
794      this.updateHighHandle(newOffset);
795      this.updateSelectionBar();
796      this.updateTicksScale();
797      this.updateCmbLabel();
798    },
799
800    /**
801     * Update low slider handle position and label
802     *
803     * @param {number} newOffset
804     * @returns {undefined}
805     */
806    updateLowHandle: function(newOffset)
807    {
808      var delta = Math.abs(this.minH.rzsl - newOffset);
809
810      if(this.minLab.rzsv && delta < 1) { return; }
811
812      this.setLeft(this.minH, newOffset);
813      this.translateFn(this.scope.rzSliderModel, this.minLab);
814      this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth);
815
816      this.shFloorCeil();
817    },
818
819    /**
820     * Update high slider handle position and label
821     *
822     * @param {number} newOffset
823     * @returns {undefined}
824     */
825    updateHighHandle: function(newOffset)
826    {
827      this.setLeft(this.maxH, newOffset);
828      this.translateFn(this.scope.rzSliderHigh, this.maxLab);
829      this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth);
830
831      this.shFloorCeil();
832    },
833
834    /**
835     * Show / hide floor / ceiling label
836     *
837     * @returns {undefined}
838     */
839    shFloorCeil: function()
840    {
841      var flHidden = false, clHidden = false;
842
843      if(this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5)
844      {
845        flHidden = true;
846        this.hideEl(this.flrLab);
847      }
848      else
849      {
850        flHidden = false;
851        this.showEl(this.flrLab);
852      }
853
854      if(this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10)
855      {
856        clHidden = true;
857        this.hideEl(this.ceilLab);
858      }
859      else
860      {
861        clHidden = false;
862        this.showEl(this.ceilLab);
863      }
864
865      if(this.range)
866      {
867        if(this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10)
868        {
869          this.hideEl(this.ceilLab);
870        }
871        else if( ! clHidden)
872        {
873          this.showEl(this.ceilLab);
874        }
875
876        // Hide or show floor label
877        if(this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth)
878        {
879          this.hideEl(this.flrLab);
880        }
881        else if( ! flHidden)
882        {
883          this.showEl(this.flrLab);
884        }
885      }
886    },
887
888    /**
889     * Update slider selection bar, combined label and range label
890     *
891     * @returns {undefined}
892     */
893    updateSelectionBar: function()
894    {
895      this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth);
896      this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0);
897    },
898
899    /**
900     * Update combined label position and value
901     *
902     * @returns {undefined}
903     */
904    updateCmbLabel: function()
905    {
906      var lowTr, highTr;
907
908      if(this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl)
909      {
910        lowTr = this.getDisplayValue(this.scope.rzSliderModel);
911        highTr = this.getDisplayValue(this.scope.rzSliderHigh);
912
913        this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false);
914        this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2);
915        this.hideEl(this.minLab);
916        this.hideEl(this.maxLab);
917        this.showEl(this.cmbLab);
918      }
919      else
920      {
921        this.showEl(this.maxLab);
922        this.showEl(this.minLab);
923        this.hideEl(this.cmbLab);
924      }
925    },
926
927    /**
928     * Return the translated value if a translate function is provided else the original value
929     * @param value
930     * @returns {*}
931     */
932    getDisplayValue: function(value) {
933      return  this.customTrFn ? this.customTrFn(value): value;
934    },
935
936    /**
937     * Round value to step and precision
938     *
939     * @param {number} value
940     * @returns {number}
941     */
942    roundStep: function(value)
943    {
944      var step = this.step,
945          remainder = +((value - this.minValue) % step).toFixed(3),
946          steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder;
947
948      steppedValue = steppedValue.toFixed(this.precision);
949      return +steppedValue;
950    },
951
952    /**
953     * Hide element
954     *
955     * @param element
956     * @returns {jqLite} The jqLite wrapped DOM element
957     */
958    hideEl: function (element)
959    {
960      return element.css({opacity: 0});
961    },
962
963    /**
964     * Show element
965     *
966     * @param element The jqLite wrapped DOM element
967     * @returns {jqLite} The jqLite
968     */
969    showEl: function (element)
970    {
971      if(!!element.rzAlwaysHide) { return element; }
972
973      return element.css({opacity: 1});
974    },
975
976    /**
977     * Set element left offset
978     *
979     * @param {jqLite} elem The jqLite wrapped DOM element
980     * @param {number} left
981     * @returns {number}
982     */
983    setLeft: function (elem, left)
984    {
985      elem.rzsl = left;
986      elem.css({left: left + 'px'});
987      return left;
988    },
989
990    /**
991     * Get element width
992     *
993     * @param {jqLite} elem The jqLite wrapped DOM element
994     * @returns {number}
995     */
996    getWidth: function(elem)
997    {
998      var val = elem[0].getBoundingClientRect();
999      elem.rzsw = val.right - val.left;
1000      return elem.rzsw;
1001    },
1002
1003    /**
1004     * Set element width
1005     *
1006     * @param {jqLite} elem  The jqLite wrapped DOM element
1007     * @param {number} width
1008     * @returns {number}
1009     */
1010    setWidth: function(elem, width)
1011    {
1012      elem.rzsw = width;
1013      elem.css({width: width + 'px'});
1014      return width;
1015    },
1016
1017    /**
1018     * Translate value to pixel offset
1019     *
1020     * @param {number} val
1021     * @returns {number}
1022     */
1023    valueToOffset: function(val)
1024    {
1025      return (val - this.minValue) * this.maxLeft / this.valueRange || 0;
1026    },
1027
1028    /**
1029     * Translate offset to model value
1030     *
1031     * @param {number} offset
1032     * @returns {number}
1033     */
1034    offsetToValue: function(offset)
1035    {
1036      return (offset / this.maxLeft) * this.valueRange + this.minValue;
1037    },
1038
1039    // Events
1040
1041    /**
1042     * Get the X-coordinate of an event
1043     *
1044     * @param {Object} event  The event
1045     * @returns {number}
1046     */
1047    getEventX: function(event)
1048    {
1049      /* http://stackoverflow.com/a/12336075/282882 */
1050      //noinspection JSLint
1051      if('clientX' in event)
1052      {
1053        return event.clientX;
1054      }
1055
1056      return event.originalEvent === undefined ?
1057          event.touches[0].clientX
1058          : event.originalEvent.touches[0].clientX;
1059    },
1060
1061    /**
1062     * Get the handle closest to an event.
1063     *
1064     * @param event {Event} The event
1065     * @returns {jqLite} The handle closest to the event.
1066     */
1067    getNearestHandle: function(event)
1068    {
1069      if (!this.range) { return this.minH; }
1070      var offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth;
1071      return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH;
1072    },
1073
1074    /**
1075     * Bind mouse and touch events to slider handles
1076     *
1077     * @returns {undefined}
1078     */
1079    bindEvents: function()
1080    {
1081      if(this.presentOnly || this.disabled) return;
1082      var barTracking, barStart, barMove;
1083
1084      if (this.dragRange)
1085      {
1086        barTracking = 'rzSliderDrag';
1087        barStart = this.onDragStart;
1088        barMove = this.onDragMove;
1089      }
1090      else
1091      {
1092        barTracking = 'rzSliderModel';
1093        barStart = this.onStart;
1094        barMove = this.onMove;
1095      }
1096
1097      this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel'));
1098      if(this.range) { this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); }
1099      this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null));
1100      this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar));
1101      this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking));
1102      this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar));
1103      this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null));
1104      this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks));
1105
1106      this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel'));
1107      if(this.range) { this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); }
1108      this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null));
1109      this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar));
1110      this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking));
1111      this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar));
1112      this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null));
1113      this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks));
1114    },
1115
1116    /**
1117     * Unbind mouse and touch events to slider handles
1118     *
1119     * @returns {undefined}
1120     */
1121    unbindEvents: function()
1122    {
1123      this.minH.off();
1124      this.maxH.off();
1125      this.fullBar.off();
1126      this.selBar.off();
1127      this.ticks.off();
1128    },
1129
1130    /**
1131     * onStart event handler
1132     *
1133     * @param {?Object} pointer The jqLite wrapped DOM element; if null, the closest handle is used
1134     * @param {?string} ref     The name of the handle being changed; if null, the closest handle's value is modified
1135     * @param {Event}   event   The event
1136     * @returns {undefined}
1137     */
1138    onStart: function (pointer, ref, event)
1139    {
1140      var ehMove, ehEnd,
1141          eventNames = this.getEventNames(event);
1142
1143      event.stopPropagation();
1144      event.preventDefault();
1145
1146      if(this.tracking !== '') { return; }
1147
1148      // We have to do this in case the HTML where the sliders are on
1149      // have been animated into view.
1150      this.calcViewDimensions();
1151
1152      if(pointer)
1153      {
1154        this.tracking = ref;
1155      }
1156      else
1157      {
1158        pointer = this.getNearestHandle(event);
1159        this.tracking = pointer === this.minH ? 'rzSliderModel' : 'rzSliderHigh';
1160      }
1161
1162      pointer.addClass('rz-active');
1163
1164      ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer);
1165      ehEnd = angular.bind(this, this.onEnd, ehMove);
1166
1167      $document.on(eventNames.moveEvent, ehMove);
1168      $document.one(eventNames.endEvent, ehEnd);
1169      this.callOnStart();
1170    },
1171
1172    /**
1173     * onMove event handler
1174     *
1175     * @param {jqLite} pointer
1176     * @param {Event}  event The event
1177     * @returns {undefined}
1178     */
1179    onMove: function (pointer, event)
1180    {
1181      var eventX = this.getEventX(event),
1182          sliderLO, newOffset, newValue;
1183
1184      sliderLO = this.sliderElem.rzsl;
1185      newOffset = eventX - sliderLO - this.handleHalfWidth;
1186
1187      if(newOffset <= 0)
1188      {
1189        if(pointer.rzsl === 0)
1190          return;
1191        newValue = this.minValue;
1192        newOffset = 0;
1193      }
1194      else if(newOffset >= this.maxLeft)
1195      {
1196        if(pointer.rzsl === this.maxLeft)
1197          return;
1198        newValue = this.maxValue;
1199        newOffset = this.maxLeft;
1200      }
1201      else {
1202        newValue = this.offsetToValue(newOffset);
1203        newValue = this.roundStep(newValue);
1204        newOffset = this.valueToOffset(newValue);
1205      }
1206      this.positionTrackingHandle(newValue, newOffset);
1207    },
1208
1209    /**
1210     * onDragStart event handler
1211     *
1212     * Handles dragging of the middle bar.
1213     *
1214     * @param {Object} pointer The jqLite wrapped DOM element
1215     * @param {string} ref     One of the refLow, refHigh values
1216     * @param {Event}  event   The event
1217     * @returns {undefined}
1218     */
1219    onDragStart: function(pointer, ref, event)
1220    {
1221      var offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth;
1222      this.dragging = {
1223        active: true,
1224        value: this.offsetToValue(offset),
1225        difference: this.scope.rzSliderHigh - this.scope.rzSliderModel,
1226        offset: offset,
1227        lowDist: offset - this.minH.rzsl,
1228        highDist: this.maxH.rzsl - offset
1229      };
1230      this.minH.addClass('rz-active');
1231      this.maxH.addClass('rz-active');
1232
1233      this.onStart(pointer, ref, event);
1234    },
1235
1236    /**
1237     * onDragMove event handler
1238     *
1239     * Handles dragging of the middle bar.
1240     *
1241     * @param {jqLite} pointer
1242     * @param {Event}  event The event
1243     * @returns {undefined}
1244     */
1245    onDragMove: function(pointer, event)
1246    {
1247      var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth,
1248          newMinOffset, newMaxOffset,
1249          newMinValue, newMaxValue;
1250
1251      if (newOffset <= this.dragging.lowDist)
1252      {
1253        if (pointer.rzsl === this.dragging.lowDist) { return; }
1254        newMinValue = this.minValue;
1255        newMinOffset = 0;
1256        newMaxValue = this.dragging.difference;
1257        newMaxOffset = this.valueToOffset(newMaxValue);
1258      }
1259      else if (newOffset >= this.maxLeft - this.dragging.highDist)
1260      {
1261        if (pointer.rzsl === this.dragging.highDist) { return; }
1262        newMaxValue = this.maxValue;
1263        newMaxOffset = this.maxLeft;
1264        newMinValue = this.maxValue - this.dragging.difference;
1265        newMinOffset = this.valueToOffset(newMinValue);
1266      }
1267      else
1268      {
1269        newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist);
1270        newMinValue = this.roundStep(newMinValue);
1271        newMinOffset = this.valueToOffset(newMinValue);
1272        newMaxValue = newMinValue + this.dragging.difference;
1273        newMaxOffset = this.valueToOffset(newMaxValue);
1274      }
1275
1276      this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset);
1277    },
1278
1279    /**
1280     * Set the new value and offset for the entire bar
1281     *
1282     * @param {number} newMinValue   the new minimum value
1283     * @param {number} newMaxValue   the new maximum value
1284     * @param {number} newMinOffset  the new minimum offset
1285     * @param {number} newMaxOffset  the new maximum offset
1286     */
1287    positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset)
1288    {
1289      this.scope.rzSliderModel = newMinValue;
1290      this.scope.rzSliderHigh = newMaxValue;
1291      this.updateHandles('rzSliderModel', newMinOffset);
1292      this.updateHandles('rzSliderHigh', newMaxOffset);
1293      this.scope.$apply();
1294      this.callOnChange();
1295    },
1296
1297    /**
1298     * Set the new value and offset to the current tracking handle
1299     *
1300     * @param {number} newValue new model value
1301     * @param {number} newOffset new offset value
1302     */
1303    positionTrackingHandle: function(newValue, newOffset)
1304    {
1305      if(this.range)
1306      {
1307        /* This is to check if we need to switch the min and max handles*/
1308        if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh)
1309        {
1310          this.scope[this.tracking] = this.scope.rzSliderHigh;
1311          this.updateHandles(this.tracking, this.maxH.rzsl);
1312          this.tracking = 'rzSliderHigh';
1313          this.minH.removeClass('rz-active');
1314          this.maxH.addClass('rz-active');
1315           /* We need to apply here because we are not sure that we will enter the next block */
1316          this.scope.$apply();
1317          this.callOnChange();
1318        }
1319        else if(this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel)
1320        {
1321          this.scope[this.tracking] = this.scope.rzSliderModel;
1322          this.updateHandles(this.tracking, this.minH.rzsl);
1323          this.tracking = 'rzSliderModel';
1324          this.maxH.removeClass('rz-active');
1325          this.minH.addClass('rz-active');
1326           /* We need to apply here because we are not sure that we will enter the next block */
1327          this.scope.$apply();
1328          this.callOnChange();
1329        }
1330      }
1331
1332      if(this.scope[this.tracking] !== newValue)
1333      {
1334        this.scope[this.tracking] = newValue;
1335        this.updateHandles(this.tracking, newOffset);
1336        this.scope.$apply();
1337        this.callOnChange();
1338      }
1339    },
1340
1341    /**
1342     * onEnd event handler
1343     *
1344     * @param {Event}    event    The event
1345     * @param {Function} ehMove   The the bound move event handler
1346     * @returns {undefined}
1347     */
1348    onEnd: function(ehMove, event)
1349    {
1350      var moveEventName = this.getEventNames(event).moveEvent;
1351
1352      this.minH.removeClass('rz-active');
1353      this.maxH.removeClass('rz-active');
1354
1355      $document.off(moveEventName, ehMove);
1356
1357      this.scope.$emit('slideEnded');
1358      this.tracking = '';
1359
1360      this.dragging.active = false;
1361      this.callOnEnd();
1362    },
1363
1364    /**
1365     * Get event names for move and event end
1366     *
1367     * @param {Event}    event    The event
1368     *
1369     * @return {{moveEvent: string, endEvent: string}}
1370     */
1371    getEventNames: function(event)
1372    {
1373      var eventNames = {moveEvent: '', endEvent: ''};
1374
1375      if(event.touches || (event.originalEvent !== undefined && event.originalEvent.touches))
1376      {
1377        eventNames.moveEvent = 'touchmove';
1378        eventNames.endEvent = 'touchend';
1379      }
1380      else
1381      {
1382        eventNames.moveEvent = 'mousemove';
1383        eventNames.endEvent = 'mouseup';
1384      }
1385
1386      return eventNames;
1387    }
1388  };
1389
1390  return Slider;
1391}])
1392
1393.directive('rzslider', ['RzSlider', function(RzSlider)
1394{
1395  'use strict';
1396
1397  return {
1398    restrict: 'E',
1399    scope: {
1400      rzSliderFloor: '=?',
1401      rzSliderCeil: '=?',
1402      rzSliderStep: '@',
1403      rzSliderPrecision: '@',
1404      rzSliderModel: '=?',
1405      rzSliderHigh: '=?',
1406      rzSliderDraggable: '@',
1407      rzSliderTranslate: '&',
1408      rzSliderHideLimitLabels: '=?',
1409      rzSliderAlwaysShowBar: '=?',
1410      rzSliderPresentOnly: '@',
1411      rzSliderOnStart: '&',
1412      rzSliderOnChange: '&',
1413      rzSliderOnEnd: '&',
1414      rzSliderShowTicks: '=?',
1415      rzSliderShowTicksValue: '=?',
1416      rzSliderDisabled: '=?',
1417    },
1418
1419    /**
1420     * Return template URL
1421     *
1422     * @param {jqLite} elem
1423     * @param {Object} attrs
1424     * @return {string}
1425     */
1426    templateUrl: function(elem, attrs) {
1427      //noinspection JSUnresolvedVariable
1428      return attrs.rzSliderTplUrl || 'rzSliderTpl.html';
1429    },
1430
1431    link: function(scope, elem, attr)
1432    {
1433      return new RzSlider(scope, elem, attr);
1434    }
1435  };
1436}]);
1437
1438// IDE assist
1439
1440/**
1441 * @name ngScope
1442 *
1443 * @property {number} rzSliderModel
1444 * @property {number} rzSliderHigh
1445 * @property {number} rzSliderCeil
1446 */
1447
1448/**
1449 * @name jqLite
1450 *
1451 * @property {number|undefined} rzsl rzslider label left offset
1452 * @property {number|undefined} rzsw rzslider element width
1453 * @property {string|undefined} rzsv rzslider label value/text
1454 * @property {Function} css
1455 * @property {Function} text
1456 */
1457
1458/**
1459 * @name Event
1460 * @property {Array} touches
1461 * @property {Event} originalEvent
1462 */
1463
1464/**
1465 * @name ThrottleOptions
1466 *
1467 * @property {boolean} leading
1468 * @property {boolean} trailing
1469 */
1470
1471  module.run(['$templateCache', function($templateCache) {
1472  'use strict';
1473
1474  $templateCache.put('rzSliderTpl.html',
1475    "<span class=rz-bar-wrapper><span class=rz-bar></span></span> <span class=rz-bar-wrapper><span class=\"rz-bar rz-selection\"></span></span> <span class=rz-pointer></span> <span class=rz-pointer></span> <span class=\"rz-bubble rz-limit\"></span> <span class=\"rz-bubble rz-limit\"></span> <span class=rz-bubble></span> <span class=rz-bubble></span> <span class=rz-bubble></span><ul class=rz-ticks></ul>"
1476  );
1477
1478}]);
1479
1480  return module
1481}));

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.