text: 
  (function(root, factory) {
      'use strict';
      /* istanbul ignore next */
      if (typeof define === 'function' && define.amd) {
          // AMD. Register as an anonymous module.
          define(['angular'], factory);
      } else if (typeof module === 'object' && module.exports) {
          // Node. Does not work with strict CommonJS, but
          // only CommonJS-like environments that support module.exports,
          // like Node.
          // to support bundler like browserify
          var angularObj = angular || require('angular');
          if ((!angularObj || !angularObj.module) && typeof angular != 'undefined') {
              angularObj = angular;
          }
          module.exports = factory(angularObj);
      } else {
          // Browser globals (root is window)
          factory(root.angular);
      }
  
  }(this, function(angular) {
      'use strict';
      var module = angular.module('rzModule', [])
  
          .factory('RzSliderOptions', function() {
              var defaultOptions = {
                  floor: 0,
                  ceil: null, //defaults to rz-slider-model
                  step: 1,
                  precision: 0,
                  minRange: null,
                  maxRange: null,
                  pushRange: false,
                  minLimit: null,
                  maxLimit: null,
                  id: null,
                  translate: null,
                  getLegend: null,
                  stepsArray: null,
                  bindIndexForStepsArray: false,
                  draggableRange: false,
                  draggableRangeOnly: false,
                  showSelectionBar: false,
                  showSelectionBarEnd: false,
                  showSelectionBarFromValue: null,
                  hidePointerLabels: false,
                  hideLimitLabels: false,
                  autoHideLimitLabels: true,
                  readOnly: false,
                  disabled: false,
                  interval: 350,
                  showTicks: false,
                  showTicksValues: false,
                  ticksArray: null,
                  ticksTooltip: null,
                  ticksValuesTooltip: null,
                  vertical: false,
                  getSelectionBarColor: null,
                  getTickColor: null,
                  getPointerColor: null,
                  keyboardSupport: true,
                  scale: 1,
                  enforceStep: true,
                  enforceRange: false,
                  noSwitching: false,
                  onlyBindHandles: false,
                  onStart: null,
                  onChange: null,
                  onEnd: null,
                  rightToLeft: false,
                  boundPointerLabels: true,
                  mergeRangeLabelsIfSame: false,
                  customTemplateScope: null,
                  logScale: false,
                  customValueToPosition: null,
                  customPositionToValue: null,
                  selectionBarGradient: null,
                  ariaLabel: null,
                  ariaLabelledBy: null,
                  ariaLabelHigh: null,
                  ariaLabelledByHigh: null
              };
              var globalOptions = {};
  
              var factory = {};
              /**
               * `options({})` allows global configuration of all sliders in the
               * application.
               *
               *   var app = angular.module( 'App', ['rzModule'], function( RzSliderOptions ) {
       *     // show ticks for all sliders
       *     RzSliderOptions.options( { showTicks: true } );
       *   });
               */
              factory.options = function(value) {
                  angular.extend(globalOptions, value);
              };
  
              factory.getOptions = function(options) {
                  return angular.extend({}, defaultOptions, globalOptions, options);
              };
  
              return factory;
          })
  
          .factory('rzThrottle', ['$timeout', function($timeout) {
              /**
               *
               * @param {Function} func
               * @param {number} wait
               * @param {ThrottleOptions} options
               * @returns {Function}
               */
              return function(func, wait, options) {
                  'use strict';
                  /* istanbul ignore next */
                  var getTime = (Date.now || function() {
                      return new Date().getTime();
                  });
                  var context, args, result;
                  var timeout = null;
                  var previous = 0;
                  options = options || {};
                  var later = function() {
                      previous = getTime();
                      timeout = null;
                      result = func.apply(context, args);
                      context = args = null;
                  };
                  return function() {
                      var now = getTime();
                      var remaining = wait - (now - previous);
                      context = this;
                      args = arguments;
                      if (remaining <= 0) {
                          $timeout.cancel(timeout);
                          timeout = null;
                          previous = now;
                          result = func.apply(context, args);
                          context = args = null;
                      } else if (!timeout && options.trailing !== false) {
                          timeout = $timeout(later, remaining);
                      }
                      return result;
                  };
              }
          }])
  
          .factory('RzSlider', ['$timeout', '$document', '$window', '$compile', 'RzSliderOptions', 'rzThrottle', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) {
              'use strict';
  
              /**
               * Slider
               *
               * @param {ngScope} scope            The AngularJS scope
               * @param {Element} sliderElem The slider directive element wrapped in jqLite
               * @constructor
               */
              var Slider = function(scope, sliderElem) {
                  /**
                   * The slider's scope
                   *
                   * @type {ngScope}
                   */
                  this.scope = scope;
  
                  /**
                   * The slider inner low value (linked to rzSliderModel)
                   * @type {number}
                   */
                  this.lowValue = 0;
  
                  /**
                   * The slider inner high value (linked to rzSliderHigh)
                   * @type {number}
                   */
                  this.highValue = 0;
  
                  /**
                   * Slider element wrapped in jqLite
                   *
                   * @type {jqLite}
                   */
                  this.sliderElem = sliderElem;
  
                  /**
                   * Slider type
                   *
                   * @type {boolean} Set to true for range slider
                   */
                  this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
  
                  /**
                   * Values recorded when first dragging the bar
                   *
                   * @type {Object}
                   */
                  this.dragging = {
                      active: false,
                      value: 0,
                      difference: 0,
                      position: 0,
                      lowLimit: 0,
                      highLimit: 0
                  };
  
                  /**
                   * property that handle position (defaults to left for horizontal)
                   * @type {string}
                   */
                  this.positionProperty = 'left';
  
                  /**
                   * property that handle dimension (defaults to width for horizontal)
                   * @type {string}
                   */
                  this.dimensionProperty = 'width';
  
                  /**
                   * Half of the width or height of the slider handles
                   *
                   * @type {number}
                   */
                  this.handleHalfDim = 0;
  
                  /**
                   * Maximum position the slider handle can have
                   *
                   * @type {number}
                   */
                  this.maxPos = 0;
  
                  /**
                   * Precision
                   *
                   * @type {number}
                   */
                  this.precision = 0;
  
                  /**
                   * Step
                   *
                   * @type {number}
                   */
                  this.step = 1;
  
                  /**
                   * The name of the handle we are currently tracking
                   *
                   * @type {string}
                   */
                  this.tracking = '';
  
                  /**
                   * Minimum value (floor) of the model
                   *
                   * @type {number}
                   */
                  this.minValue = 0;
  
                  /**
                   * Maximum value (ceiling) of the model
                   *
                   * @type {number}
                   */
                  this.maxValue = 0;
  
  
                  /**
                   * The delta between min and max value
                   *
                   * @type {number}
                   */
                  this.valueRange = 0;
  
  
                  /**
                   * If showTicks/showTicksValues options are number.
                   * In this case, ticks values should be displayed below the slider.
                   * @type {boolean}
                   */
                  this.intermediateTicks = false;
  
                  /**
                   * Set to true if init method already executed
                   *
                   * @type {boolean}
                   */
                  this.initHasRun = false;
  
                  /**
                   * Used to call onStart on the first keydown event
                   *
                   * @type {boolean}
                   */
                  this.firstKeyDown = false;
  
                  /**
                   * Internal flag to prevent watchers to be called when the sliders value are modified internally.
                   * @type {boolean}
                   */
                  this.internalChange = false;
  
                  /**
                   * Internal flag to keep track of the visibility of combo label
                   * @type {boolean}
                   */
                  this.cmbLabelShown = false;
  
                  /**
                   * Internal variable to keep track of the focus element
                   */
                  this.currentFocusElement = null;
  
                  // Slider DOM elements wrapped in jqLite
                  this.fullBar = null; // The whole slider bar
                  this.selBar = null; // Highlight between two handles
                  this.minH = null; // Left slider handle
                  this.maxH = null; // Right slider handle
                  this.flrLab = null; // Floor label
                  this.ceilLab = null; // Ceiling label
                  this.minLab = null; // Label above the low value
                  this.maxLab = null; // Label above the high value
                  this.cmbLab = null; // Combined label
                  this.ticks = null; // The ticks
  
                  // Initialize slider
                  this.init();
              };
  
              // Add instance methods
              Slider.prototype = {
  
                  /**
                   * Initialize slider
                   *
                   * @returns {undefined}
                   */
                  init: function() {
                      var thrLow, thrHigh,
                          self = this;
  
                      var calcDimFn = function() {
                          self.calcViewDimensions();
                      };
  
                      this.applyOptions();
                      this.syncLowValue();
                      if (this.range)
                          this.syncHighValue();
                      this.initElemHandles();
                      this.manageElementsStyle();
                      this.setDisabledState();
                      this.calcViewDimensions();
                      this.setMinAndMax();
                      this.addAccessibility();
                      this.updateCeilLab();
                      this.updateFloorLab();
                      this.initHandles();
                      this.manageEventsBindings();
  
                      // Recalculate slider view dimensions
                      this.scope.$on('reCalcViewDimensions', calcDimFn);
  
                      // Recalculate stuff if view port dimensions have changed
                      angular.element($window).on('resize', calcDimFn);
  
                      this.initHasRun = true;
  
                      // Watch for changes to the model
                      thrLow = rzThrottle(function() {
                          self.onLowHandleChange();
                      }, self.options.interval);
  
                      thrHigh = rzThrottle(function() {
                          self.onHighHandleChange();
                      }, self.options.interval);
  
                      this.scope.$on('rzSliderForceRender', function() {
                          self.resetLabelsValue();
                          thrLow();
                          if (self.range) {
                              thrHigh();
                          }
                          self.resetSlider();
                      });
  
                      // Watchers (order is important because in case of simultaneous change,
                      // watchers will be called in the same order)
                      this.scope.$watch('rzSliderOptions()', function(newValue, oldValue) {
                          if (newValue === oldValue)
                              return;
                          self.applyOptions(); // need to be called before synchronizing the values
                          self.syncLowValue();
                          if (self.range)
                              self.syncHighValue();
                          self.resetSlider();
                      }, true);
  
                      this.scope.$watch('rzSliderModel', function(newValue, oldValue) {
                          if (self.internalChange)
                              return;
                          if (newValue === oldValue)
                              return;
                          thrLow();
                      });
  
                      this.scope.$watch('rzSliderHigh', function(newValue, oldValue) {
                          if (self.internalChange)
                              return;
                          if (newValue === oldValue)
                              return;
                          if (newValue != null)
                              thrHigh();
                          if (self.range && newValue == null || !self.range && newValue != null) {
                              self.applyOptions();
                              self.resetSlider();
                          }
                      });
  
                      this.scope.$on('$destroy', function() {
                          self.unbindEvents();
                          angular.element($window).off('resize', calcDimFn);
                          self.currentFocusElement = null;
                      });
                  },
  
                  findStepIndex: function(modelValue) {
                      var index = 0;
                      for (var i = 0; i < this.options.stepsArray.length; i++) {
                          var step = this.options.stepsArray[i];
                          if (step === modelValue) {
                              index = i;
                              break;
                          }
                          else if (angular.isDate(step)) {
                              if (step.getTime() === modelValue.getTime()) {
                                  index = i;
                                  break;
                              }
                          }
                          else if (angular.isObject(step)) {
                              if (angular.isDate(step.value) && step.value.getTime() === modelValue.getTime() || step.value === modelValue) {
                                  index = i;
                                  break;
                              }
                          }
                      }
                      return index;
                  },
  
                  syncLowValue: function() {
                      if (this.options.stepsArray) {
                          if (!this.options.bindIndexForStepsArray)
                              this.lowValue = this.findStepIndex(this.scope.rzSliderModel);
                          else
                              this.lowValue = this.scope.rzSliderModel
                      }
                      else
                          this.lowValue = this.scope.rzSliderModel;
                  },
  
                  syncHighValue: function() {
                      if (this.options.stepsArray) {
                          if (!this.options.bindIndexForStepsArray)
                              this.highValue = this.findStepIndex(this.scope.rzSliderHigh);
                          else
                              this.highValue = this.scope.rzSliderHigh
                      }
                      else
                          this.highValue = this.scope.rzSliderHigh;
                  },
  
                  getStepValue: function(sliderValue) {
                      var step = this.options.stepsArray[sliderValue];
                      if (angular.isDate(step))
                          return step;
                      if (angular.isObject(step))
                          return step.value;
                      return step;
                  },
  
                  applyLowValue: function() {
                      if (this.options.stepsArray) {
                          if (!this.options.bindIndexForStepsArray)
                              this.scope.rzSliderModel = this.getStepValue(this.lowValue);
                          else
                              this.scope.rzSliderModel = this.lowValue
                      }
                      else
                          this.scope.rzSliderModel = this.lowValue;
                  },
  
                  applyHighValue: function() {
                      if (this.options.stepsArray) {
                          if (!this.options.bindIndexForStepsArray)
                              this.scope.rzSliderHigh = this.getStepValue(this.highValue);
                          else
                              this.scope.rzSliderHigh = this.highValue
                      }
                      else
                          this.scope.rzSliderHigh = this.highValue;
                  },
  
                  /*
                   * Reflow the slider when the low handle changes (called with throttle)
                   */
                  onLowHandleChange: function() {
                      this.syncLowValue();
                      if (this.range)
                          this.syncHighValue();
                      this.setMinAndMax();
                      this.updateLowHandle(this.valueToPosition(this.lowValue));
                      this.updateSelectionBar();
                      this.updateTicksScale();
                      this.updateAriaAttributes();
                      if (this.range) {
                          this.updateCmbLabel();
                      }
                  },
  
                  /*
                   * Reflow the slider when the high handle changes (called with throttle)
                   */
                  onHighHandleChange: function() {
                      this.syncLowValue();
                      this.syncHighValue();
                      this.setMinAndMax();
                      this.updateHighHandle(this.valueToPosition(this.highValue));
                      this.updateSelectionBar();
                      this.updateTicksScale();
                      this.updateCmbLabel();
                      this.updateAriaAttributes();
                  },
  
                  /**
                   * Read the user options and apply them to the slider model
                   */
                  applyOptions: function() {
                      var sliderOptions;
                      if (this.scope.rzSliderOptions)
                          sliderOptions = this.scope.rzSliderOptions();
                      else
                          sliderOptions = {};
  
                      this.options = RzSliderOptions.getOptions(sliderOptions);
  
                      if (this.options.step <= 0)
                          this.options.step = 1;
  
                      this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
                      this.options.draggableRange = this.range && this.options.draggableRange;
                      this.options.draggableRangeOnly = this.range && this.options.draggableRangeOnly;
                      if (this.options.draggableRangeOnly) {
                          this.options.draggableRange = true;
                      }
  
                      this.options.showTicks = this.options.showTicks || this.options.showTicksValues || !!this.options.ticksArray;
                      this.scope.showTicks = this.options.showTicks; //scope is used in the template
                      if (angular.isNumber(this.options.showTicks) || this.options.ticksArray)
                          this.intermediateTicks = true;
  
                      this.options.showSelectionBar = this.options.showSelectionBar || this.options.showSelectionBarEnd
                          || this.options.showSelectionBarFromValue !== null;
  
                      if (this.options.stepsArray) {
                          this.parseStepsArray();
                      } else {
                          if (this.options.translate)
                              this.customTrFn = this.options.translate;
                          else
                              this.customTrFn = function(value) {
                                  return String(value);
                              };
  
                          this.getLegend = this.options.getLegend;
                      }
  
                      if (this.options.vertical) {
                          this.positionProperty = 'bottom';
                          this.dimensionProperty = 'height';
                      }
  
                      if (this.options.customTemplateScope)
                          this.scope.custom = this.options.customTemplateScope;
                  },
  
                  parseStepsArray: function() {
                      this.options.floor = 0;
                      this.options.ceil = this.options.stepsArray.length - 1;
                      this.options.step = 1;
  
                      if (this.options.translate) {
                          this.customTrFn = this.options.translate;
                      }
                      else {
                          this.customTrFn = function(modelValue) {
                              if (this.options.bindIndexForStepsArray)
                                  return this.getStepValue(modelValue);
                              return modelValue;
                          };
                      }
  
                      this.getLegend = function(index) {
                          var step = this.options.stepsArray[index];
                          if (angular.isObject(step))
                              return step.legend;
                          return null;
                      };
                  },
  
                  /**
                   * Resets slider
                   *
                   * @returns {undefined}
                   */
                  resetSlider: function() {
                      this.manageElementsStyle();
                      this.addAccessibility();
                      this.setMinAndMax();
                      this.updateCeilLab();
                      this.updateFloorLab();
                      this.unbindEvents();
                      this.manageEventsBindings();
                      this.setDisabledState();
                      this.calcViewDimensions();
                      this.refocusPointerIfNeeded();
                  },
  
                  refocusPointerIfNeeded: function() {
                      if (this.currentFocusElement) {
                          this.onPointerFocus(this.currentFocusElement.pointer, this.currentFocusElement.ref);
                          this.focusElement(this.currentFocusElement.pointer)
                      }
                  },
  
                  /**
                   * Set the slider children to variables for easy access
                   *
                   * Run only once during initialization
                   *
                   * @returns {undefined}
                   */
                  initElemHandles: function() {
                      // Assign all slider elements to object properties for easy access
                      angular.forEach(this.sliderElem.children(), function(elem, index) {
                          var jElem = angular.element(elem);
  
                          switch (index) {
                              case 0:
                                  this.fullBar = jElem;
                                  break;
                              case 1:
                                  this.selBar = jElem;
                                  break;
                              case 2:
                                  this.minH = jElem;
                                  break;
                              case 3:
                                  this.maxH = jElem;
                                  break;
                              case 4:
                                  this.flrLab = jElem;
                                  break;
                              case 5:
                                  this.ceilLab = jElem;
                                  break;
                              case 6:
                                  this.minLab = jElem;
                                  break;
                              case 7:
                                  this.maxLab = jElem;
                                  break;
                              case 8:
                                  this.cmbLab = jElem;
                                  break;
                              case 9:
                                  this.ticks = jElem;
                                  break;
                          }
  
                      }, this);
  
                      // Initialize position cache properties
                      this.selBar.rzsp = 0;
                      this.minH.rzsp = 0;
                      this.maxH.rzsp = 0;
                      this.flrLab.rzsp = 0;
                      this.ceilLab.rzsp = 0;
                      this.minLab.rzsp = 0;
                      this.maxLab.rzsp = 0;
                      this.cmbLab.rzsp = 0;
                  },
  
                  /**
                   * Update each elements style based on options
                   */
                  manageElementsStyle: function() {
  
                      if (!this.range)
                          this.maxH.css('display', 'none');
                      else
                          this.maxH.css('display', '');
  
  
                      this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels);
                      this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels);
  
                      var hideLabelsForTicks = this.options.showTicksValues && !this.intermediateTicks;
                      this.alwaysHide(this.minLab, hideLabelsForTicks || this.options.hidePointerLabels);
                      this.alwaysHide(this.maxLab, hideLabelsForTicks || !this.range || this.options.hidePointerLabels);
                      this.alwaysHide(this.cmbLab, hideLabelsForTicks || !this.range || this.options.hidePointerLabels);
                      this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar);
  
                      if (this.options.vertical)
                          this.sliderElem.addClass('rz-vertical');
  
                      if (this.options.draggableRange)
                          this.selBar.addClass('rz-draggable');
                      else
                          this.selBar.removeClass('rz-draggable');
  
                      if (this.intermediateTicks && this.options.showTicksValues)
                          this.ticks.addClass('rz-ticks-values-under');
                  },
  
                  alwaysHide: function(el, hide) {
                      el.rzAlwaysHide = hide;
                      if (hide)
                          this.hideEl(el);
                      else
                          this.showEl(el)
                  },
  
                  /**
                   * Manage the events bindings based on readOnly and disabled options
                   *
                   * @returns {undefined}
                   */
                  manageEventsBindings: function() {
                      if (this.options.disabled || this.options.readOnly)
                          this.unbindEvents();
                      else
                          this.bindEvents();
                  },
  
                  /**
                   * Set the disabled state based on rzSliderDisabled
                   *
                   * @returns {undefined}
                   */
                  setDisabledState: function() {
                      if (this.options.disabled) {
                          this.sliderElem.attr('disabled', 'disabled');
                      } else {
                          this.sliderElem.attr('disabled', null);
                      }
                  },
  
                  /**
                   * Reset label values
                   *
                   * @return {undefined}
                   */
                  resetLabelsValue: function() {
                      this.minLab.rzsv = undefined;
                      this.maxLab.rzsv = undefined;
                  },
  
                  /**
                   * Initialize slider handles positions and labels
                   *
                   * Run only once during initialization and every time view port changes size
                   *
                   * @returns {undefined}
                   */
                  initHandles: function() {
                      this.updateLowHandle(this.valueToPosition(this.lowValue));
  
                      /*
                       the order here is important since the selection bar should be
                       updated after the high handle but before the combined label
                       */
                      if (this.range)
                          this.updateHighHandle(this.valueToPosition(this.highValue));
                      this.updateSelectionBar();
                      if (this.range)
                          this.updateCmbLabel();
  
                      this.updateTicksScale();
                  },
  
                  /**
                   * Translate value to human readable format
                   *
                   * @param {number|string} value
                   * @param {jqLite} label
                   * @param {String} which
                   * @param {boolean} [useCustomTr]
                   * @returns {undefined}
                   */
                  translateFn: function(value, label, which, useCustomTr) {
                      useCustomTr = useCustomTr === undefined ? true : useCustomTr;
  
                      var valStr = '',
                          getDimension = false,
                          noLabelInjection = label.hasClass('no-label-injection');
  
                      if (useCustomTr) {
                          if (this.options.stepsArray && !this.options.bindIndexForStepsArray)
                              value = this.getStepValue(value);
                          valStr = String(this.customTrFn(value, this.options.id, which));
                      }
                      else {
                          valStr = String(value)
                      }
  
                      if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsd === 0)) {
                          getDimension = true;
                          label.rzsv = valStr;
                      }
  
                      if (!noLabelInjection) {
                          label.html(valStr);
                      }
                      ;
  
                      this.scope[which + 'Label'] = valStr;
  
                      // Update width only when length of the label have changed
                      if (getDimension) {
                          this.getDimension(label);
                      }
                  },
  
                  /**
                   * Set maximum and minimum values for the slider and ensure the model and high
                   * value match these limits
                   * @returns {undefined}
                   */
                  setMinAndMax: function() {
  
                      this.step = +this.options.step;
                      this.precision = +this.options.precision;
  
                      this.minValue = this.options.floor;
                      if (this.options.logScale && this.minValue === 0)
                          throw Error("Can't use floor=0 with logarithmic scale");
  
                      if (this.options.enforceStep) {
                          this.lowValue = this.roundStep(this.lowValue);
                          if (this.range)
                              this.highValue = this.roundStep(this.highValue);
                      }
  
                      if (this.options.ceil != null)
                          this.maxValue = this.options.ceil;
                      else
                          this.maxValue = this.options.ceil = this.range ? this.highValue : this.lowValue;
  
                      if (this.options.enforceRange) {
                          this.lowValue = this.sanitizeValue(this.lowValue);
                          if (this.range)
                              this.highValue = this.sanitizeValue(this.highValue);
                      }
  
                      this.applyLowValue();
                      if (this.range)
                          this.applyHighValue();
  
                      this.valueRange = this.maxValue - this.minValue;
                  },
  
                  /**
                   * Adds accessibility attributes
                   *
                   * Run only once during initialization
                   *
                   * @returns {undefined}
                   */
                  addAccessibility: function() {
                      this.minH.attr('role', 'slider');
                      this.updateAriaAttributes();
                      if (this.options.keyboardSupport && !(this.options.readOnly || this.options.disabled))
                          this.minH.attr('tabindex', '0');
                      else
                          this.minH.attr('tabindex', '');
                      if (this.options.vertical)
                          this.minH.attr('aria-orientation', 'vertical');
                      if (this.options.ariaLabel)
                          this.minH.attr('aria-label', this.options.ariaLabel);
                      else if (this.options.ariaLabelledBy)
                          this.minH.attr('aria-labelledby', this.options.ariaLabelledBy);
  
                      if (this.range) {
                          this.maxH.attr('role', 'slider');
                          if (this.options.keyboardSupport && !(this.options.readOnly || this.options.disabled))
                              this.maxH.attr('tabindex', '0');
                          else
                              this.maxH.attr('tabindex', '');
                          if (this.options.vertical)
                              this.maxH.attr('aria-orientation', 'vertical');
                          if (this.options.ariaLabelHigh)
                              this.maxH.attr('aria-label', this.options.ariaLabelHigh);
                          else if (this.options.ariaLabelledByHigh)
                              this.maxH.attr('aria-labelledby', this.options.ariaLabelledByHigh);
                      }
                  },
  
                  /**
                   * Updates aria attributes according to current values
                   */
                  updateAriaAttributes: function() {
                      this.minH.attr({
                          'aria-valuenow': this.scope.rzSliderModel,
                          'aria-valuetext': this.customTrFn(this.scope.rzSliderModel, this.options.id, 'model'),
                          'aria-valuemin': this.minValue,
                          'aria-valuemax': this.maxValue
                      });
                      if (this.range) {
                          this.maxH.attr({
                              'aria-valuenow': this.scope.rzSliderHigh,
                              'aria-valuetext': this.customTrFn(this.scope.rzSliderHigh, this.options.id, 'high'),
                              'aria-valuemin': this.minValue,
                              'aria-valuemax': this.maxValue
                          });
                      }
                  },
  
                  /**
                   * Calculate dimensions that are dependent on view port size
                   *
                   * Run once during initialization and every time view port changes size.
                   *
                   * @returns {undefined}
                   */
                  calcViewDimensions: function() {
                      var handleWidth = this.getDimension(this.minH);
  
                      this.handleHalfDim = handleWidth / 2;
                      this.barDimension = this.getDimension(this.fullBar);
  
                      this.maxPos = this.barDimension - handleWidth;
  
                      this.getDimension(this.sliderElem);
                      this.sliderElem.rzsp = this.sliderElem[0].getBoundingClientRect()[this.positionProperty];
  
                      if (this.initHasRun) {
                          this.updateFloorLab();
                          this.updateCeilLab();
                          this.initHandles();
                          var self = this;
                          $timeout(function() {
                              self.updateTicksScale();
                          });
                      }
                  },
  
                  /**
                   * Update the ticks position
                   *
                   * @returns {undefined}
                   */
                  updateTicksScale: function() {
                      if (!this.options.showTicks) return;
  
                      var ticksArray = this.options.ticksArray || this.getTicksArray(),
                          translate = this.options.vertical ? 'translateY' : 'translateX',
                          self = this;
  
                      if (this.options.rightToLeft)
                          ticksArray.reverse();
  
                      this.scope.ticks = ticksArray.map(function(value) {
                          var position = self.valueToPosition(value);
  
                          if (self.options.vertical)
                              position = self.maxPos - position;
  
                          var translation = translate + '(' + Math.round(position) + 'px)';
                          var tick = {
                              selected: self.isTickSelected(value),
                              style: {
                                  '-webkit-transform': translation,
                                  '-moz-transform': translation,
                                  '-o-transform': translation,
                                  '-ms-transform': translation,
                                  'transform': translation
                              }
                          };
                          if (tick.selected && self.options.getSelectionBarColor) {
                              tick.style['background-color'] = self.getSelectionBarColor();
                          }
                          if (!tick.selected && self.options.getTickColor) {
                              tick.style['background-color'] = self.getTickColor(value);
                          }
                          if (self.options.ticksTooltip) {
                              tick.tooltip = self.options.ticksTooltip(value);
                              tick.tooltipPlacement = self.options.vertical ? 'right' : 'top';
                          }
                          if (self.options.showTicksValues) {
                              tick.value = self.getDisplayValue(value, 'tick-value');
                              if (self.options.ticksValuesTooltip) {
                                  tick.valueTooltip = self.options.ticksValuesTooltip(value);
                                  tick.valueTooltipPlacement = self.options.vertical ? 'right' : 'top';
                              }
                          }
                          if (self.getLegend) {
                              var legend = self.getLegend(value, self.options.id);
                              if (legend)
                                  tick.legend = legend;
                          }
                          return tick;
                      });
                  },
  
                  getTicksArray: function() {
                      var step = this.step,
                          ticksArray = [];
                      if (this.intermediateTicks)
                          step = this.options.showTicks;
                      for (var value = this.minValue; value <= this.maxValue; value += step) {
                          ticksArray.push(value);
                      }
                      return ticksArray;
                  },
  
                  isTickSelected: function(value) {
                      if (!this.range) {
                          if (this.options.showSelectionBarFromValue !== null) {
                              var center = this.options.showSelectionBarFromValue;
                              if (this.lowValue > center && value >= center && value <= this.lowValue)
                                  return true;
                              else if (this.lowValue < center && value <= center && value >= this.lowValue)
                                  return true;
                          }
                          else if (this.options.showSelectionBarEnd) {
                              if (value >= this.lowValue)
                                  return true;
                          }
                          else if (this.options.showSelectionBar && value <= this.lowValue)
                              return true;
                      }
                      if (this.range && value >= this.lowValue && value <= this.highValue)
                          return true;
                      return false;
                  },
  
                  /**
                   * Update position of the floor label
                   *
                   * @returns {undefined}
                   */
                  updateFloorLab: function() {
                      this.translateFn(this.minValue, this.flrLab, 'floor');
                      this.getDimension(this.flrLab);
                      var position = this.options.rightToLeft ? this.barDimension - this.flrLab.rzsd : 0;
                      this.setPosition(this.flrLab, position);
                  },
  
                  /**
                   * Update position of the ceiling label
                   *
                   * @returns {undefined}
                   */
                  updateCeilLab: function() {
                      this.translateFn(this.maxValue, this.ceilLab, 'ceil');
                      this.getDimension(this.ceilLab);
                      var position = this.options.rightToLeft ? 0 : this.barDimension - this.ceilLab.rzsd;
                      this.setPosition(this.ceilLab, position);
                  },
  
                  /**
                   * Update slider handles and label positions
                   *
                   * @param {string} which
                   * @param {number} newPos
                   */
                  updateHandles: function(which, newPos) {
                      if (which === 'lowValue')
                          this.updateLowHandle(newPos);
                      else
                          this.updateHighHandle(newPos);
  
                      this.updateSelectionBar();
                      this.updateTicksScale();
                      if (this.range)
                          this.updateCmbLabel();
                  },
  
                  /**
                   * Helper function to work out the position for handle labels depending on RTL or not
                   *
                   * @param {string} labelName maxLab or minLab
                   * @param newPos
                   *
                   * @returns {number}
                   */
                  getHandleLabelPos: function(labelName, newPos) {
                      var labelRzsd = this[labelName].rzsd,
                          nearHandlePos = newPos - labelRzsd / 2 + this.handleHalfDim,
                          endOfBarPos = this.barDimension - labelRzsd;
  
                      if (!this.options.boundPointerLabels)
                          return nearHandlePos;
  
                      if (this.options.rightToLeft && labelName === 'minLab' || !this.options.rightToLeft && labelName === 'maxLab') {
                          return Math.min(nearHandlePos, endOfBarPos);
                      } else {
                          return Math.min(Math.max(nearHandlePos, 0), endOfBarPos);
                      }
                  },
  
                  /**
                   * Update low slider handle position and label
                   *
                   * @param {number} newPos
                   * @returns {undefined}
                   */
                  updateLowHandle: function(newPos) {
                      this.setPosition(this.minH, newPos);
                      this.translateFn(this.lowValue, this.minLab, 'model');
                      this.setPosition(this.minLab, this.getHandleLabelPos('minLab', newPos));
  
                      if (this.options.getPointerColor) {
                          var pointercolor = this.getPointerColor('min');
                          this.scope.minPointerStyle = {
                              backgroundColor: pointercolor
                          };
                      }
  
                      if (this.options.autoHideLimitLabels) {
                          this.shFloorCeil();
                      }
                  },
  
                  /**
                   * Update high slider handle position and label
                   *
                   * @param {number} newPos
                   * @returns {undefined}
                   */
                  updateHighHandle: function(newPos) {
                      this.setPosition(this.maxH, newPos);
                      this.translateFn(this.highValue, this.maxLab, 'high');
                      this.setPosition(this.maxLab, this.getHandleLabelPos('maxLab', newPos));
  
                      if (this.options.getPointerColor) {
                          var pointercolor = this.getPointerColor('max');
                          this.scope.maxPointerStyle = {
                              backgroundColor: pointercolor
                          };
                      }
                      if (this.options.autoHideLimitLabels) {
                          this.shFloorCeil();
                      }
  
                  },
  
                  /**
                   * Show/hide floor/ceiling label
                   *
                   * @returns {undefined}
                   */
                  shFloorCeil: function() {
                      // Show based only on hideLimitLabels if pointer labels are hidden
                      if (this.options.hidePointerLabels) {
                          return;
                      }
                      var flHidden = false,
                          clHidden = false,
                          isMinLabAtFloor = this.isLabelBelowFloorLab(this.minLab),
                          isMinLabAtCeil = this.isLabelAboveCeilLab(this.minLab),
                          isMaxLabAtCeil = this.isLabelAboveCeilLab(this.maxLab),
                          isCmbLabAtFloor = this.isLabelBelowFloorLab(this.cmbLab),
                          isCmbLabAtCeil = this.isLabelAboveCeilLab(this.cmbLab);
  
                      if (isMinLabAtFloor) {
                          flHidden = true;
                          this.hideEl(this.flrLab);
                      } else {
                          flHidden = false;
                          this.showEl(this.flrLab);
                      }
  
                      if (isMinLabAtCeil) {
                          clHidden = true;
                          this.hideEl(this.ceilLab);
                      } else {
                          clHidden = false;
                          this.showEl(this.ceilLab);
                      }
  
                      if (this.range) {
                          var hideCeil = this.cmbLabelShown ? isCmbLabAtCeil : isMaxLabAtCeil;
                          var hideFloor = this.cmbLabelShown ? isCmbLabAtFloor : isMinLabAtFloor;
  
                          if (hideCeil) {
                              this.hideEl(this.ceilLab);
                          } else if (!clHidden) {
                              this.showEl(this.ceilLab);
                          }
  
                          // Hide or show floor label
                          if (hideFloor) {
                              this.hideEl(this.flrLab);
                          } else if (!flHidden) {
                              this.showEl(this.flrLab);
                          }
                      }
                  },
  
                  isLabelBelowFloorLab: function(label) {
                      var isRTL = this.options.rightToLeft,
                          pos = label.rzsp,
                          dim = label.rzsd,
                          floorPos = this.flrLab.rzsp,
                          floorDim = this.flrLab.rzsd;
                      return isRTL ?
                          pos + dim >= floorPos - 2 :
                          pos <= floorPos + floorDim + 2;
                  },
  
                  isLabelAboveCeilLab: function(label) {
                      var isRTL = this.options.rightToLeft,
                          pos = label.rzsp,
                          dim = label.rzsd,
                          ceilPos = this.ceilLab.rzsp,
                          ceilDim = this.ceilLab.rzsd;
                      return isRTL ?
                          pos <= ceilPos + ceilDim + 2 :
                          pos + dim >= ceilPos - 2;
                  },
  
                  /**
                   * Update slider selection bar, combined label and range label
                   *
                   * @returns {undefined}
                   */
                  updateSelectionBar: function() {
                      var position = 0,
                          dimension = 0,
                          isSelectionBarFromRight = this.options.rightToLeft ? !this.options.showSelectionBarEnd : this.options.showSelectionBarEnd,
                          positionForRange = this.options.rightToLeft ? this.maxH.rzsp + this.handleHalfDim : this.minH.rzsp + this.handleHalfDim;
  
                      if (this.range) {
                          dimension = Math.abs(this.maxH.rzsp - this.minH.rzsp);
                          position = positionForRange;
                      }
                      else {
                          if (this.options.showSelectionBarFromValue !== null) {
                              var center = this.options.showSelectionBarFromValue,
                                  centerPosition = this.valueToPosition(center),
                                  isModelGreaterThanCenter = this.options.rightToLeft ? this.lowValue <= center : this.lowValue > center;
                              if (isModelGreaterThanCenter) {
                                  dimension = this.minH.rzsp - centerPosition;
                                  position = centerPosition + this.handleHalfDim;
                              }
                              else {
                                  dimension = centerPosition - this.minH.rzsp;
                                  position = this.minH.rzsp + this.handleHalfDim;
                              }
                          }
                          else if (isSelectionBarFromRight) {
                              dimension = Math.abs(this.maxPos - this.minH.rzsp) + this.handleHalfDim;
                              position = this.minH.rzsp + this.handleHalfDim;
                          } else {
                              dimension = this.minH.rzsp + this.handleHalfDim;
                              position = 0;
                          }
                      }
                      this.setDimension(this.selBar, dimension);
                      this.setPosition(this.selBar, position);
                      if (this.options.getSelectionBarColor) {
                          var color = this.getSelectionBarColor();
                          this.scope.barStyle = {
                              backgroundColor: color
                          };
                      } else if (this.options.selectionBarGradient) {
                          var offset = this.options.showSelectionBarFromValue !== null ? this.valueToPosition(this.options.showSelectionBarFromValue) : 0,
                              reversed = offset - position > 0 ^ isSelectionBarFromRight,
                              direction = this.options.vertical ? (reversed ? 'bottom' : 'top') : (reversed ? 'left' : 'right');
                          this.scope.barStyle = {
                              backgroundImage: 'linear-gradient(to ' + direction + ', ' + this.options.selectionBarGradient.from + ' 0%,' + this.options.selectionBarGradient.to + ' 100%)'
                          };
                          if (this.options.vertical) {
                              this.scope.barStyle.backgroundPosition = 'center ' + (offset + dimension + position + (reversed ? -this.handleHalfDim : 0)) + 'px';
                              this.scope.barStyle.backgroundSize = '100% ' + (this.barDimension - this.handleHalfDim) + 'px';
                          } else {
                              this.scope.barStyle.backgroundPosition = (offset - position + (reversed ? this.handleHalfDim : 0)) + 'px center';
                              this.scope.barStyle.backgroundSize = (this.barDimension - this.handleHalfDim) + 'px 100%';
                          }
                      }
                  },
  
                  /**
                   * Wrapper around the getSelectionBarColor of the user to pass to
                   * correct parameters
                   */
                  getSelectionBarColor: function() {
                      if (this.range)
                          return this.options.getSelectionBarColor(this.scope.rzSliderModel, this.scope.rzSliderHigh);
                      return this.options.getSelectionBarColor(this.scope.rzSliderModel);
                  },
  
                  /**
                   * Wrapper around the getPointerColor of the user to pass to
                   * correct parameters
                   */
                  getPointerColor: function(pointerType) {
                      if (pointerType === 'max') {
                          return this.options.getPointerColor(this.scope.rzSliderHigh, pointerType);
                      }
                      return this.options.getPointerColor(this.scope.rzSliderModel, pointerType);
                  },
  
                  /**
                   * Wrapper around the getTickColor of the user to pass to
                   * correct parameters
                   */
                  getTickColor: function(value) {
                      return this.options.getTickColor(value);
                  },
  
                  /**
                   * Update combined label position and value
                   *
                   * @returns {undefined}
                   */
                  updateCmbLabel: function() {
                      var isLabelOverlap = null;
                      if (this.options.rightToLeft) {
                          isLabelOverlap = this.minLab.rzsp - this.minLab.rzsd - 10 <= this.maxLab.rzsp;
                      } else {
                          isLabelOverlap = this.minLab.rzsp + this.minLab.rzsd + 10 >= this.maxLab.rzsp;
                      }
  
                      if (isLabelOverlap) {
                          var lowTr = this.getDisplayValue(this.lowValue, 'model'),
                              highTr = this.getDisplayValue(this.highValue, 'high'),
                              labelVal = '';
                          if (this.options.mergeRangeLabelsIfSame && lowTr === highTr) {
                              labelVal = lowTr;
                          } else {
                              labelVal = this.options.rightToLeft ? highTr + ' - ' + lowTr : lowTr + ' - ' + highTr;
                          }
  
                          this.translateFn(labelVal, this.cmbLab, 'cmb', false);
                          var pos = this.options.boundPointerLabels ? Math.min(
                                  Math.max(
                                      this.selBar.rzsp + this.selBar.rzsd / 2 - this.cmbLab.rzsd / 2,
                                      0
                                  ),
                                  this.barDimension - this.cmbLab.rzsd
                              ) : this.selBar.rzsp + this.selBar.rzsd / 2 - this.cmbLab.rzsd / 2;
  
                          this.setPosition(this.cmbLab, pos);
                          this.cmbLabelShown = true;
                          this.hideEl(this.minLab);
                          this.hideEl(this.maxLab);
                          this.showEl(this.cmbLab);
                      } else {
                          this.cmbLabelShown = false;
                          this.updateHighHandle(this.valueToPosition(this.highValue));
                          this.updateLowHandle(this.valueToPosition(this.lowValue));
                          this.showEl(this.maxLab);
                          this.showEl(this.minLab);
                          this.hideEl(this.cmbLab);
                      }
                      if (this.options.autoHideLimitLabels) {
                          this.shFloorCeil();
                      }
                  },
  
                  /**
                   * Return the translated value if a translate function is provided else the original value
                   * @param value
                   * @param which if it's min or max handle
                   * @returns {*}
                   */
                  getDisplayValue: function(value, which) {
                      if (this.options.stepsArray && !this.options.bindIndexForStepsArray) {
                          value = this.getStepValue(value);
                      }
                      return this.customTrFn(value, this.options.id, which);
                  },
  
                  /**
                   * Round value to step and precision based on minValue
                   *
                   * @param {number} value
                   * @param {number} customStep a custom step to override the defined step
                   * @returns {number}
                   */
                  roundStep: function(value, customStep) {
                      var step = customStep ? customStep : this.step,
                          steppedDifference = parseFloat((value - this.minValue) / step).toPrecision(12);
                      steppedDifference = Math.round(+steppedDifference) * step;
                      var newValue = (this.minValue + steppedDifference).toFixed(this.precision);
                      return +newValue;
                  },
  
                  /**
                   * Hide element
                   *
                   * @param element
                   * @returns {jqLite} The jqLite wrapped DOM element
                   */
                  hideEl: function(element) {
                      return element.css({
                          visibility: 'hidden'
                      });
                  },
  
                  /**
                   * Show element
                   *
                   * @param element The jqLite wrapped DOM element
                   * @returns {jqLite} The jqLite
                   */
                  showEl: function(element) {
                      if (!!element.rzAlwaysHide) {
                          return element;
                      }
  
                      return element.css({
                          visibility: 'visible'
                      });
                  },
  
                  /**
                   * Set element left/top position depending on whether slider is horizontal or vertical
                   *
                   * @param {jqLite} elem The jqLite wrapped DOM element
                   * @param {number} pos
                   * @returns {number}
                   */
                  setPosition: function(elem, pos) {
                      elem.rzsp = pos;
                      var css = {};
                      css[this.positionProperty] = Math.round(pos) + 'px';
                      elem.css(css);
                      return pos;
                  },
  
                  /**
                   * Get element width/height depending on whether slider is horizontal or vertical
                   *
                   * @param {jqLite} elem The jqLite wrapped DOM element
                   * @returns {number}
                   */
                  getDimension: function(elem) {
                      var val = elem[0].getBoundingClientRect();
                      if (this.options.vertical)
                          elem.rzsd = (val.bottom - val.top) * this.options.scale;
                      else
                          elem.rzsd = (val.right - val.left) * this.options.scale;
                      return elem.rzsd;
                  },
  
                  /**
                   * Set element width/height depending on whether slider is horizontal or vertical
                   *
                   * @param {jqLite} elem  The jqLite wrapped DOM element
                   * @param {number} dim
                   * @returns {number}
                   */
                  setDimension: function(elem, dim) {
                      elem.rzsd = dim;
                      var css = {};
                      css[this.dimensionProperty] = Math.round(dim) + 'px';
                      elem.css(css);
                      return dim;
                  },
  
                  /**
                   * Returns a value that is within slider range
                   *
                   * @param {number} val
                   * @returns {number}
                   */
                  sanitizeValue: function(val) {
                      return Math.min(Math.max(val, this.minValue), this.maxValue);
                  },
  
                  /**
                   * Translate value to pixel position
                   *
                   * @param {number} val
                   * @returns {number}
                   */
                  valueToPosition: function(val) {
                      var fn = this.linearValueToPosition;
                      if (this.options.customValueToPosition)
                          fn = this.options.customValueToPosition;
                      else if (this.options.logScale)
                          fn = this.logValueToPosition;
  
                      val = this.sanitizeValue(val);
                      var percent = fn(val, this.minValue, this.maxValue) || 0;
                      if (this.options.rightToLeft)
                          percent = 1 - percent;
                      return percent * this.maxPos;
                  },
  
                  linearValueToPosition: function(val, minVal, maxVal) {
                      var range = maxVal - minVal;
                      return (val - minVal) / range;
                  },
  
                  logValueToPosition: function(val, minVal, maxVal) {
                      val = Math.log(val);
                      minVal = Math.log(minVal);
                      maxVal = Math.log(maxVal);
                      var range = maxVal - minVal;
                      return (val - minVal) / range;
                  },
  
                  /**
                   * Translate position to model value
                   *
                   * @param {number} position
                   * @returns {number}
                   */
                  positionToValue: function(position) {
                      var percent = position / this.maxPos;
                      if (this.options.rightToLeft)
                          percent = 1 - percent;
                      var fn = this.linearPositionToValue;
                      if (this.options.customPositionToValue)
                          fn = this.options.customPositionToValue;
                      else if (this.options.logScale)
                          fn = this.logPositionToValue;
                      return fn(percent, this.minValue, this.maxValue) || 0;
                  },
  
                  linearPositionToValue: function(percent, minVal, maxVal) {
                      return percent * (maxVal - minVal) + minVal;
                  },
  
                  logPositionToValue: function(percent, minVal, maxVal) {
                      minVal = Math.log(minVal);
                      maxVal = Math.log(maxVal);
                      var value = percent * (maxVal - minVal) + minVal;
                      return Math.exp(value);
                  },
  
                  getEventAttr: function(event, attr) {
                      return event.originalEvent === undefined ? event[attr] : event.originalEvent[attr]
                  },
  
                  // Events
                  /**
                   * Get the X-coordinate or Y-coordinate of an event
                   *
                   * @param {Object} event  The event
                   * @param targetTouchId The identifier of the touch with the X/Y coordinates
                   * @returns {number}
                   */
                  getEventXY: function(event, targetTouchId) {
                      /* http://stackoverflow.com/a/12336075/282882 */
                      //noinspection JSLint
                      var clientXY = this.options.vertical ? 'clientY' : 'clientX';
                      if (event[clientXY] !== undefined) {
                          return event[clientXY];
                      }
  
                      var touches = this.getEventAttr(event, 'touches');
  
                      if (targetTouchId !== undefined) {
                          for (var i = 0; i < touches.length; i++) {
                              if (touches[i].identifier === targetTouchId) {
                                  return touches[i][clientXY];
                              }
                          }
                      }
  
                      // If no target touch or the target touch was not found in the event
                      // returns the coordinates of the first touch
                      return touches[0][clientXY];
                  },
  
                  /**
                   * Compute the event position depending on whether the slider is horizontal or vertical
                   * @param event
                   * @param targetTouchId If targetTouchId is provided it will be considered the position of that
                   * @returns {number}
                   */
                  getEventPosition: function(event, targetTouchId) {
                      var sliderPos = this.sliderElem.rzsp,
                          eventPos = 0;
                      if (this.options.vertical)
                          eventPos = -this.getEventXY(event, targetTouchId) + sliderPos;
                      else
                          eventPos = this.getEventXY(event, targetTouchId) - sliderPos;
                      return eventPos * this.options.scale - this.handleHalfDim; // #346 handleHalfDim is already scaled
                  },
  
                  /**
                   * Get event names for move and event end
                   *
                   * @param {Event}    event    The event
                   *
                   * @return {{moveEvent: string, endEvent: string}}
                   */
                  getEventNames: function(event) {
                      var eventNames = {
                          moveEvent: '',
                          endEvent: ''
                      };
  
                      if (this.getEventAttr(event, 'touches')) {
                          eventNames.moveEvent = 'touchmove';
                          eventNames.endEvent = 'touchend';
                      } else {
                          eventNames.moveEvent = 'mousemove';
                          eventNames.endEvent = 'mouseup';
                      }
  
                      return eventNames;
                  },
  
                  /**
                   * Get the handle closest to an event.
                   *
                   * @param event {Event} The event
                   * @returns {jqLite} The handle closest to the event.
                   */
                  getNearestHandle: function(event) {
                      if (!this.range) {
                          return this.minH;
                      }
                      var position = this.getEventPosition(event),
                          distanceMin = Math.abs(position - this.minH.rzsp),
                          distanceMax = Math.abs(position - this.maxH.rzsp);
                      if (distanceMin < distanceMax)
                          return this.minH;
                      else if (distanceMin > distanceMax)
                          return this.maxH;
                      else if (!this.options.rightToLeft)
                      //if event is at the same distance from min/max then if it's at left of minH, we return minH else maxH
                          return position < this.minH.rzsp ? this.minH : this.maxH;
                      else
                      //reverse in rtl
                          return position > this.minH.rzsp ? this.minH : this.maxH;
                  },
  
                  /**
                   * Wrapper function to focus an angular element
                   *
                   * @param el {AngularElement} the element to focus
                   */
                  focusElement: function(el) {
                      var DOM_ELEMENT = 0;
                      el[DOM_ELEMENT].focus();
                  },
  
                  /**
                   * Bind mouse and touch events to slider handles
                   *
                   * @returns {undefined}
                   */
                  bindEvents: function() {
                      var barTracking, barStart, barMove;
  
                      if (this.options.draggableRange) {
                          barTracking = 'rzSliderDrag';
                          barStart = this.onDragStart;
                          barMove = this.onDragMove;
                      } else {
                          barTracking = 'lowValue';
                          barStart = this.onStart;
                          barMove = this.onMove;
                      }
  
                      if (!this.options.onlyBindHandles) {
                          this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking));
                          this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar));
                      }
  
                      if (this.options.draggableRangeOnly) {
                          this.minH.on('mousedown', angular.bind(this, barStart, null, barTracking));
                          this.maxH.on('mousedown', angular.bind(this, barStart, null, barTracking));
                      } else {
                          this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'lowValue'));
                          if (this.range) {
                              this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'highValue'));
                          }
                          if (!this.options.onlyBindHandles) {
                              this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null));
                              this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar));
                              this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null));
                              this.ticks.on('mousedown', angular.bind(this, this.onTickClick, this.ticks));
                          }
                      }
  
                      if (!this.options.onlyBindHandles) {
                          this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking));
                          this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar));
                      }
                      if (this.options.draggableRangeOnly) {
                          this.minH.on('touchstart', angular.bind(this, barStart, null, barTracking));
                          this.maxH.on('touchstart', angular.bind(this, barStart, null, barTracking));
                      } else {
                          this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'lowValue'));
                          if (this.range) {
                              this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'highValue'));
                          }
                          if (!this.options.onlyBindHandles) {
                              this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null));
                              this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar));
                              this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null));
                              this.ticks.on('touchstart', angular.bind(this, this.onTickClick, this.ticks));
                          }
                      }
  
                      if (this.options.keyboardSupport) {
                          this.minH.on('focus', angular.bind(this, this.onPointerFocus, this.minH, 'lowValue'));
                          if (this.range) {
                              this.maxH.on('focus', angular.bind(this, this.onPointerFocus, this.maxH, 'highValue'));
                          }
                      }
                  },
  
                  /**
                   * Unbind mouse and touch events to slider handles
                   *
                   * @returns {undefined}
                   */
                  unbindEvents: function() {
                      this.minH.off();
                      this.maxH.off();
                      this.fullBar.off();
                      this.selBar.off();
                      this.ticks.off();
                  },
  
                  /**
                   * onStart event handler
                   *
                   * @param {?Object} pointer The jqLite wrapped DOM element; if null, the closest handle is used
                   * @param {?string} ref     The name of the handle being changed; if null, the closest handle's value is modified
                   * @param {Event}   event   The event
                   * @returns {undefined}
                   */
                  onStart: function(pointer, ref, event) {
                      var ehMove, ehEnd,
                          eventNames = this.getEventNames(event);
  
                      event.stopPropagation();
                      event.preventDefault();
  
                      // We have to do this in case the HTML where the sliders are on
                      // have been animated into view.
                      this.calcViewDimensions();
  
                      if (pointer) {
                          this.tracking = ref;
                      } else {
                          pointer = this.getNearestHandle(event);
                          this.tracking = pointer === this.minH ? 'lowValue' : 'highValue';
                      }
  
                      pointer.addClass('rz-active');
  
                      if (this.options.keyboardSupport)
                          this.focusElement(pointer);
  
                      ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer);
                      ehEnd = angular.bind(this, this.onEnd, ehMove);
  
                      $document.on(eventNames.moveEvent, ehMove)
                      $document.on(eventNames.endEvent, ehEnd);
                      this.endHandlerToBeRemovedOnEnd = ehEnd;
  
                      this.callOnStart();
  
                      var changedTouches = this.getEventAttr(event, 'changedTouches');
                      if (changedTouches) {
                          // Store the touch identifier
                          if (!this.touchId) {
                              this.isDragging = true;
                              this.touchId = changedTouches[0].identifier;
                          }
                      }
                  },
  
                  /**
                   * onMove event handler
                   *
                   * @param {jqLite} pointer
                   * @param {Event}  event The event
                   * @param {boolean}  fromTick if the event occured on a tick or not
                   * @returns {undefined}
                   */
                  onMove: function(pointer, event, fromTick) {
                      var changedTouches = this.getEventAttr(event, 'changedTouches');
                      var touchForThisSlider;
                      if (changedTouches) {
                          for (var i = 0; i < changedTouches.length; i++) {
                              if (changedTouches[i].identifier === this.touchId) {
                                  touchForThisSlider = changedTouches[i];
                                  break;
                              }
                          }
                      }
  
                      if (changedTouches && !touchForThisSlider) {
                          return;
                      }
  
                      var newPos = this.getEventPosition(event, touchForThisSlider ? touchForThisSlider.identifier : undefined),
                          newValue,
                          ceilValue = this.options.rightToLeft ? this.minValue : this.maxValue,
                          flrValue = this.options.rightToLeft ? this.maxValue : this.minValue;
  
                      if (newPos <= 0) {
                          newValue = flrValue;
                      } else if (newPos >= this.maxPos) {
                          newValue = ceilValue;
                      } else {
                          newValue = this.positionToValue(newPos);
                          if (fromTick && angular.isNumber(this.options.showTicks))
                              newValue = this.roundStep(newValue, this.options.showTicks);
                          else
                              newValue = this.roundStep(newValue);
                      }
                      this.positionTrackingHandle(newValue);
                  },
  
                  /**
                   * onEnd event handler
                   *
                   * @param {Event}    event    The event
                   * @param {Function} ehMove   The bound move event handler
                   * @returns {undefined}
                   */
                  onEnd: function(ehMove, event) {
                      var changedTouches = this.getEventAttr(event, 'changedTouches');
                      if (changedTouches && changedTouches[0].identifier !== this.touchId) {
                          return;
                      }
                      this.isDragging = false;
                      this.touchId = null;
  
                      if (!this.options.keyboardSupport) {
                          this.minH.removeClass('rz-active');
                          this.maxH.removeClass('rz-active');
                          this.tracking = '';
                      }
                      this.dragging.active = false;
  
                      var eventName = this.getEventNames(event);
                      $document.off(eventName.moveEvent, ehMove);
                      $document.off(eventName.endEvent, this.endHandlerToBeRemovedOnEnd);
                      this.endHandlerToBeRemovedOnEnd = null;
                      this.callOnEnd();
                  },
  
                  onTickClick: function(pointer, event) {
                      this.onMove(pointer, event, true);
                  },
  
                  onPointerFocus: function(pointer, ref) {
                      this.tracking = ref;
                      pointer.one('blur', angular.bind(this, this.onPointerBlur, pointer));
                      pointer.on('keydown', angular.bind(this, this.onKeyboardEvent));
                      pointer.on('keyup', angular.bind(this, this.onKeyUp));
                      this.firstKeyDown = true;
                      pointer.addClass('rz-active');
  
                      this.currentFocusElement = {
                          pointer: pointer,
                          ref: ref
                      };
                  },
  
                  onKeyUp: function() {
                      this.firstKeyDown = true;
                      this.callOnEnd();
                  },
  
                  onPointerBlur: function(pointer) {
                      pointer.off('keydown');
                      pointer.off('keyup');
                      pointer.removeClass('rz-active');
                      if (!this.isDragging) {
                          this.tracking = '';
                          this.currentFocusElement = null
                      }
                  },
  
                  /**
                   * Key actions helper function
                   *
                   * @param {number} currentValue value of the slider
                   *
                   * @returns {?Object} action value mappings
                   */
                  getKeyActions: function(currentValue) {
                      var increaseStep = currentValue + this.step,
                          decreaseStep = currentValue - this.step,
                          increasePage = currentValue + this.valueRange / 10,
                          decreasePage = currentValue - this.valueRange / 10;
  
                      //Left to right default actions
                      var actions = {
                          'UP': increaseStep,
                          'DOWN': decreaseStep,
                          'LEFT': decreaseStep,
                          'RIGHT': increaseStep,
                          'PAGEUP': increasePage,
                          'PAGEDOWN': decreasePage,
                          'HOME': this.minValue,
                          'END': this.maxValue
                      };
                      //right to left means swapping right and left arrows
                      if (this.options.rightToLeft) {
                          actions.LEFT = increaseStep;
                          actions.RIGHT = decreaseStep;
                          // right to left and vertical means we also swap up and down
                          if (this.options.vertical) {
                              actions.UP = decreaseStep;
                              actions.DOWN = increaseStep;
                          }
                      }
                      return actions;
                  },
  
                  onKeyboardEvent: function(event) {
                      var currentValue = this[this.tracking],
                          keyCode = event.keyCode || event.which,
                          keys = {
                              38: 'UP',
                              40: 'DOWN',
                              37: 'LEFT',
                              39: 'RIGHT',
                              33: 'PAGEUP',
                              34: 'PAGEDOWN',
                              36: 'HOME',
                              35: 'END'
                          },
                          actions = this.getKeyActions(currentValue),
                          key = keys[keyCode],
                          action = actions[key];
                      if (action == null || this.tracking === '') return;
                      event.preventDefault();
  
                      if (this.firstKeyDown) {
                          this.firstKeyDown = false;
                          this.callOnStart();
                      }
  
                      var self = this;
                      $timeout(function() {
                          var newValue = self.roundStep(self.sanitizeValue(action));
                          if (!self.options.draggableRangeOnly) {
                              self.positionTrackingHandle(newValue);
                          }
                          else {
                              var difference = self.highValue - self.lowValue,
                                  newMinValue, newMaxValue;
                              if (self.tracking === 'lowValue') {
                                  newMinValue = newValue;
                                  newMaxValue = newValue + difference;
                                  if (newMaxValue > self.maxValue) {
                                      newMaxValue = self.maxValue;
                                      newMinValue = newMaxValue - difference;
                                  }
                              } else {
                                  newMaxValue = newValue;
                                  newMinValue = newValue - difference;
                                  if (newMinValue < self.minValue) {
                                      newMinValue = self.minValue;
                                      newMaxValue = newMinValue + difference;
                                  }
                              }
                              self.positionTrackingBar(newMinValue, newMaxValue);
                          }
                      });
                  },
  
                  /**
                   * onDragStart event handler
                   *
                   * Handles dragging of the middle bar.
                   *
                   * @param {Object} pointer The jqLite wrapped DOM element
                   * @param {string} ref     One of the refLow, refHigh values
                   * @param {Event}  event   The event
                   * @returns {undefined}
                   */
                  onDragStart: function(pointer, ref, event) {
                      var position = this.getEventPosition(event);
                      this.dragging = {
                          active: true,
                          value: this.positionToValue(position),
                          difference: this.highValue - this.lowValue,
                          lowLimit: this.options.rightToLeft ? this.minH.rzsp - position : position - this.minH.rzsp,
                          highLimit: this.options.rightToLeft ? position - this.maxH.rzsp : this.maxH.rzsp - position
                      };
  
                      this.onStart(pointer, ref, event);
                  },
  
                  /**
                   * getValue helper function
                   *
                   * gets max or min value depending on whether the newPos is outOfBounds above or below the bar and rightToLeft
                   *
                   * @param {string} type 'max' || 'min' The value we are calculating
                   * @param {number} newPos  The new position
                   * @param {boolean} outOfBounds Is the new position above or below the max/min?
                   * @param {boolean} isAbove Is the new position above the bar if out of bounds?
                   *
                   * @returns {number}
                   */
                  getValue: function(type, newPos, outOfBounds, isAbove) {
                      var isRTL = this.options.rightToLeft,
                          value = null;
  
                      if (type === 'min') {
                          if (outOfBounds) {
                              if (isAbove) {
                                  value = isRTL ? this.minValue : this.maxValue - this.dragging.difference;
                              } else {
                                  value = isRTL ? this.maxValue - this.dragging.difference : this.minValue;
                              }
                          } else {
                              value = isRTL ? this.positionToValue(newPos + this.dragging.lowLimit) : this.positionToValue(newPos - this.dragging.lowLimit)
                          }
                      } else {
                          if (outOfBounds) {
                              if (isAbove) {
                                  value = isRTL ? this.minValue + this.dragging.difference : this.maxValue;
                              } else {
                                  value = isRTL ? this.maxValue : this.minValue + this.dragging.difference;
                              }
                          } else {
                              if (isRTL) {
                                  value = this.positionToValue(newPos + this.dragging.lowLimit) + this.dragging.difference
                              } else {
                                  value = this.positionToValue(newPos - this.dragging.lowLimit) + this.dragging.difference;
                              }
                          }
                      }
                      return this.roundStep(value);
                  },
  
                  /**
                   * onDragMove event handler
                   *
                   * Handles dragging of the middle bar.
                   *
                   * @param {jqLite} pointer
                   * @param {Event}  event The event
                   * @returns {undefined}
                   */
                  onDragMove: function(pointer, event) {
                      var newPos = this.getEventPosition(event),
                          newMinValue, newMaxValue,
                          ceilLimit, flrLimit,
                          isUnderFlrLimit, isOverCeilLimit,
                          flrH, ceilH;
  
                      if (this.options.rightToLeft) {
                          ceilLimit = this.dragging.lowLimit;
                          flrLimit = this.dragging.highLimit;
                          flrH = this.maxH;
                          ceilH = this.minH;
                      } else {
                          ceilLimit = this.dragging.highLimit;
                          flrLimit = this.dragging.lowLimit;
                          flrH = this.minH;
                          ceilH = this.maxH;
                      }
                      isUnderFlrLimit = newPos <= flrLimit;
                      isOverCeilLimit = newPos >= this.maxPos - ceilLimit;
  
                      if (isUnderFlrLimit) {
                          if (flrH.rzsp === 0)
                              return;
                          newMinValue = this.getValue('min', newPos, true, false);
                          newMaxValue = this.getValue('max', newPos, true, false);
                      } else if (isOverCeilLimit) {
                          if (ceilH.rzsp === this.maxPos)
                              return;
                          newMaxValue = this.getValue('max', newPos, true, true);
                          newMinValue = this.getValue('min', newPos, true, true);
                      } else {
                          newMinValue = this.getValue('min', newPos, false);
                          newMaxValue = this.getValue('max', newPos, false);
                      }
                      this.positionTrackingBar(newMinValue, newMaxValue);
                  },
  
                  /**
                   * Set the new value and position for the entire bar
                   *
                   * @param {number} newMinValue   the new minimum value
                   * @param {number} newMaxValue   the new maximum value
                   */
                  positionTrackingBar: function(newMinValue, newMaxValue) {
  
                      if (this.options.minLimit != null && newMinValue < this.options.minLimit) {
                          newMinValue = this.options.minLimit;
                          newMaxValue = newMinValue + this.dragging.difference;
                      }
                      if (this.options.maxLimit != null && newMaxValue > this.options.maxLimit) {
                          newMaxValue = this.options.maxLimit;
                          newMinValue = newMaxValue - this.dragging.difference;
                      }
  
                      this.lowValue = newMinValue;
                      this.highValue = newMaxValue;
                      this.applyLowValue();
                      if (this.range)
                          this.applyHighValue();
                      this.applyModel(true);
                      this.updateHandles('lowValue', this.valueToPosition(newMinValue));
                      this.updateHandles('highValue', this.valueToPosition(newMaxValue));
                  },
  
                  /**
                   * Set the new value and position to the current tracking handle
                   *
                   * @param {number} newValue new model value
                   */
                  positionTrackingHandle: function(newValue) {
                      var valueChanged = false;
                      newValue = this.applyMinMaxLimit(newValue);
                      if (this.range) {
                          if (this.options.pushRange) {
                              newValue = this.applyPushRange(newValue);
                              valueChanged = true;
                          }
                          else {
                              if (this.options.noSwitching) {
                                  if (this.tracking === 'lowValue' && newValue > this.highValue)
                                      newValue = this.applyMinMaxRange(this.highValue);
                                  else if (this.tracking === 'highValue' && newValue < this.lowValue)
                                      newValue = this.applyMinMaxRange(this.lowValue);
                              }
                              newValue = this.applyMinMaxRange(newValue);
                              /* This is to check if we need to switch the min and max handles */
                              if (this.tracking === 'lowValue' && newValue > this.highValue) {
                                  this.lowValue = this.highValue;
                                  this.applyLowValue();
                                  this.applyModel();
                                  this.updateHandles(this.tracking, this.maxH.rzsp);
                                  this.updateAriaAttributes();
                                  this.tracking = 'highValue';
                                  this.minH.removeClass('rz-active');
                                  this.maxH.addClass('rz-active');
                                  if (this.options.keyboardSupport)
                                      this.focusElement(this.maxH);
                                  valueChanged = true;
                              }
                              else if (this.tracking === 'highValue' && newValue < this.lowValue) {
                                  this.highValue = this.lowValue;
                                  this.applyHighValue();
                                  this.applyModel();
                                  this.updateHandles(this.tracking, this.minH.rzsp);
                                  this.updateAriaAttributes();
                                  this.tracking = 'lowValue';
                                  this.maxH.removeClass('rz-active');
                                  this.minH.addClass('rz-active');
                                  if (this.options.keyboardSupport)
                                      this.focusElement(this.minH);
                                  valueChanged = true;
                              }
                          }
                      }
  
                      if (this[this.tracking] !== newValue) {
                          this[this.tracking] = newValue;
                          if (this.tracking === 'lowValue')
                              this.applyLowValue();
                          else
                              this.applyHighValue();
                          this.applyModel();
                          this.updateHandles(this.tracking, this.valueToPosition(newValue));
                          this.updateAriaAttributes();
                          valueChanged = true;
                      }
  
                      if (valueChanged)
                          this.applyModel(true);
                  },
  
                  applyMinMaxLimit: function(newValue) {
                      if (this.options.minLimit != null && newValue < this.options.minLimit)
                          return this.options.minLimit;
                      if (this.options.maxLimit != null && newValue > this.options.maxLimit)
                          return this.options.maxLimit;
                      return newValue;
                  },
  
                  applyMinMaxRange: function(newValue) {
                      var oppositeValue = this.tracking === 'lowValue' ? this.highValue : this.lowValue,
                          difference = Math.abs(newValue - oppositeValue);
                      if (this.options.minRange != null) {
                          if (difference < this.options.minRange) {
                              if (this.tracking === 'lowValue')
                                  return this.highValue - this.options.minRange;
                              else
                                  return this.lowValue + this.options.minRange;
                          }
                      }
                      if (this.options.maxRange != null) {
                          if (difference > this.options.maxRange) {
                              if (this.tracking === 'lowValue')
                                  return this.highValue - this.options.maxRange;
                              else
                                  return this.lowValue + this.options.maxRange;
                          }
                      }
                      return newValue;
                  },
  
                  applyPushRange: function(newValue) {
                      var difference = this.tracking === 'lowValue' ? this.highValue - newValue : newValue - this.lowValue,
                          minRange = this.options.minRange !== null ? this.options.minRange : this.options.step,
                          maxRange = this.options.maxRange;
                      // if smaller than minRange
                      if (difference < minRange) {
                          if (this.tracking === 'lowValue') {
                              this.highValue = Math.min(newValue + minRange, this.maxValue);
                              newValue = this.highValue - minRange;
                              this.applyHighValue();
                              this.updateHandles('highValue', this.valueToPosition(this.highValue));
                          }
                          else {
                              this.lowValue = Math.max(newValue - minRange, this.minValue);
                              newValue = this.lowValue + minRange;
                              this.applyLowValue();
                              this.updateHandles('lowValue', this.valueToPosition(this.lowValue));
                          }
                          this.updateAriaAttributes();
                      }
                      // if greater than maxRange
                      else if (maxRange !== null && difference > maxRange) {
                          if (this.tracking === 'lowValue') {
                              this.highValue = newValue + maxRange;
                              this.applyHighValue();
                              this.updateHandles('highValue', this.valueToPosition(this.highValue));
                          }
                          else {
                              this.lowValue = newValue - maxRange;
                              this.applyLowValue();
                              this.updateHandles('lowValue', this.valueToPosition(this.lowValue));
                          }
                          this.updateAriaAttributes();
                      }
                      return newValue;
                  },
  
                  /**
                   * Apply the model values using scope.$apply.
                   * We wrap it with the internalChange flag to avoid the watchers to be called
                   */
                  applyModel: function(callOnChange) {
                      this.internalChange = true;
                      this.scope.$apply();
                      callOnChange && this.callOnChange();
                      this.internalChange = false;
                  },
  
                  /**
                   * Call the onStart callback if defined
                   * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope.
                   *
                   * @returns {undefined}
                   */
                  callOnStart: function() {
                      if (this.options.onStart) {
                          var self = this,
                              pointerType = this.tracking === 'lowValue' ? 'min' : 'max';
                          this.scope.$evalAsync(function() {
                              self.options.onStart(self.options.id, self.scope.rzSliderModel, self.scope.rzSliderHigh, pointerType);
                          });
                      }
                  },
  
                  /**
                   * Call the onChange callback if defined
                   * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope.
                   *
                   * @returns {undefined}
                   */
                  callOnChange: function() {
                      if (this.options.onChange) {
                          var self = this,
                              pointerType = this.tracking === 'lowValue' ? 'min' : 'max';
                          this.scope.$evalAsync(function() {
                              self.options.onChange(self.options.id, self.scope.rzSliderModel, self.scope.rzSliderHigh, pointerType);
                          });
                      }
                  },
  
                  /**
                   * Call the onEnd callback if defined
                   * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope.
                   *
                   * @returns {undefined}
                   */
                  callOnEnd: function() {
                      if (this.options.onEnd) {
                          var self = this,
                              pointerType = this.tracking === 'lowValue' ? 'min' : 'max';
                          this.scope.$evalAsync(function() {
                              self.options.onEnd(self.options.id, self.scope.rzSliderModel, self.scope.rzSliderHigh, pointerType);
                          });
                      }
                      this.scope.$emit('slideEnded');
                  }
              };
  
              return Slider;
          }])
  
          .directive('rzslider', ['RzSlider', function(RzSlider) {
              'use strict';
  
              return {
                  restrict: 'AE',
                  replace: true,
                  scope: {
                      rzSliderModel: '=?',
                      rzSliderHigh: '=?',
                      rzSliderOptions: '&?',
                      rzSliderTplUrl: '@'
                  },
  
                  /**
                   * Return template URL
                   *
                   * @param {jqLite} elem
                   * @param {Object} attrs
                   * @return {string}
                   */
                  templateUrl: function(elem, attrs) {
                      //noinspection JSUnresolvedVariable
                      return attrs.rzSliderTplUrl || 'rzSliderTpl.html';
                  },
  
                  link: function(scope, elem) {
                      scope.slider = new RzSlider(scope, elem); //attach on scope so we can test it
                  }
              };
          }]);
  
      // IDE assist
  
      /**
       * @name ngScope
       *
       * @property {number} rzSliderModel
       * @property {number} rzSliderHigh
       * @property {Object} rzSliderOptions
       */
  
      /**
       * @name jqLite
       *
       * @property {number|undefined} rzsp rzslider label position position
       * @property {number|undefined} rzsd rzslider element dimension
       * @property {string|undefined} rzsv rzslider label value/text
       * @property {Function} css
       * @property {Function} text
       */
  
      /**
       * @name Event
       * @property {Array} touches
       * @property {Event} originalEvent
       */
  
      /**
       * @name ThrottleOptions
       *
       * @property {boolean} leading
       * @property {boolean} trailing
       */
  
      module.run(['$templateCache', function($templateCache) {
          'use strict';
  
          $templateCache.put('rzSliderTpl.html',
              "<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>"
          );
  
      }]);
  
      return module.name
  }));