From 39ef89e78fdda704c1967d4e59adfedb14824b10 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Fri, 23 Oct 2015 13:21:41 +0200 Subject: [PATCH 01/15] Refactor the slider architecture. Add a rzSliderOptions that handle all the options except model, high and rzSliderTplUrl --- demo/demo.css | 1 + demo/demo.js | 161 +++ demo/index.html | 363 ++---- dist/rzslider.js | 2750 ++++++++++++++++++++--------------------- dist/rzslider.min.css | 2 +- dist/rzslider.min.js | 4 +- src/rzslider.js | 2750 ++++++++++++++++++++--------------------- 7 files changed, 2938 insertions(+), 3093 deletions(-) create mode 100644 demo/demo.js diff --git a/demo/demo.css b/demo/demo.css index 5f2aab4..7c3752a 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -2,6 +2,7 @@ body { font-family: 'Open Sans', sans-serif; color: #1f2636; font-size: 14px; } header { background: #0db9f0; color: #fff; margin: -40px; margin-bottom: 40px; text-align: center; padding: 40px 0; } h1 { font-weight: 300; } +h2 {margin-bottom:10px;} .wrapper { background: #fff; padding: 40px; } article { margin-bottom: 40px; } diff --git a/demo/demo.js b/demo/demo.js new file mode 100644 index 0000000..9f3011f --- /dev/null +++ b/demo/demo.js @@ -0,0 +1,161 @@ +var app = angular.module('rzSliderDemo', ['rzModule']); + +app.controller('MainCtrl', function($scope, $timeout) { + //Minimal slider config + $scope.minSlider = { + value: 10 + }; + + //Slider with selection bar + $scope.slider_visible_bar = { + value: 10, + options: { + showSelectionBar: true + } + }; + + //Range slider config + $scope.minRangeSlider = { + minValue: 10, + maxValue: 90, + options: { + floor: 0, + ceil: 100, + step: 1 + } + }; + + //Slider config with floor, ceil and step + $scope.slider_floor_ceil = { + value: 12, + options: { + floor: 10, + ceil: 100, + step: 5 + } + }; + + //Slider config with callbacks + $scope.slider_callbacks = { + value: 100, + options: { + onStart: function() { + $scope.otherData.start = $scope.slider_callbacks.value * 10; + }, + onChange: function() { + $scope.otherData.change = $scope.slider_callbacks.value * 10; + }, + onEnd: function() { + $scope.otherData.end = $scope.slider_callbacks.value * 10; + } + } + }; + $scope.otherData = {start: 0, change: 0, end: 0}; + + //Slider config with custom display function + $scope.slider_translate = { + minValue: 100, + maxValue: 400, + options: { + ceil: 500, + floor: 0, + translate: function(value) { + return '$' + value; + } + } + }; + + //Slider config with custom display function displaying letters + var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $scope.slider_alphabet = { + value: 0, + options: { + ceil: alphabet.length - 1, + floor: 0, + translate: function(value) { + if (value >= 0 && value < alphabet.length) + return alphabet[value]; + return ''; + } + } + }; + + //Slider with ticks + $scope.slider_ticks = { + value: 5, + options: { + ceil: 10, + floor: 0, + showTicks: true + } + }; + + //Slider with ticks and values + $scope.slider_ticks_values = { + value: 5, + options: { + ceil: 10, + floor: 0, + showTicksValues: true + } + }; + + //Range slider with ticks and values + $scope.range_slider_ticks_values = { + minValue: 1, + maxValue: 8, + options: { + ceil: 10, + floor: 0, + showTicksValues: true + } + }; + + //Slider with draggable range + $scope.slider_draggable_range = { + minValue: 1, + maxValue: 8, + options: { + ceil: 10, + floor: 0, + draggableRange: true + } + }; + + //Read-only slider + $scope.read_only_slider = { + value: 50, + options: { + ceil: 100, + floor: 0, + readOnly: true + } + }; + + //Disabled slider + $scope.disabled_slider = { + value: 50, + options: { + ceil: 100, + floor: 0, + disabled: true + } + }; + + $scope.visible = false; + + $scope.toggle = function() { + $scope.visible = !$scope.visible; + $timeout(function() { + $scope.$broadcast('rzSliderForceRender'); + }); + }; + + $scope.slider_toggle = { + value: 5, + options: { + ceil: 10, + floor: 0 + } + }; +}); diff --git a/demo/index.html b/demo/index.html index a2337ef..71c7e9d 100644 --- a/demo/index.html +++ b/demo/index.html @@ -2,240 +2,151 @@ - - - AngularJS Touch Slider - - - + + + AngularJS Touch Slider + + +
-
-

AngularJS Touch Slider

-
- -
-

Min/max slider example

- Value: -
{{ slider_data | json }}
-

Value linked on change: {{ otherData.value }}

- - -
- -
-

Min/max slider example

- Value: -
{{ priceSlider | json }}
- -
-
- - -
- -
-

Currency slider example

- - Value: {{ priceSlider2 | json }} - -
- -
-

One value slider example

- - Value: {{ priceSlider3 | json }} - -
- -
-

Alphabet slider example

- Value: {{ alphabetTranslate(letter) }} - -
- -
-

Slider with ticks example

- Value: {{ priceSlider4 | json }} - -
- -
-

Slider with ticks value example

- Value: {{ priceSlider5 | json }} - -
- -
-

Slider with ticks value and visible bar example

- Value: {{ priceSlider6 | json }} - -
- -
-

Range Slider with ticks value example

- Value: {{ priceSlider7 | json }} - -
- -
-

Draggable range example

- Value: -
{{ priceSlider | json }}
- -
-
- - -
- -
-

Toggle slider example

- -
- -
-
- -
-

Disabled slider example

- - -
+
+

AngularJS Touch Slider

+
+ +
+

Simple slider

+ Model:
+ +
+ +
+

Range slider

+ Min Value:
+ Max Value:
+ +
+ +
+

Slider with visible selection bar

+ +
+ +
+

Slider with custom floor/ceil/step

+ +
+ +
+

Slider with callbacks on start, change and end

+

Value linked on start: {{ otherData.start }}

+

Value linked on change: {{ otherData.change }}

+

Value linked on end: {{ otherData.end }}

+ + +
+ +
+

Slider with custom display function

+ +
+ +
+

Slider with Alphabet

+ +
+ +
+

Slider with ticks

+ +
+ +
+

Slider with ticks and values

+ +
+ +
+

Range slider with ticks and values

+ +
+ +
+

Slider with draggable range

+ +
+ +
+

Disabled slider

+ + +
+ +
+

Read-only slider

+ + +
+ +
+

Toggle slider example

+ +
+
+ +
+
-
+ + - - - - - + diff --git a/dist/rzslider.js b/dist/rzslider.js index 21151b2..89194ff 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -11,7 +11,7 @@ /*jslint unparam: true */ /*global angular: false, console: false, define, module */ -(function (root, factory) { +(function(root, factory) { 'use strict'; if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. @@ -27,1458 +27,1344 @@ factory(root.angular); } -}(this, function (angular) { +}(this, function(angular) { 'use strict'; -var module = angular.module('rzModule', []) - -.value('throttle', - /** - * throttle - * - * Taken from underscore project - * - * @param {Function} func - * @param {number} wait - * @param {ThrottleOptions} options - * @returns {Function} - */ -function throttle(func, wait, options) { - 'use strict'; - 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 = options.leading === false ? 0 : getTime(); - timeout = null; - result = func.apply(context, args); - context = args = null; - }; - return function() { - var now = getTime(); - if (!previous && options.leading === false) { previous = now; } - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - context = args = null; - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); - } - return result; - }; -}) - -.factory('RzSlider', ['$timeout', '$document', '$window', 'throttle', function($timeout, $document, $window, throttle) -{ - 'use strict'; - - /** - * Slider - * - * @param {ngScope} scope The AngularJS scope - * @param {Element} sliderElem The slider directive element wrapped in jqLite - * @param {*} attributes The slider directive attributes - * @constructor - */ - var Slider = function(scope, sliderElem, attributes) - { - /** - * The slider's scope - * - * @type {ngScope} - */ - this.scope = scope; - - /** - * The slider attributes - * - * @type {Object} - */ - this.attributes = attributes; - - /** - * Slider element wrapped in jqLite - * - * @type {jqLite} - */ - this.sliderElem = sliderElem; - - /** - * Slider type - * - * @type {boolean} Set to true for range slider - */ - this.range = attributes.rzSliderHigh !== undefined && attributes.rzSliderModel !== undefined; - - /** - * Whether to allow draggable range - * - * @type {boolean} Set to true for draggable range slider - */ - this.dragRange = this.range && attributes.rzSliderDraggableRange === 'true'; - - /** - * Values recorded when first dragging the bar - * - * @type {Object} - */ - this.dragging = { - active: false, - value: 0, - difference: 0, - offset: 0, - lowDist: 0, - highDist: 0 - }; - - /** - * Half of the width of the slider handles - * - * @type {number} - */ - this.handleHalfWidth = 0; - - /** - * Always show selection bar - * - * @type {boolean} - */ - this.alwaysShowBar = !!attributes.rzSliderAlwaysShowBar; - - /** - * Maximum left the slider handle can have - * - * @type {number} - */ - this.maxLeft = 0; - - /** - * Precision - * - * @type {number} - */ - this.precision = 0; - - /** - * Step - * - * @type {number} - */ - this.step = 0; - - /** - * 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; - - /** - * Hide limit labels - * - * @type {boolean} - */ - this.hideLimitLabels = !!attributes.rzSliderHideLimitLabels; - - /** - * Only present model values - * - * Do not allow to change values - * - * @type {boolean} - */ - this.presentOnly = attributes.rzSliderPresentOnly === 'true'; - - /** - * Display ticks on each possible value. - * - * @type {boolean} - */ - this.showTicks = attributes.rzSliderShowTicks || attributes.rzSliderShowTicksValue; - - /** - * Display the value on each tick. - * - * @type {boolean} - */ - this.showTicksValue = attributes.rzSliderShowTicksValue; - - /** - * Disable the slider - * - * @type {boolean} - */ - this.disabled = this.scope.rzSliderDisabled; - - /** - * The interval at which the slider updates when the model/high values - * are altered from outside the slider - * - * @type {number} - */ - this.interval = this.scope.rzSliderInterval !== null ? this.scope.rzSliderInterval : 350; - - /** - * The delta between min and max value - * - * @type {number} - */ - this.valueRange = 0; - - /** - * Set to true if init method already executed - * - * @type {boolean} - */ - this.initHasRun = false; - - /** - * Custom translate function - * - * @type {function} - */ - this.customTrFn = this.scope.rzSliderTranslate() || function(value) { return String(value); }; - - /** - * Array of de-registration functions to call on $destroy - * - * @type {Array.} - */ - this.deRegFuncs = []; - - // 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, unRegFn, - calcDimFn = angular.bind(this, this.calcViewDimensions), - self = this; - - this.initElemHandles(); - this.addAccessibility(); - this.setDisabledState(); - this.calcViewDimensions(); - this.setMinAndMax(); - - this.precision = this.scope.rzSliderPrecision === undefined ? 0 : +this.scope.rzSliderPrecision; - this.step = this.scope.rzSliderStep === undefined ? 1 : +this.scope.rzSliderStep; - - $timeout(function() - { - self.updateCeilLab(); - self.updateFloorLab(); - self.initHandles(); - self.bindEvents(); + var module = angular.module('rzModule', []) + + .value('defaultOptions', { + floor: 0, + ceil: null, //defaults to rz-slider-model + step: 1, + precision: 0, + translate: null, + draggableRange: false, + showSelectionBar: false, + hideLimitLabels: false, + readOnly: false, + disabled: false, + interval: 350, + showTicks: false, + showTicksValues: false, + onStart: null, + onChange: null, + onEnd: null + }) + + .value('throttle', + /** + * throttle + * + * Taken from underscore project + * + * @param {Function} func + * @param {number} wait + * @param {ThrottleOptions} options + * @returns {Function} + */ + function throttle(func, wait, options) { + 'use strict'; + var getTime = (Date.now || function() { + return new Date().getTime(); }); - - // Recalculate slider view dimensions - unRegFn = this.scope.$on('reCalcViewDimensions', calcDimFn); - this.deRegFuncs.push(unRegFn); - - // Recalculate stuff if view port dimensions have changed - angular.element($window).on('resize', calcDimFn); - - this.initHasRun = true; - - // Watch for changes to the model - - thrLow = throttle(function() - { - self.setMinAndMax(); - self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); - self.updateSelectionBar(); - self.updateTicksScale(); - - if(self.range) - { - self.updateCmbLabel(); + var context, args, result; + var timeout = null; + var previous = 0; + options = options || {}; + var later = function() { + previous = options.leading === false ? 0 : getTime(); + timeout = null; + result = func.apply(context, args); + context = args = null; + }; + return function() { + var now = getTime(); + if (!previous && options.leading === false) { + previous = now; } - - }, self.interval); - - thrHigh = throttle(function() - { - self.setMinAndMax(); - self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); - self.updateSelectionBar(); - self.updateTicksScale(); - self.updateCmbLabel(); - }, self.interval); - - this.scope.$on('rzSliderForceRender', function() - { - self.resetLabelsValue(); - thrLow(); - if(self.range) { thrHigh(); } - self.resetSlider(); - }); - - // Watchers - - unRegFn = this.scope.$watch('rzSliderModel', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - thrLow(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderHigh', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - thrHigh(); - }); - this.deRegFuncs.push(unRegFn); - - this.scope.$watch('rzSliderFloor', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderCeil', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderShowTicks', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderShowTicksValue', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderDisabled', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - if(self.disabled) - self.unbindEvents(); - else - self.bindEvents(); - }); - this.deRegFuncs.push(unRegFn); - - this.scope.$on('$destroy', function() - { - self.unbindEvents(); - angular.element($window).off('resize', calcDimFn); - self.deRegFuncs.map(function(unbind) { unbind(); }); - }); - }, - - /** - * Resets slider - * - * @returns {undefined} - */ - resetSlider: function() - { - this.setMinAndMax(); - this.updateCeilLab(); - this.updateFloorLab(); - this.setDisabledState(); - this.calcViewDimensions(); - }, - - /** - * Set the disabled state based on rzSliderDisabled - * - * @returns {undefined} - */ - setDisabledState: function() - { - this.disabled = this.scope.rzSliderDisabled; - if(this.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.valueToOffset(this.scope.rzSliderModel)); - - /* - 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.valueToOffset(this.scope.rzSliderHigh)); - this.updateSelectionBar(); - if(this.range) - this.updateCmbLabel(); - - this.updateTicksScale(); - }, - - /** - * Translate value to human readable format - * - * @param {number|string} value - * @param {jqLite} label - * @param {boolean} [useCustomTr] - * @returns {undefined} - */ - translateFn: function(value, label, useCustomTr) - { - useCustomTr = useCustomTr === undefined ? true : useCustomTr; - - var valStr = (useCustomTr ? this.customTrFn(value) : value).toString(), - getWidth = false; - - if(label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) - { - getWidth = true; - label.rzsv = valStr; - } - - label.text(valStr); - - // Update width only when length of the label have changed - if(getWidth) { this.getWidth(label); } - }, - - /** - * Set maximum and minimum values for the slider - * - * @returns {undefined} - */ - setMinAndMax: function() - { - if(this.scope.rzSliderFloor) - { - this.minValue = +this.scope.rzSliderFloor; - } - else - { - this.minValue = this.scope.rzSliderFloor = 0; - } - - if(this.scope.rzSliderCeil) - { - this.maxValue = +this.scope.rzSliderCeil; - } - else - { - this.maxValue = this.scope.rzSliderCeil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; - } - - if(this.scope.rzSliderStep) - { - this.step = +this.scope.rzSliderStep; - } - - this.valueRange = this.maxValue - this.minValue; - }, - - /** - * 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; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); } + return result; + }; + }) - }, this); - - // Initialize offset cache properties - this.selBar.rzsl = 0; - this.minH.rzsl = 0; - this.maxH.rzsl = 0; - this.flrLab.rzsl = 0; - this.ceilLab.rzsl = 0; - this.minLab.rzsl = 0; - this.maxLab.rzsl = 0; - this.cmbLab.rzsl = 0; - - // Hide limit labels - if(this.hideLimitLabels) - { - this.flrLab.rzAlwaysHide = true; - this.ceilLab.rzAlwaysHide = true; - this.hideEl(this.flrLab); - this.hideEl(this.ceilLab); - } - - if(this.showTicksValue) { - this.flrLab.rzAlwaysHide = true; - this.ceilLab.rzAlwaysHide = true; - this.minLab.rzAlwaysHide = true; - this.maxLab.rzAlwaysHide = true; - this.cmbLab.rzAlwaysHide = true; - this.hideEl(this.flrLab); - this.hideEl(this.ceilLab); - this.hideEl(this.minLab); - this.hideEl(this.maxLab); - this.hideEl(this.cmbLab); - } - - // Remove stuff not needed in single slider - if(this.range === false) - { - this.cmbLab.remove(); - this.maxLab.remove(); - - // Hide max handle - this.maxH.rzAlwaysHide = true; - this.maxH[0].style.zIndex = '-1000'; - this.hideEl(this.maxH); - } - - // Show selection bar for single slider or not - if(this.range === false && this.alwaysShowBar === false) - { - this.maxH.remove(); - this.selBar.remove(); - } - - // If using draggable range, use appropriate cursor for this.selBar. - if (this.dragRange) - { - this.selBar.css('cursor', 'move'); - this.selBar.addClass('rz-draggable'); - } - }, - - /** - * Adds accessibility atributes - * - * Run only once during initialization - * - * @returns {undefined} - */ - addAccessibility: function () - { - this.sliderElem.attr("role", "slider"); - }, - - /** - * 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.getWidth(this.minH); - - this.handleHalfWidth = handleWidth / 2; - this.barWidth = this.getWidth(this.fullBar); - - this.maxLeft = this.barWidth - handleWidth; - - this.getWidth(this.sliderElem); - this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left; - - if(this.initHasRun) - { - this.updateFloorLab(); - this.updateCeilLab(); - this.initHandles(); - } - }, - - /** - * Update the ticks position - * - * @returns {undefined} - */ - updateTicksScale: function() { - if(!this.showTicks) return; - if(!this.step) return; //if step is 0, the following loop will be endless. - - var positions = '', - ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1; - for (var i = 0; i < ticksCount; i++) { - var value = this.roundStep(this.minValue + i * this.step); - var selectedClass = this.isTickSelected(value) ? 'selected': ''; - positions += '
  • '; - if(this.showTicksValue) - positions += ''+ this.getDisplayValue(value) +''; - positions += '
  • '; - } - this.ticks.html(positions); - }, - - isTickSelected: function(value) { - var tickLeft = this.valueToOffset(value); - if(!this.range && this.alwaysShowBar && value <= this.scope.rzSliderModel) - return true; - if(this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh) - return true; - return false; - }, - - /** - * Update position of the ceiling label - * - * @returns {undefined} - */ - updateCeilLab: function() - { - this.translateFn(this.scope.rzSliderCeil, this.ceilLab); - this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw); - this.getWidth(this.ceilLab); - }, - - /** - * Update position of the floor label - * - * @returns {undefined} - */ - updateFloorLab: function() - { - this.translateFn(this.scope.rzSliderFloor, this.flrLab); - this.getWidth(this.flrLab); - }, - - /** - * Call the onStart callback if defined - * - * @returns {undefined} - */ - callOnStart: function() { - if(this.scope.rzSliderOnStart) { - var self = this; - $timeout(function() { - self.scope.rzSliderOnStart(); - }); - } - }, - - /** - * Call the onChange callback if defined - * - * @returns {undefined} - */ - callOnChange: function() { - if(this.scope.rzSliderOnChange) { - var self = this; - $timeout(function() { - self.scope.rzSliderOnChange(); - }); - } - }, - - /** - * Call the onEnd callback if defined - * - * @returns {undefined} - */ - callOnEnd: function() { - if(this.scope.rzSliderOnEnd) { - var self = this; - $timeout(function() { - self.scope.rzSliderOnEnd(); - }); - } - }, - - /** - * Update slider handles and label positions - * - * @param {string} which - * @param {number} newOffset - */ - updateHandles: function(which, newOffset) - { - if(which === 'rzSliderModel') - { - this.updateLowHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); - - if(this.range) - { - this.updateCmbLabel(); - } - return; - } + .factory('RzSlider', ['$timeout', '$document', '$window', 'defaultOptions', 'throttle', function($timeout, $document, $window, defaultOptions, throttle) { + 'use strict'; - if(which === 'rzSliderHigh') - { - this.updateHighHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); + /** + * 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; + + /** + * 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, + offset: 0, + lowDist: 0, + highDist: 0 + }; + + /** + * Half of the width of the slider handles + * + * @type {number} + */ + this.handleHalfWidth = 0; + + /** + * Maximum left the slider handle can have + * + * @type {number} + */ + this.maxLeft = 0; + + /** + * Precision + * + * @type {number} + */ + this.precision = 0; + + /** + * Step + * + * @type {number} + */ + this.step = 0; + + /** + * 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; + + /** + * Set to true if init method already executed + * + * @type {boolean} + */ + this.initHasRun = false; + + + /** If the slider events are already bound to the slider + * + * @type {boolean} + */ + this.eventsBound = false; + + // 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(); + }; - if(this.range) - { + // Add instance methods + Slider.prototype = { + + /** + * Initialize slider + * + * @returns {undefined} + */ + init: function() { + var thrLow, thrHigh, + calcDimFn = angular.bind(this, this.calcViewDimensions), + self = this; + + this.applyOptions(); + this.initElemHandles(); + this.addAccessibility(); + this.manageEventsBindings(); + this.setDisabledState(); + this.calcViewDimensions(); + this.setMinAndMax(); + + $timeout(function() { + self.updateCeilLab(); + self.updateFloorLab(); + self.initHandles(); + self.bindEvents(); + }); + + // 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 = throttle(function() { + self.setMinAndMax(); + self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); + self.updateSelectionBar(); + self.updateTicksScale(); + + if (self.range) { + self.updateCmbLabel(); + } + + }, self.interval); + + thrHigh = throttle(function() { + self.setMinAndMax(); + self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); + self.updateSelectionBar(); + self.updateTicksScale(); + self.updateCmbLabel(); + }, self.interval); + + this.scope.$on('rzSliderForceRender', function() { + self.resetLabelsValue(); + thrLow(); + if (self.range) { + thrHigh(); + } + self.resetSlider(); + }); + + // Watchers + this.scope.$watch('rzSliderModel', function(newValue, oldValue) { + if (newValue === oldValue) + return; + thrLow(); + }); + + this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { + if (newValue === oldValue) + return; + thrHigh(); + }); + + this.scope.$watch('rzSliderOptions', function(newValue, oldValue) { + if (newValue === oldValue) + return; + self.applyOptions(); + self.resetSlider(); + }, true); + + this.scope.$on('$destroy', function() { + self.unbindEvents(); + angular.element($window).off('resize', calcDimFn); + }); + }, + + /** + * Read the user options and apply them to the slider model + */ + applyOptions: function() { + var userOpts = this.scope.rzSliderOptions; + this.options = {}; + for (var option_name in defaultOptions) { + if (!userOpts || userOpts[option_name] === undefined) + this.options[option_name] = defaultOptions[option_name]; + else + this.options[option_name] = userOpts[option_name]; + } + this.options.draggableRange = this.range && this.options.draggableRange; + this.options.showTicks = this.options.showTicks || this.options.showTicksValues; + if (this.options.translate) + this.customTrFn = this.options.translate; + else + this.customTrFn = function(value) { + return String(value); + }; + }, + + /** + * Resets slider + * + * @returns {undefined} + */ + resetSlider: function() { + this.setMinAndMax(); + this.updateCeilLab(); + this.updateFloorLab(); + this.manageEventsBindings(); + this.setDisabledState(); + this.calcViewDimensions(); + }, + + /** + * Manage the events bindings based on readOnly and disabled options + * + * @returns {undefined} + */ + manageEventsBindings: function() { + if ((this.options.disabled || this.options.readOnly) && this.eventsBound) + this.unbindEvents(); + else if ((!this.options.disabled || !this.options.readOnly) && !this.eventsBound) + 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); + } + if (this.options.disabled && this.eventsBound) + this.unbindEvents(); + else if (!this.options.disabled && !this.eventsBound) + this.bindEvents(); + }, + + /** + * 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.valueToOffset(this.scope.rzSliderModel)); + + /* + 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.valueToOffset(this.scope.rzSliderHigh)); + this.updateSelectionBar(); + if (this.range) + this.updateCmbLabel(); + + this.updateTicksScale(); + }, + + /** + * Translate value to human readable format + * + * @param {number|string} value + * @param {jqLite} label + * @param {boolean} [useCustomTr] + * @returns {undefined} + */ + translateFn: function(value, label, useCustomTr) { + useCustomTr = useCustomTr === undefined ? true : useCustomTr; + + var valStr = String((useCustomTr ? this.customTrFn(value) : value)), + getWidth = false; + + if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) { + getWidth = true; + label.rzsv = valStr; + } + + label.text(valStr); + + // Update width only when length of the label have changed + if (getWidth) { + this.getWidth(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.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel); + if (this.range) + this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); + + this.minValue = +this.options.floor; + if (this.scope.rzSliderModel < this.minValue) + this.scope.rzSliderModel = this.minValue; + if (this.range && this.scope.rzSliderHigh < this.minValue) + this.scope.rzSliderHigh = this.minValue; + + if (this.options.ceil) { + this.maxValue = +this.options.ceil; + if (this.scope.rzSliderModel > this.maxValue) + this.scope.rzSliderModel = this.maxValue; + if (this.range && this.scope.rzSliderHigh > this.maxValue) + this.scope.rzSliderHigh = this.maxValue; + } + else + this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; + + this.valueRange = this.maxValue - this.minValue; + }, + + /** + * 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 offset cache properties + this.selBar.rzsl = 0; + this.minH.rzsl = 0; + this.maxH.rzsl = 0; + this.flrLab.rzsl = 0; + this.ceilLab.rzsl = 0; + this.minLab.rzsl = 0; + this.maxLab.rzsl = 0; + this.cmbLab.rzsl = 0; + + // Hide limit labels + if (this.options.hideLimitLabels) { + this.flrLab.rzAlwaysHide = true; + this.ceilLab.rzAlwaysHide = true; + this.hideEl(this.flrLab); + this.hideEl(this.ceilLab); + } + + if (this.options.showTicksValues) { + this.flrLab.rzAlwaysHide = true; + this.ceilLab.rzAlwaysHide = true; + this.minLab.rzAlwaysHide = true; + this.maxLab.rzAlwaysHide = true; + this.cmbLab.rzAlwaysHide = true; + this.hideEl(this.flrLab); + this.hideEl(this.ceilLab); + this.hideEl(this.minLab); + this.hideEl(this.maxLab); + this.hideEl(this.cmbLab); + } + + // Remove stuff not needed in single slider + if (this.range === false) { + this.cmbLab.remove(); + this.maxLab.remove(); + + // Hide max handle + this.maxH.rzAlwaysHide = true; + this.maxH[0].style.zIndex = '-1000'; + this.hideEl(this.maxH); + } + + // Show selection bar for single slider or not + if (this.range === false && this.options.showSelectionBar === false) { + this.maxH.remove(); + this.selBar.remove(); + } + + // If using draggable range, use appropriate cursor for this.selBar. + if (this.options.draggableRange) { + this.selBar.css('cursor', 'move'); + this.selBar.addClass('rz-draggable'); + } + }, + + /** + * Adds accessibility atributes + * + * Run only once during initialization + * + * @returns {undefined} + */ + addAccessibility: function() { + this.sliderElem.attr("role", "slider"); + }, + + /** + * 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.getWidth(this.minH); + + this.handleHalfWidth = handleWidth / 2; + this.barWidth = this.getWidth(this.fullBar); + + this.maxLeft = this.barWidth - handleWidth; + + this.getWidth(this.sliderElem); + this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left; + + if (this.initHasRun) { + this.updateFloorLab(); + this.updateCeilLab(); + this.initHandles(); + } + }, + + /** + * Update the ticks position + * + * @returns {undefined} + */ + updateTicksScale: function() { + if (!this.options.showTicks) return; + if (!this.step) return; //if step is 0, the following loop will be endless. + + var positions = '', + ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1; + for (var i = 0; i < ticksCount; i++) { + var value = this.roundStep(this.minValue + i * this.step); + var selectedClass = this.isTickSelected(value) ? 'selected' : ''; + positions += '
  • '; + if (this.options.showTicksValues) + positions += '' + this.getDisplayValue(value) + ''; + positions += '
  • '; + } + this.ticks.html(positions); + }, + + isTickSelected: function(value) { + if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel) + return true; + if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh) + return true; + return false; + }, + + /** + * Update position of the ceiling label + * + * @returns {undefined} + */ + updateCeilLab: function() { + this.translateFn(this.maxValue, this.ceilLab); + this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw); + this.getWidth(this.ceilLab); + }, + + /** + * Update position of the floor label + * + * @returns {undefined} + */ + updateFloorLab: function() { + this.translateFn(this.minValue, this.flrLab); + this.getWidth(this.flrLab); + }, + + /** + * Call the onStart callback if defined + * + * @returns {undefined} + */ + callOnStart: function() { + if (this.options.onStart) { + var self = this; + $timeout(function() { + self.options.onStart(); + }); + } + }, + + /** + * Call the onChange callback if defined + * + * @returns {undefined} + */ + callOnChange: function() { + if (this.options.onChange) { + var self = this; + $timeout(function() { + self.options.onChange(); + }); + } + }, + + /** + * Call the onEnd callback if defined + * + * @returns {undefined} + */ + callOnEnd: function() { + if (this.options.onEnd) { + var self = this; + $timeout(function() { + self.options.onEnd(); + }); + } + }, + + /** + * Update slider handles and label positions + * + * @param {string} which + * @param {number} newOffset + */ + updateHandles: function(which, newOffset) { + if (which === 'rzSliderModel') { + this.updateLowHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); + + if (this.range) { + this.updateCmbLabel(); + } + return; + } + + if (which === 'rzSliderHigh') { + this.updateHighHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); + + if (this.range) { + this.updateCmbLabel(); + } + return; + } + + // Update both + this.updateLowHandle(newOffset); + this.updateHighHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); this.updateCmbLabel(); - } - return; - } - - // Update both - this.updateLowHandle(newOffset); - this.updateHighHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); - this.updateCmbLabel(); - }, - - /** - * Update low slider handle position and label - * - * @param {number} newOffset - * @returns {undefined} - */ - updateLowHandle: function(newOffset) - { - this.setLeft(this.minH, newOffset); - this.translateFn(this.scope.rzSliderModel, this.minLab); - this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth); - - this.shFloorCeil(); - }, - - /** - * Update high slider handle position and label - * - * @param {number} newOffset - * @returns {undefined} - */ - updateHighHandle: function(newOffset) - { - this.setLeft(this.maxH, newOffset); - this.translateFn(this.scope.rzSliderHigh, this.maxLab); - this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth); + }, + + /** + * Update low slider handle position and label + * + * @param {number} newOffset + * @returns {undefined} + */ + updateLowHandle: function(newOffset) { + this.setLeft(this.minH, newOffset); + this.translateFn(this.scope.rzSliderModel, this.minLab); + this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth); + + this.shFloorCeil(); + }, + + /** + * Update high slider handle position and label + * + * @param {number} newOffset + * @returns {undefined} + */ + updateHighHandle: function(newOffset) { + this.setLeft(this.maxH, newOffset); + this.translateFn(this.scope.rzSliderHigh, this.maxLab); + this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth); + + this.shFloorCeil(); + }, + + /** + * Show / hide floor / ceiling label + * + * @returns {undefined} + */ + shFloorCeil: function() { + var flHidden = false, clHidden = false; + + if (this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5) { + flHidden = true; + this.hideEl(this.flrLab); + } + else { + flHidden = false; + this.showEl(this.flrLab); + } + + if (this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) { + clHidden = true; + this.hideEl(this.ceilLab); + } + else { + clHidden = false; + this.showEl(this.ceilLab); + } + + if (this.range) { + if (this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) { + this.hideEl(this.ceilLab); + } + else if (!clHidden) { + this.showEl(this.ceilLab); + } + + // Hide or show floor label + if (this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth) { + this.hideEl(this.flrLab); + } + else if (!flHidden) { + this.showEl(this.flrLab); + } + } + }, + + /** + * Update slider selection bar, combined label and range label + * + * @returns {undefined} + */ + updateSelectionBar: function() { + this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth); + this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0); + }, + + /** + * Update combined label position and value + * + * @returns {undefined} + */ + updateCmbLabel: function() { + var lowTr, highTr; + + if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) { + lowTr = this.getDisplayValue(this.scope.rzSliderModel); + highTr = this.getDisplayValue(this.scope.rzSliderHigh); + + this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false); + this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2); + this.hideEl(this.minLab); + this.hideEl(this.maxLab); + this.showEl(this.cmbLab); + } + else { + this.showEl(this.maxLab); + this.showEl(this.minLab); + this.hideEl(this.cmbLab); + } + }, + + /** + * Return the translated value if a translate function is provided else the original value + * @param value + * @returns {*} + */ + getDisplayValue: function(value) { + return this.customTrFn(value); + }, + + /** + * Round value to step and precision + * + * @param {number} value + * @returns {number} + */ + roundStep: function(value) { + var step = this.step, + remainder = +((value - this.minValue) % step).toFixed(3), + steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder; + + steppedValue = steppedValue.toFixed(this.precision); + return +steppedValue; + }, + + /** + * Hide element + * + * @param element + * @returns {jqLite} The jqLite wrapped DOM element + */ + hideEl: function(element) { + return element.css({opacity: 0}); + }, + + /** + * Show element + * + * @param element The jqLite wrapped DOM element + * @returns {jqLite} The jqLite + */ + showEl: function(element) { + if (!!element.rzAlwaysHide) { + return element; + } + + return element.css({opacity: 1}); + }, + + /** + * Set element left offset + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @param {number} left + * @returns {number} + */ + setLeft: function(elem, left) { + elem.rzsl = left; + elem.css({left: left + 'px'}); + return left; + }, + + /** + * Get element width + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @returns {number} + */ + getWidth: function(elem) { + var val = elem[0].getBoundingClientRect(); + elem.rzsw = val.right - val.left; + return elem.rzsw; + }, + + /** + * Set element width + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @param {number} width + * @returns {number} + */ + setWidth: function(elem, width) { + elem.rzsw = width; + elem.css({width: width + 'px'}); + return width; + }, + + /** + * Translate value to pixel offset + * + * @param {number} val + * @returns {number} + */ + valueToOffset: function(val) { + return (val - this.minValue) * this.maxLeft / this.valueRange || 0; + }, + + /** + * Translate offset to model value + * + * @param {number} offset + * @returns {number} + */ + offsetToValue: function(offset) { + return (offset / this.maxLeft) * this.valueRange + this.minValue; + }, + + // Events + + /** + * Get the X-coordinate of an event + * + * @param {Object} event The event + * @returns {number} + */ + getEventX: function(event) { + /* http://stackoverflow.com/a/12336075/282882 */ + //noinspection JSLint + if ('clientX' in event) { + return event.clientX; + } + + return event.originalEvent === undefined ? + event.touches[0].clientX + : event.originalEvent.touches[0].clientX; + }, + + /** + * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; + return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; + }, + + /** + * Bind mouse and touch events to slider handles + * + * @returns {undefined} + */ + bindEvents: function() { + if (this.options.readOnly || this.options.disabled) return; + var barTracking, barStart, barMove; + + if (this.options.draggableRange) { + barTracking = 'rzSliderDrag'; + barStart = this.onDragStart; + barMove = this.onDragMove; + } + else { + barTracking = 'rzSliderModel'; + barStart = this.onStart; + barMove = this.onMove; + } + + this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); + if (this.range) { + this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); + } + this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); + this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); + this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); + this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); + this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); + this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks)); + + this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); + if (this.range) { + this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); + } + this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); + this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); + this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); + this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); + this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); + this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); + + this.eventsBound = true; + }, + + /** + * 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(); + this.eventsBound = false; + }, + + /** + * 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(); + + if (this.tracking !== '') { + return; + } + + // 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 ? 'rzSliderModel' : 'rzSliderHigh'; + } + + pointer.addClass('rz-active'); + + 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.one(eventNames.endEvent, ehEnd); + this.callOnStart(); + }, + + /** + * onMove event handler + * + * @param {jqLite} pointer + * @param {Event} event The event + * @returns {undefined} + */ + onMove: function(pointer, event) { + var eventX = this.getEventX(event), + sliderLO, newOffset, newValue; + + sliderLO = this.sliderElem.rzsl; + newOffset = eventX - sliderLO - this.handleHalfWidth; + + if (newOffset <= 0) { + if (pointer.rzsl === 0) + return; + newValue = this.minValue; + newOffset = 0; + } + else if (newOffset >= this.maxLeft) { + if (pointer.rzsl === this.maxLeft) + return; + newValue = this.maxValue; + newOffset = this.maxLeft; + } + else { + newValue = this.offsetToValue(newOffset); + newValue = this.roundStep(newValue); + newOffset = this.valueToOffset(newValue); + } + this.positionTrackingHandle(newValue, newOffset); + }, + + /** + * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; + this.dragging = { + active: true, + value: this.offsetToValue(offset), + difference: this.scope.rzSliderHigh - this.scope.rzSliderModel, + offset: offset, + lowDist: offset - this.minH.rzsl, + highDist: this.maxH.rzsl - offset + }; + this.minH.addClass('rz-active'); + this.maxH.addClass('rz-active'); - this.shFloorCeil(); - }, + this.onStart(pointer, ref, event); + }, + + /** + * onDragMove event handler + * + * Handles dragging of the middle bar. + * + * @param {jqLite} pointer + * @param {Event} event The event + * @returns {undefined} + */ + onDragMove: function(pointer, event) { + var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth, + newMinOffset, newMaxOffset, + newMinValue, newMaxValue; + + if (newOffset <= this.dragging.lowDist) { + if (pointer.rzsl === this.dragging.lowDist) { + return; + } + newMinValue = this.minValue; + newMinOffset = 0; + newMaxValue = this.dragging.difference; + newMaxOffset = this.valueToOffset(newMaxValue); + } + else if (newOffset >= this.maxLeft - this.dragging.highDist) { + if (pointer.rzsl === this.dragging.highDist) { + return; + } + newMaxValue = this.maxValue; + newMaxOffset = this.maxLeft; + newMinValue = this.maxValue - this.dragging.difference; + newMinOffset = this.valueToOffset(newMinValue); + } + else { + newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist); + newMinValue = this.roundStep(newMinValue); + newMinOffset = this.valueToOffset(newMinValue); + newMaxValue = newMinValue + this.dragging.difference; + newMaxOffset = this.valueToOffset(newMaxValue); + } + + this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset); + }, + + /** + * Set the new value and offset for the entire bar + * + * @param {number} newMinValue the new minimum value + * @param {number} newMaxValue the new maximum value + * @param {number} newMinOffset the new minimum offset + * @param {number} newMaxOffset the new maximum offset + */ + positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) { + this.scope.rzSliderModel = newMinValue; + this.scope.rzSliderHigh = newMaxValue; + this.updateHandles('rzSliderModel', newMinOffset); + this.updateHandles('rzSliderHigh', newMaxOffset); + this.scope.$apply(); + this.callOnChange(); + }, + + /** + * Set the new value and offset to the current tracking handle + * + * @param {number} newValue new model value + * @param {number} newOffset new offset value + */ + positionTrackingHandle: function(newValue, newOffset) { + if (this.range) { + /* This is to check if we need to switch the min and max handles*/ + if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh) { + this.scope[this.tracking] = this.scope.rzSliderHigh; + this.updateHandles(this.tracking, this.maxH.rzsl); + this.tracking = 'rzSliderHigh'; + this.minH.removeClass('rz-active'); + this.maxH.addClass('rz-active'); + /* We need to apply here because we are not sure that we will enter the next block */ + this.scope.$apply(); + this.callOnChange(); + } + else if (this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) { + this.scope[this.tracking] = this.scope.rzSliderModel; + this.updateHandles(this.tracking, this.minH.rzsl); + this.tracking = 'rzSliderModel'; + this.maxH.removeClass('rz-active'); + this.minH.addClass('rz-active'); + /* We need to apply here because we are not sure that we will enter the next block */ + this.scope.$apply(); + this.callOnChange(); + } + } + + if (this.scope[this.tracking] !== newValue) { + this.scope[this.tracking] = newValue; + this.updateHandles(this.tracking, newOffset); + this.scope.$apply(); + this.callOnChange(); + } + }, + + /** + * onEnd event handler + * + * @param {Event} event The event + * @param {Function} ehMove The the bound move event handler + * @returns {undefined} + */ + onEnd: function(ehMove, event) { + var moveEventName = this.getEventNames(event).moveEvent; - /** - * Show / hide floor / ceiling label - * - * @returns {undefined} - */ - shFloorCeil: function() - { - var flHidden = false, clHidden = false; - - if(this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5) - { - flHidden = true; - this.hideEl(this.flrLab); - } - else - { - flHidden = false; - this.showEl(this.flrLab); - } - - if(this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) - { - clHidden = true; - this.hideEl(this.ceilLab); - } - else - { - clHidden = false; - this.showEl(this.ceilLab); - } - - if(this.range) - { - if(this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) - { - this.hideEl(this.ceilLab); - } - else if( ! clHidden) - { - this.showEl(this.ceilLab); - } + this.minH.removeClass('rz-active'); + this.maxH.removeClass('rz-active'); - // Hide or show floor label - if(this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth) - { - this.hideEl(this.flrLab); + $document.off(moveEventName, ehMove); + + this.scope.$emit('slideEnded'); + this.tracking = ''; + + this.dragging.active = false; + this.callOnEnd(); + }, + + /** + * 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 (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) { + eventNames.moveEvent = 'touchmove'; + eventNames.endEvent = 'touchend'; + } + else { + eventNames.moveEvent = 'mousemove'; + eventNames.endEvent = 'mouseup'; + } + + return eventNames; } - else if( ! flHidden) - { - this.showEl(this.flrLab); - } - } - }, - - /** - * Update slider selection bar, combined label and range label - * - * @returns {undefined} - */ - updateSelectionBar: function() - { - this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth); - this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0); - }, - - /** - * Update combined label position and value - * - * @returns {undefined} - */ - updateCmbLabel: function() - { - var lowTr, highTr; - - if(this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) - { - lowTr = this.getDisplayValue(this.scope.rzSliderModel); - highTr = this.getDisplayValue(this.scope.rzSliderHigh); - - this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false); - this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2); - this.hideEl(this.minLab); - this.hideEl(this.maxLab); - this.showEl(this.cmbLab); - } - else - { - this.showEl(this.maxLab); - this.showEl(this.minLab); - this.hideEl(this.cmbLab); - } - }, - - /** - * Return the translated value if a translate function is provided else the original value - * @param value - * @returns {*} - */ - getDisplayValue: function(value) { - return this.customTrFn ? this.customTrFn(value): value; - }, - - /** - * Round value to step and precision - * - * @param {number} value - * @returns {number} - */ - roundStep: function(value) - { - var step = this.step, - remainder = +((value - this.minValue) % step).toFixed(3), - steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder; - - steppedValue = steppedValue.toFixed(this.precision); - return +steppedValue; - }, - - /** - * Hide element - * - * @param element - * @returns {jqLite} The jqLite wrapped DOM element - */ - hideEl: function (element) - { - return element.css({opacity: 0}); - }, - - /** - * Show element - * - * @param element The jqLite wrapped DOM element - * @returns {jqLite} The jqLite - */ - showEl: function (element) - { - if(!!element.rzAlwaysHide) { return element; } - - return element.css({opacity: 1}); - }, - - /** - * Set element left offset - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @param {number} left - * @returns {number} - */ - setLeft: function (elem, left) - { - elem.rzsl = left; - elem.css({left: left + 'px'}); - return left; - }, - - /** - * Get element width - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @returns {number} - */ - getWidth: function(elem) - { - var val = elem[0].getBoundingClientRect(); - elem.rzsw = val.right - val.left; - return elem.rzsw; - }, - - /** - * Set element width - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @param {number} width - * @returns {number} - */ - setWidth: function(elem, width) - { - elem.rzsw = width; - elem.css({width: width + 'px'}); - return width; - }, - - /** - * Translate value to pixel offset - * - * @param {number} val - * @returns {number} - */ - valueToOffset: function(val) - { - return (val - this.minValue) * this.maxLeft / this.valueRange || 0; - }, - - /** - * Translate offset to model value - * - * @param {number} offset - * @returns {number} - */ - offsetToValue: function(offset) - { - return (offset / this.maxLeft) * this.valueRange + this.minValue; - }, - - // Events - - /** - * Get the X-coordinate of an event - * - * @param {Object} event The event - * @returns {number} - */ - getEventX: function(event) - { - /* http://stackoverflow.com/a/12336075/282882 */ - //noinspection JSLint - if('clientX' in event) - { - return event.clientX; - } - - return event.originalEvent === undefined ? - event.touches[0].clientX - : event.originalEvent.touches[0].clientX; - }, - - /** - * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; - return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; - }, - - /** - * Bind mouse and touch events to slider handles - * - * @returns {undefined} - */ - bindEvents: function() - { - if(this.presentOnly || this.disabled) return; - var barTracking, barStart, barMove; - - if (this.dragRange) - { - barTracking = 'rzSliderDrag'; - barStart = this.onDragStart; - barMove = this.onDragMove; - } - else - { - barTracking = 'rzSliderModel'; - barStart = this.onStart; - barMove = this.onMove; - } - - this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); - if(this.range) { this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); } - this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); - this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); - this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); - this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); - this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); - this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks)); - - this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); - if(this.range) { this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); } - this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); - this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); - this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); - this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); - this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); - this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); - }, - - /** - * 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(); - - if(this.tracking !== '') { return; } - - // 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 ? 'rzSliderModel' : 'rzSliderHigh'; - } - - pointer.addClass('rz-active'); - - 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.one(eventNames.endEvent, ehEnd); - this.callOnStart(); - }, - - /** - * onMove event handler - * - * @param {jqLite} pointer - * @param {Event} event The event - * @returns {undefined} - */ - onMove: function (pointer, event) - { - var eventX = this.getEventX(event), - sliderLO, newOffset, newValue; - - sliderLO = this.sliderElem.rzsl; - newOffset = eventX - sliderLO - this.handleHalfWidth; - - if(newOffset <= 0) - { - if(pointer.rzsl === 0) - return; - newValue = this.minValue; - newOffset = 0; - } - else if(newOffset >= this.maxLeft) - { - if(pointer.rzsl === this.maxLeft) - return; - newValue = this.maxValue; - newOffset = this.maxLeft; - } - else { - newValue = this.offsetToValue(newOffset); - newValue = this.roundStep(newValue); - newOffset = this.valueToOffset(newValue); - } - this.positionTrackingHandle(newValue, newOffset); - }, - - /** - * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; - this.dragging = { - active: true, - value: this.offsetToValue(offset), - difference: this.scope.rzSliderHigh - this.scope.rzSliderModel, - offset: offset, - lowDist: offset - this.minH.rzsl, - highDist: this.maxH.rzsl - offset }; - this.minH.addClass('rz-active'); - this.maxH.addClass('rz-active'); - - this.onStart(pointer, ref, event); - }, - - /** - * onDragMove event handler - * - * Handles dragging of the middle bar. - * - * @param {jqLite} pointer - * @param {Event} event The event - * @returns {undefined} - */ - onDragMove: function(pointer, event) - { - var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth, - newMinOffset, newMaxOffset, - newMinValue, newMaxValue; - - if (newOffset <= this.dragging.lowDist) - { - if (pointer.rzsl === this.dragging.lowDist) { return; } - newMinValue = this.minValue; - newMinOffset = 0; - newMaxValue = this.dragging.difference; - newMaxOffset = this.valueToOffset(newMaxValue); - } - else if (newOffset >= this.maxLeft - this.dragging.highDist) - { - if (pointer.rzsl === this.dragging.highDist) { return; } - newMaxValue = this.maxValue; - newMaxOffset = this.maxLeft; - newMinValue = this.maxValue - this.dragging.difference; - newMinOffset = this.valueToOffset(newMinValue); - } - else - { - newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist); - newMinValue = this.roundStep(newMinValue); - newMinOffset = this.valueToOffset(newMinValue); - newMaxValue = newMinValue + this.dragging.difference; - newMaxOffset = this.valueToOffset(newMaxValue); - } - - this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset); - }, - /** - * Set the new value and offset for the entire bar - * - * @param {number} newMinValue the new minimum value - * @param {number} newMaxValue the new maximum value - * @param {number} newMinOffset the new minimum offset - * @param {number} newMaxOffset the new maximum offset - */ - positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) - { - this.scope.rzSliderModel = newMinValue; - this.scope.rzSliderHigh = newMaxValue; - this.updateHandles('rzSliderModel', newMinOffset); - this.updateHandles('rzSliderHigh', newMaxOffset); - this.scope.$apply(); - this.callOnChange(); - }, - - /** - * Set the new value and offset to the current tracking handle - * - * @param {number} newValue new model value - * @param {number} newOffset new offset value - */ - positionTrackingHandle: function(newValue, newOffset) - { - if(this.range) - { - /* This is to check if we need to switch the min and max handles*/ - if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh) - { - this.scope[this.tracking] = this.scope.rzSliderHigh; - this.updateHandles(this.tracking, this.maxH.rzsl); - this.tracking = 'rzSliderHigh'; - this.minH.removeClass('rz-active'); - this.maxH.addClass('rz-active'); - /* We need to apply here because we are not sure that we will enter the next block */ - this.scope.$apply(); - this.callOnChange(); + return Slider; + }]) + + .directive('rzslider', ['RzSlider', function(RzSlider) { + 'use strict'; + + return { + restrict: 'E', + 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) { + return new RzSlider(scope, elem); } - else if(this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) - { - this.scope[this.tracking] = this.scope.rzSliderModel; - this.updateHandles(this.tracking, this.minH.rzsl); - this.tracking = 'rzSliderModel'; - this.maxH.removeClass('rz-active'); - this.minH.addClass('rz-active'); - /* We need to apply here because we are not sure that we will enter the next block */ - this.scope.$apply(); - this.callOnChange(); - } - } - - if(this.scope[this.tracking] !== newValue) - { - this.scope[this.tracking] = newValue; - this.updateHandles(this.tracking, newOffset); - this.scope.$apply(); - this.callOnChange(); - } - }, - - /** - * onEnd event handler - * - * @param {Event} event The event - * @param {Function} ehMove The the bound move event handler - * @returns {undefined} - */ - onEnd: function(ehMove, event) - { - var moveEventName = this.getEventNames(event).moveEvent; - - this.minH.removeClass('rz-active'); - this.maxH.removeClass('rz-active'); - - $document.off(moveEventName, ehMove); - - this.scope.$emit('slideEnded'); - this.tracking = ''; - - this.dragging.active = false; - this.callOnEnd(); - }, - - /** - * 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(event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) - { - eventNames.moveEvent = 'touchmove'; - eventNames.endEvent = 'touchend'; - } - else - { - eventNames.moveEvent = 'mousemove'; - eventNames.endEvent = 'mouseup'; - } - - return eventNames; - } - }; - - return Slider; -}]) - -.directive('rzslider', ['RzSlider', function(RzSlider) -{ - 'use strict'; - - return { - restrict: 'E', - scope: { - rzSliderFloor: '=?', - rzSliderCeil: '=?', - rzSliderStep: '@', - rzSliderPrecision: '@', - rzSliderModel: '=?', - rzSliderHigh: '=?', - rzSliderDraggable: '@', - rzSliderTranslate: '&', - rzSliderHideLimitLabels: '=?', - rzSliderAlwaysShowBar: '=?', - rzSliderPresentOnly: '@', - rzSliderOnStart: '&', - rzSliderOnChange: '&', - rzSliderOnEnd: '&', - rzSliderShowTicks: '=?', - rzSliderShowTicksValue: '=?', - rzSliderDisabled: '=?', - rzSliderInterval: '=?', - }, - - /** - * 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, attr) - { - return new RzSlider(scope, elem, attr); - } - }; -}]); + }; + }]); // IDE assist -/** - * @name ngScope - * - * @property {number} rzSliderModel - * @property {number} rzSliderHigh - * @property {number} rzSliderCeil - */ + /** + * @name ngScope + * + * @property {number} rzSliderModel + * @property {number} rzSliderHigh + * @property {Object} rzSliderOptions + */ -/** - * @name jqLite - * - * @property {number|undefined} rzsl rzslider label left offset - * @property {number|undefined} rzsw rzslider element width - * @property {string|undefined} rzsv rzslider label value/text - * @property {Function} css - * @property {Function} text - */ + /** + * @name jqLite + * + * @property {number|undefined} rzsl rzslider label left offset + * @property {number|undefined} rzsw rzslider element width + * @property {string|undefined} rzsv rzslider label value/text + * @property {Function} css + * @property {Function} text + */ -/** - * @name Event - * @property {Array} touches - * @property {Event} originalEvent - */ + /** + * @name Event + * @property {Array} touches + * @property {Event} originalEvent + */ -/** - * @name ThrottleOptions - * - * @property {boolean} leading - * @property {boolean} trailing - */ + /** + * @name ThrottleOptions + * + * @property {boolean} leading + * @property {boolean} trailing + */ module.run(['$templateCache', function($templateCache) { 'use strict'; diff --git a/dist/rzslider.min.css b/dist/rzslider.min.css index 7b451a8..03c5704 100644 --- a/dist/rzslider.min.css +++ b/dist/rzslider.min.css @@ -1,2 +1,2 @@ -/*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-07 */ +/*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:30px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}rzslider[disabled]{cursor:not-allowed}rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}rzslider span{position:absolute;display:inline-block;white-space:nowrap}rzslider .rz-base{width:100%;height:100%;padding:0}rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}rzslider .rz-pointer:hover:after{background-color:#fff}rzslider .rz-pointer.rz-active:after{background-color:#451aff}rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}rzslider .rz-bubble.rz-selection{top:16px}rzslider .rz-bubble.rz-limit{color:#55637d}rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:flex;width:100%;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;justify-content:space-between}rzslider .rz-ticks .tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}rzslider .rz-ticks .tick.selected{background:#0db9f0}rzslider .rz-ticks .tick .tick-value{position:absolute;top:-30px;transform:translate(-50%,0)} \ No newline at end of file diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index 48384b2..63ff9e1 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ -/*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-07 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","throttle",function(b,c,d,e){var f=function(a,b,c){this.scope=a,this.attributes=c,this.sliderElem=b,this.range=void 0!==c.rzSliderHigh&&void 0!==c.rzSliderModel,this.dragRange=this.range&&"true"===c.rzSliderDraggableRange,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.alwaysShowBar=!!c.rzSliderAlwaysShowBar,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.hideLimitLabels=!!c.rzSliderHideLimitLabels,this.presentOnly="true"===c.rzSliderPresentOnly,this.showTicks=c.rzSliderShowTicks||c.rzSliderShowTicksValue,this.showTicksValue=c.rzSliderShowTicksValue,this.disabled=this.scope.rzSliderDisabled,this.interval=null!==this.scope.rzSliderInterval?this.scope.rzSliderInterval:350,this.valueRange=0,this.initHasRun=!1,this.customTrFn=this.scope.rzSliderTranslate()||function(a){return String(a)},this.deRegFuncs=[],this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return f.prototype={init:function(){var c,f,g,h=a.bind(this,this.calcViewDimensions),i=this;this.initElemHandles(),this.addAccessibility(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),this.precision=void 0===this.scope.rzSliderPrecision?0:+this.scope.rzSliderPrecision,this.step=void 0===this.scope.rzSliderStep?1:+this.scope.rzSliderStep,b(function(){i.updateCeilLab(),i.updateFloorLab(),i.initHandles(),i.bindEvents()}),g=this.scope.$on("reCalcViewDimensions",h),this.deRegFuncs.push(g),a.element(d).on("resize",h),this.initHasRun=!0,c=e(function(){i.setMinAndMax(),i.updateLowHandle(i.valueToOffset(i.scope.rzSliderModel)),i.updateSelectionBar(),i.updateTicksScale(),i.range&&i.updateCmbLabel()},i.interval),f=e(function(){i.setMinAndMax(),i.updateHighHandle(i.valueToOffset(i.scope.rzSliderHigh)),i.updateSelectionBar(),i.updateTicksScale(),i.updateCmbLabel()},i.interval),this.scope.$on("rzSliderForceRender",function(){i.resetLabelsValue(),c(),i.range&&f(),i.resetSlider()}),g=this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.deRegFuncs.push(g),g=this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&f()}),this.deRegFuncs.push(g),this.scope.$watch("rzSliderFloor",function(a,b){a!==b&&i.resetSlider()}),this.deRegFuncs.push(g),g=this.scope.$watch("rzSliderCeil",function(a,b){a!==b&&i.resetSlider()}),this.deRegFuncs.push(g),g=this.scope.$watch("rzSliderShowTicks",function(a,b){a!==b&&i.resetSlider()}),this.deRegFuncs.push(g),g=this.scope.$watch("rzSliderShowTicksValue",function(a,b){a!==b&&i.resetSlider()}),this.deRegFuncs.push(g),g=this.scope.$watch("rzSliderDisabled",function(a,b){a!==b&&(i.resetSlider(),i.disabled?i.unbindEvents():i.bindEvents())}),this.deRegFuncs.push(g),this.scope.$on("$destroy",function(){i.unbindEvents(),a.element(d).off("resize",h),i.deRegFuncs.map(function(a){a()})})},resetSlider:function(){this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.setDisabledState(),this.calcViewDimensions()},setDisabledState:function(){this.disabled=this.scope.rzSliderDisabled,this.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=(c?this.customTrFn(a):a).toString(),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.scope.rzSliderFloor?this.minValue=+this.scope.rzSliderFloor:this.minValue=this.scope.rzSliderFloor=0,this.scope.rzSliderCeil?this.maxValue=+this.scope.rzSliderCeil:this.maxValue=this.scope.rzSliderCeil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.scope.rzSliderStep&&(this.step=+this.scope.rzSliderStep),this.valueRange=this.maxValue-this.minValue},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0,this.hideLimitLabels&&(this.flrLab.rzAlwaysHide=!0,this.ceilLab.rzAlwaysHide=!0,this.hideEl(this.flrLab),this.hideEl(this.ceilLab)),this.showTicksValue&&(this.flrLab.rzAlwaysHide=!0,this.ceilLab.rzAlwaysHide=!0,this.minLab.rzAlwaysHide=!0,this.maxLab.rzAlwaysHide=!0,this.cmbLab.rzAlwaysHide=!0,this.hideEl(this.flrLab),this.hideEl(this.ceilLab),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.hideEl(this.cmbLab)),this.range===!1&&(this.cmbLab.remove(),this.maxLab.remove(),this.maxH.rzAlwaysHide=!0,this.maxH[0].style.zIndex="-1000",this.hideEl(this.maxH)),this.range===!1&&this.alwaysShowBar===!1&&(this.maxH.remove(),this.selBar.remove()),this.dragRange&&(this.selBar.css("cursor","move"),this.selBar.addClass("rz-draggable"))},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
  • ',this.showTicksValue&&(a+=''+this.getDisplayValue(d)+""),a+="
  • "}this.ticks.html(a)}},isTickSelected:function(a){this.valueToOffset(a);return!this.range&&this.alwaysShowBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.scope.rzSliderCeil,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.scope.rzSliderFloor,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.scope.rzSliderOnStart){var a=this;b(function(){a.scope.rzSliderOnStart()})}},callOnChange:function(){if(this.scope.rzSliderOnChange){var a=this;b(function(){a.scope.rzSliderOnChange()})}},callOnEnd:function(){if(this.scope.rzSliderOnEnd){var a=this;b(function(){a.scope.rzSliderOnEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn?this.customTrFn(a):a},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=b.right-b.left,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(a-this.minValue)*this.maxLeft/this.valueRange||0},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},f}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderFloor:"=?",rzSliderCeil:"=?",rzSliderStep:"@",rzSliderPrecision:"@",rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderDraggable:"@",rzSliderTranslate:"&",rzSliderHideLimitLabels:"=?",rzSliderAlwaysShowBar:"=?",rzSliderPresentOnly:"@",rzSliderOnStart:"&",rzSliderOnChange:"&",rzSliderOnEnd:"&",rzSliderShowTicks:"=?",rzSliderShowTicksValue:"=?",rzSliderDisabled:"=?",rzSliderInterval:"=?"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c,d){return new a(b,c,d)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
      ')}]),b}); \ No newline at end of file +/*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).value("defaultOptions",{floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,onStart:null,onChange:null,onEnd:null}).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","defaultOptions","throttle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.eventsBound=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&e()}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){var a=this.scope.rzSliderOptions;this.options={};for(var b in e)a&&void 0!==a[b]?this.options[b]=a[b]:this.options[b]=e[b];this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},manageEventsBindings:function(){(this.options.disabled||this.options.readOnly)&&this.eventsBound?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.eventsBound||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null),this.options.disabled&&this.eventsBound?this.unbindEvents():this.options.disabled||this.eventsBound||this.bindEvents()},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=+this.options.floor,this.scope.rzSliderModelthis.maxValue&&(this.scope.rzSliderModel=this.maxValue),this.range&&this.scope.rzSliderHigh>this.maxValue&&(this.scope.rzSliderHigh=this.maxValue)):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0,this.options.hideLimitLabels&&(this.flrLab.rzAlwaysHide=!0,this.ceilLab.rzAlwaysHide=!0,this.hideEl(this.flrLab),this.hideEl(this.ceilLab)),this.options.showTicksValues&&(this.flrLab.rzAlwaysHide=!0,this.ceilLab.rzAlwaysHide=!0,this.minLab.rzAlwaysHide=!0,this.maxLab.rzAlwaysHide=!0,this.cmbLab.rzAlwaysHide=!0,this.hideEl(this.flrLab),this.hideEl(this.ceilLab),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.hideEl(this.cmbLab)),this.range===!1&&(this.cmbLab.remove(),this.maxLab.remove(),this.maxH.rzAlwaysHide=!0,this.maxH[0].style.zIndex="-1000",this.hideEl(this.maxH)),this.range===!1&&this.options.showSelectionBar===!1&&(this.maxH.remove(),this.selBar.remove()),this.options.draggableRange&&(this.selBar.css("cursor","move"),this.selBar.addClass("rz-draggable"))},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
    • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
    • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=b.right-b.left,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(a-this.minValue)*this.maxLeft/this.valueRange||0},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
        ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index 116227d..028625d 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -11,7 +11,7 @@ /*jslint unparam: true */ /*global angular: false, console: false, define, module */ -(function (root, factory) { +(function(root, factory) { 'use strict'; if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. @@ -27,1458 +27,1344 @@ factory(root.angular); } -}(this, function (angular) { +}(this, function(angular) { 'use strict'; -var module = angular.module('rzModule', []) - -.value('throttle', - /** - * throttle - * - * Taken from underscore project - * - * @param {Function} func - * @param {number} wait - * @param {ThrottleOptions} options - * @returns {Function} - */ -function throttle(func, wait, options) { - 'use strict'; - 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 = options.leading === false ? 0 : getTime(); - timeout = null; - result = func.apply(context, args); - context = args = null; - }; - return function() { - var now = getTime(); - if (!previous && options.leading === false) { previous = now; } - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - context = args = null; - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); - } - return result; - }; -}) - -.factory('RzSlider', function($timeout, $document, $window, throttle) -{ - 'use strict'; - - /** - * Slider - * - * @param {ngScope} scope The AngularJS scope - * @param {Element} sliderElem The slider directive element wrapped in jqLite - * @param {*} attributes The slider directive attributes - * @constructor - */ - var Slider = function(scope, sliderElem, attributes) - { - /** - * The slider's scope - * - * @type {ngScope} - */ - this.scope = scope; - - /** - * The slider attributes - * - * @type {Object} - */ - this.attributes = attributes; - - /** - * Slider element wrapped in jqLite - * - * @type {jqLite} - */ - this.sliderElem = sliderElem; - - /** - * Slider type - * - * @type {boolean} Set to true for range slider - */ - this.range = attributes.rzSliderHigh !== undefined && attributes.rzSliderModel !== undefined; - - /** - * Whether to allow draggable range - * - * @type {boolean} Set to true for draggable range slider - */ - this.dragRange = this.range && attributes.rzSliderDraggableRange === 'true'; - - /** - * Values recorded when first dragging the bar - * - * @type {Object} - */ - this.dragging = { - active: false, - value: 0, - difference: 0, - offset: 0, - lowDist: 0, - highDist: 0 - }; - - /** - * Half of the width of the slider handles - * - * @type {number} - */ - this.handleHalfWidth = 0; - - /** - * Always show selection bar - * - * @type {boolean} - */ - this.alwaysShowBar = !!attributes.rzSliderAlwaysShowBar; - - /** - * Maximum left the slider handle can have - * - * @type {number} - */ - this.maxLeft = 0; - - /** - * Precision - * - * @type {number} - */ - this.precision = 0; - - /** - * Step - * - * @type {number} - */ - this.step = 0; - - /** - * 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; - - /** - * Hide limit labels - * - * @type {boolean} - */ - this.hideLimitLabels = !!attributes.rzSliderHideLimitLabels; - - /** - * Only present model values - * - * Do not allow to change values - * - * @type {boolean} - */ - this.presentOnly = attributes.rzSliderPresentOnly === 'true'; - - /** - * Display ticks on each possible value. - * - * @type {boolean} - */ - this.showTicks = attributes.rzSliderShowTicks || attributes.rzSliderShowTicksValue; - - /** - * Display the value on each tick. - * - * @type {boolean} - */ - this.showTicksValue = attributes.rzSliderShowTicksValue; - - /** - * Disable the slider - * - * @type {boolean} - */ - this.disabled = this.scope.rzSliderDisabled; - - /** - * The interval at which the slider updates when the model/high values - * are altered from outside the slider - * - * @type {number} - */ - this.interval = this.scope.rzSliderInterval !== null ? this.scope.rzSliderInterval : 350; - - /** - * The delta between min and max value - * - * @type {number} - */ - this.valueRange = 0; - - /** - * Set to true if init method already executed - * - * @type {boolean} - */ - this.initHasRun = false; - - /** - * Custom translate function - * - * @type {function} - */ - this.customTrFn = this.scope.rzSliderTranslate() || function(value) { return String(value); }; - - /** - * Array of de-registration functions to call on $destroy - * - * @type {Array.} - */ - this.deRegFuncs = []; - - // 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, unRegFn, - calcDimFn = angular.bind(this, this.calcViewDimensions), - self = this; - - this.initElemHandles(); - this.addAccessibility(); - this.setDisabledState(); - this.calcViewDimensions(); - this.setMinAndMax(); - - this.precision = this.scope.rzSliderPrecision === undefined ? 0 : +this.scope.rzSliderPrecision; - this.step = this.scope.rzSliderStep === undefined ? 1 : +this.scope.rzSliderStep; - - $timeout(function() - { - self.updateCeilLab(); - self.updateFloorLab(); - self.initHandles(); - self.bindEvents(); + var module = angular.module('rzModule', []) + + .value('defaultOptions', { + floor: 0, + ceil: null, //defaults to rz-slider-model + step: 1, + precision: 0, + translate: null, + draggableRange: false, + showSelectionBar: false, + hideLimitLabels: false, + readOnly: false, + disabled: false, + interval: 350, + showTicks: false, + showTicksValues: false, + onStart: null, + onChange: null, + onEnd: null + }) + + .value('throttle', + /** + * throttle + * + * Taken from underscore project + * + * @param {Function} func + * @param {number} wait + * @param {ThrottleOptions} options + * @returns {Function} + */ + function throttle(func, wait, options) { + 'use strict'; + var getTime = (Date.now || function() { + return new Date().getTime(); }); - - // Recalculate slider view dimensions - unRegFn = this.scope.$on('reCalcViewDimensions', calcDimFn); - this.deRegFuncs.push(unRegFn); - - // Recalculate stuff if view port dimensions have changed - angular.element($window).on('resize', calcDimFn); - - this.initHasRun = true; - - // Watch for changes to the model - - thrLow = throttle(function() - { - self.setMinAndMax(); - self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); - self.updateSelectionBar(); - self.updateTicksScale(); - - if(self.range) - { - self.updateCmbLabel(); + var context, args, result; + var timeout = null; + var previous = 0; + options = options || {}; + var later = function() { + previous = options.leading === false ? 0 : getTime(); + timeout = null; + result = func.apply(context, args); + context = args = null; + }; + return function() { + var now = getTime(); + if (!previous && options.leading === false) { + previous = now; } - - }, self.interval); - - thrHigh = throttle(function() - { - self.setMinAndMax(); - self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); - self.updateSelectionBar(); - self.updateTicksScale(); - self.updateCmbLabel(); - }, self.interval); - - this.scope.$on('rzSliderForceRender', function() - { - self.resetLabelsValue(); - thrLow(); - if(self.range) { thrHigh(); } - self.resetSlider(); - }); - - // Watchers - - unRegFn = this.scope.$watch('rzSliderModel', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - thrLow(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderHigh', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - thrHigh(); - }); - this.deRegFuncs.push(unRegFn); - - this.scope.$watch('rzSliderFloor', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderCeil', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderShowTicks', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderShowTicksValue', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - }); - this.deRegFuncs.push(unRegFn); - - unRegFn = this.scope.$watch('rzSliderDisabled', function(newValue, oldValue) - { - if(newValue === oldValue) { return; } - self.resetSlider(); - if(self.disabled) - self.unbindEvents(); - else - self.bindEvents(); - }); - this.deRegFuncs.push(unRegFn); - - this.scope.$on('$destroy', function() - { - self.unbindEvents(); - angular.element($window).off('resize', calcDimFn); - self.deRegFuncs.map(function(unbind) { unbind(); }); - }); - }, - - /** - * Resets slider - * - * @returns {undefined} - */ - resetSlider: function() - { - this.setMinAndMax(); - this.updateCeilLab(); - this.updateFloorLab(); - this.setDisabledState(); - this.calcViewDimensions(); - }, - - /** - * Set the disabled state based on rzSliderDisabled - * - * @returns {undefined} - */ - setDisabledState: function() - { - this.disabled = this.scope.rzSliderDisabled; - if(this.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.valueToOffset(this.scope.rzSliderModel)); - - /* - 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.valueToOffset(this.scope.rzSliderHigh)); - this.updateSelectionBar(); - if(this.range) - this.updateCmbLabel(); - - this.updateTicksScale(); - }, - - /** - * Translate value to human readable format - * - * @param {number|string} value - * @param {jqLite} label - * @param {boolean} [useCustomTr] - * @returns {undefined} - */ - translateFn: function(value, label, useCustomTr) - { - useCustomTr = useCustomTr === undefined ? true : useCustomTr; - - var valStr = (useCustomTr ? this.customTrFn(value) : value).toString(), - getWidth = false; - - if(label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) - { - getWidth = true; - label.rzsv = valStr; - } - - label.text(valStr); - - // Update width only when length of the label have changed - if(getWidth) { this.getWidth(label); } - }, - - /** - * Set maximum and minimum values for the slider - * - * @returns {undefined} - */ - setMinAndMax: function() - { - if(this.scope.rzSliderFloor) - { - this.minValue = +this.scope.rzSliderFloor; - } - else - { - this.minValue = this.scope.rzSliderFloor = 0; - } - - if(this.scope.rzSliderCeil) - { - this.maxValue = +this.scope.rzSliderCeil; - } - else - { - this.maxValue = this.scope.rzSliderCeil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; - } - - if(this.scope.rzSliderStep) - { - this.step = +this.scope.rzSliderStep; - } - - this.valueRange = this.maxValue - this.minValue; - }, - - /** - * 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; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); } + return result; + }; + }) - }, this); - - // Initialize offset cache properties - this.selBar.rzsl = 0; - this.minH.rzsl = 0; - this.maxH.rzsl = 0; - this.flrLab.rzsl = 0; - this.ceilLab.rzsl = 0; - this.minLab.rzsl = 0; - this.maxLab.rzsl = 0; - this.cmbLab.rzsl = 0; - - // Hide limit labels - if(this.hideLimitLabels) - { - this.flrLab.rzAlwaysHide = true; - this.ceilLab.rzAlwaysHide = true; - this.hideEl(this.flrLab); - this.hideEl(this.ceilLab); - } - - if(this.showTicksValue) { - this.flrLab.rzAlwaysHide = true; - this.ceilLab.rzAlwaysHide = true; - this.minLab.rzAlwaysHide = true; - this.maxLab.rzAlwaysHide = true; - this.cmbLab.rzAlwaysHide = true; - this.hideEl(this.flrLab); - this.hideEl(this.ceilLab); - this.hideEl(this.minLab); - this.hideEl(this.maxLab); - this.hideEl(this.cmbLab); - } - - // Remove stuff not needed in single slider - if(this.range === false) - { - this.cmbLab.remove(); - this.maxLab.remove(); - - // Hide max handle - this.maxH.rzAlwaysHide = true; - this.maxH[0].style.zIndex = '-1000'; - this.hideEl(this.maxH); - } - - // Show selection bar for single slider or not - if(this.range === false && this.alwaysShowBar === false) - { - this.maxH.remove(); - this.selBar.remove(); - } - - // If using draggable range, use appropriate cursor for this.selBar. - if (this.dragRange) - { - this.selBar.css('cursor', 'move'); - this.selBar.addClass('rz-draggable'); - } - }, - - /** - * Adds accessibility atributes - * - * Run only once during initialization - * - * @returns {undefined} - */ - addAccessibility: function () - { - this.sliderElem.attr("role", "slider"); - }, - - /** - * 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.getWidth(this.minH); - - this.handleHalfWidth = handleWidth / 2; - this.barWidth = this.getWidth(this.fullBar); - - this.maxLeft = this.barWidth - handleWidth; - - this.getWidth(this.sliderElem); - this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left; - - if(this.initHasRun) - { - this.updateFloorLab(); - this.updateCeilLab(); - this.initHandles(); - } - }, - - /** - * Update the ticks position - * - * @returns {undefined} - */ - updateTicksScale: function() { - if(!this.showTicks) return; - if(!this.step) return; //if step is 0, the following loop will be endless. - - var positions = '', - ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1; - for (var i = 0; i < ticksCount; i++) { - var value = this.roundStep(this.minValue + i * this.step); - var selectedClass = this.isTickSelected(value) ? 'selected': ''; - positions += '
      • '; - if(this.showTicksValue) - positions += ''+ this.getDisplayValue(value) +''; - positions += '
      • '; - } - this.ticks.html(positions); - }, - - isTickSelected: function(value) { - var tickLeft = this.valueToOffset(value); - if(!this.range && this.alwaysShowBar && value <= this.scope.rzSliderModel) - return true; - if(this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh) - return true; - return false; - }, - - /** - * Update position of the ceiling label - * - * @returns {undefined} - */ - updateCeilLab: function() - { - this.translateFn(this.scope.rzSliderCeil, this.ceilLab); - this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw); - this.getWidth(this.ceilLab); - }, - - /** - * Update position of the floor label - * - * @returns {undefined} - */ - updateFloorLab: function() - { - this.translateFn(this.scope.rzSliderFloor, this.flrLab); - this.getWidth(this.flrLab); - }, - - /** - * Call the onStart callback if defined - * - * @returns {undefined} - */ - callOnStart: function() { - if(this.scope.rzSliderOnStart) { - var self = this; - $timeout(function() { - self.scope.rzSliderOnStart(); - }); - } - }, - - /** - * Call the onChange callback if defined - * - * @returns {undefined} - */ - callOnChange: function() { - if(this.scope.rzSliderOnChange) { - var self = this; - $timeout(function() { - self.scope.rzSliderOnChange(); - }); - } - }, - - /** - * Call the onEnd callback if defined - * - * @returns {undefined} - */ - callOnEnd: function() { - if(this.scope.rzSliderOnEnd) { - var self = this; - $timeout(function() { - self.scope.rzSliderOnEnd(); - }); - } - }, - - /** - * Update slider handles and label positions - * - * @param {string} which - * @param {number} newOffset - */ - updateHandles: function(which, newOffset) - { - if(which === 'rzSliderModel') - { - this.updateLowHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); - - if(this.range) - { - this.updateCmbLabel(); - } - return; - } + .factory('RzSlider', function($timeout, $document, $window, defaultOptions, throttle) { + 'use strict'; - if(which === 'rzSliderHigh') - { - this.updateHighHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); + /** + * 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; + + /** + * 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, + offset: 0, + lowDist: 0, + highDist: 0 + }; + + /** + * Half of the width of the slider handles + * + * @type {number} + */ + this.handleHalfWidth = 0; + + /** + * Maximum left the slider handle can have + * + * @type {number} + */ + this.maxLeft = 0; + + /** + * Precision + * + * @type {number} + */ + this.precision = 0; + + /** + * Step + * + * @type {number} + */ + this.step = 0; + + /** + * 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; + + /** + * Set to true if init method already executed + * + * @type {boolean} + */ + this.initHasRun = false; + + + /** If the slider events are already bound to the slider + * + * @type {boolean} + */ + this.eventsBound = false; + + // 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(); + }; - if(this.range) - { + // Add instance methods + Slider.prototype = { + + /** + * Initialize slider + * + * @returns {undefined} + */ + init: function() { + var thrLow, thrHigh, + calcDimFn = angular.bind(this, this.calcViewDimensions), + self = this; + + this.applyOptions(); + this.initElemHandles(); + this.addAccessibility(); + this.manageEventsBindings(); + this.setDisabledState(); + this.calcViewDimensions(); + this.setMinAndMax(); + + $timeout(function() { + self.updateCeilLab(); + self.updateFloorLab(); + self.initHandles(); + self.bindEvents(); + }); + + // 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 = throttle(function() { + self.setMinAndMax(); + self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); + self.updateSelectionBar(); + self.updateTicksScale(); + + if (self.range) { + self.updateCmbLabel(); + } + + }, self.interval); + + thrHigh = throttle(function() { + self.setMinAndMax(); + self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); + self.updateSelectionBar(); + self.updateTicksScale(); + self.updateCmbLabel(); + }, self.interval); + + this.scope.$on('rzSliderForceRender', function() { + self.resetLabelsValue(); + thrLow(); + if (self.range) { + thrHigh(); + } + self.resetSlider(); + }); + + // Watchers + this.scope.$watch('rzSliderModel', function(newValue, oldValue) { + if (newValue === oldValue) + return; + thrLow(); + }); + + this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { + if (newValue === oldValue) + return; + thrHigh(); + }); + + this.scope.$watch('rzSliderOptions', function(newValue, oldValue) { + if (newValue === oldValue) + return; + self.applyOptions(); + self.resetSlider(); + }, true); + + this.scope.$on('$destroy', function() { + self.unbindEvents(); + angular.element($window).off('resize', calcDimFn); + }); + }, + + /** + * Read the user options and apply them to the slider model + */ + applyOptions: function() { + var userOpts = this.scope.rzSliderOptions; + this.options = {}; + for (var option_name in defaultOptions) { + if (!userOpts || userOpts[option_name] === undefined) + this.options[option_name] = defaultOptions[option_name]; + else + this.options[option_name] = userOpts[option_name]; + } + this.options.draggableRange = this.range && this.options.draggableRange; + this.options.showTicks = this.options.showTicks || this.options.showTicksValues; + if (this.options.translate) + this.customTrFn = this.options.translate; + else + this.customTrFn = function(value) { + return String(value); + }; + }, + + /** + * Resets slider + * + * @returns {undefined} + */ + resetSlider: function() { + this.setMinAndMax(); + this.updateCeilLab(); + this.updateFloorLab(); + this.manageEventsBindings(); + this.setDisabledState(); + this.calcViewDimensions(); + }, + + /** + * Manage the events bindings based on readOnly and disabled options + * + * @returns {undefined} + */ + manageEventsBindings: function() { + if ((this.options.disabled || this.options.readOnly) && this.eventsBound) + this.unbindEvents(); + else if ((!this.options.disabled || !this.options.readOnly) && !this.eventsBound) + 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); + } + if (this.options.disabled && this.eventsBound) + this.unbindEvents(); + else if (!this.options.disabled && !this.eventsBound) + this.bindEvents(); + }, + + /** + * 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.valueToOffset(this.scope.rzSliderModel)); + + /* + 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.valueToOffset(this.scope.rzSliderHigh)); + this.updateSelectionBar(); + if (this.range) + this.updateCmbLabel(); + + this.updateTicksScale(); + }, + + /** + * Translate value to human readable format + * + * @param {number|string} value + * @param {jqLite} label + * @param {boolean} [useCustomTr] + * @returns {undefined} + */ + translateFn: function(value, label, useCustomTr) { + useCustomTr = useCustomTr === undefined ? true : useCustomTr; + + var valStr = String((useCustomTr ? this.customTrFn(value) : value)), + getWidth = false; + + if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) { + getWidth = true; + label.rzsv = valStr; + } + + label.text(valStr); + + // Update width only when length of the label have changed + if (getWidth) { + this.getWidth(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.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel); + if (this.range) + this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); + + this.minValue = +this.options.floor; + if (this.scope.rzSliderModel < this.minValue) + this.scope.rzSliderModel = this.minValue; + if (this.range && this.scope.rzSliderHigh < this.minValue) + this.scope.rzSliderHigh = this.minValue; + + if (this.options.ceil) { + this.maxValue = +this.options.ceil; + if (this.scope.rzSliderModel > this.maxValue) + this.scope.rzSliderModel = this.maxValue; + if (this.range && this.scope.rzSliderHigh > this.maxValue) + this.scope.rzSliderHigh = this.maxValue; + } + else + this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; + + this.valueRange = this.maxValue - this.minValue; + }, + + /** + * 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 offset cache properties + this.selBar.rzsl = 0; + this.minH.rzsl = 0; + this.maxH.rzsl = 0; + this.flrLab.rzsl = 0; + this.ceilLab.rzsl = 0; + this.minLab.rzsl = 0; + this.maxLab.rzsl = 0; + this.cmbLab.rzsl = 0; + + // Hide limit labels + if (this.options.hideLimitLabels) { + this.flrLab.rzAlwaysHide = true; + this.ceilLab.rzAlwaysHide = true; + this.hideEl(this.flrLab); + this.hideEl(this.ceilLab); + } + + if (this.options.showTicksValues) { + this.flrLab.rzAlwaysHide = true; + this.ceilLab.rzAlwaysHide = true; + this.minLab.rzAlwaysHide = true; + this.maxLab.rzAlwaysHide = true; + this.cmbLab.rzAlwaysHide = true; + this.hideEl(this.flrLab); + this.hideEl(this.ceilLab); + this.hideEl(this.minLab); + this.hideEl(this.maxLab); + this.hideEl(this.cmbLab); + } + + // Remove stuff not needed in single slider + if (this.range === false) { + this.cmbLab.remove(); + this.maxLab.remove(); + + // Hide max handle + this.maxH.rzAlwaysHide = true; + this.maxH[0].style.zIndex = '-1000'; + this.hideEl(this.maxH); + } + + // Show selection bar for single slider or not + if (this.range === false && this.options.showSelectionBar === false) { + this.maxH.remove(); + this.selBar.remove(); + } + + // If using draggable range, use appropriate cursor for this.selBar. + if (this.options.draggableRange) { + this.selBar.css('cursor', 'move'); + this.selBar.addClass('rz-draggable'); + } + }, + + /** + * Adds accessibility atributes + * + * Run only once during initialization + * + * @returns {undefined} + */ + addAccessibility: function() { + this.sliderElem.attr("role", "slider"); + }, + + /** + * 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.getWidth(this.minH); + + this.handleHalfWidth = handleWidth / 2; + this.barWidth = this.getWidth(this.fullBar); + + this.maxLeft = this.barWidth - handleWidth; + + this.getWidth(this.sliderElem); + this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left; + + if (this.initHasRun) { + this.updateFloorLab(); + this.updateCeilLab(); + this.initHandles(); + } + }, + + /** + * Update the ticks position + * + * @returns {undefined} + */ + updateTicksScale: function() { + if (!this.options.showTicks) return; + if (!this.step) return; //if step is 0, the following loop will be endless. + + var positions = '', + ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1; + for (var i = 0; i < ticksCount; i++) { + var value = this.roundStep(this.minValue + i * this.step); + var selectedClass = this.isTickSelected(value) ? 'selected' : ''; + positions += '
      • '; + if (this.options.showTicksValues) + positions += '' + this.getDisplayValue(value) + ''; + positions += '
      • '; + } + this.ticks.html(positions); + }, + + isTickSelected: function(value) { + if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel) + return true; + if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh) + return true; + return false; + }, + + /** + * Update position of the ceiling label + * + * @returns {undefined} + */ + updateCeilLab: function() { + this.translateFn(this.maxValue, this.ceilLab); + this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw); + this.getWidth(this.ceilLab); + }, + + /** + * Update position of the floor label + * + * @returns {undefined} + */ + updateFloorLab: function() { + this.translateFn(this.minValue, this.flrLab); + this.getWidth(this.flrLab); + }, + + /** + * Call the onStart callback if defined + * + * @returns {undefined} + */ + callOnStart: function() { + if (this.options.onStart) { + var self = this; + $timeout(function() { + self.options.onStart(); + }); + } + }, + + /** + * Call the onChange callback if defined + * + * @returns {undefined} + */ + callOnChange: function() { + if (this.options.onChange) { + var self = this; + $timeout(function() { + self.options.onChange(); + }); + } + }, + + /** + * Call the onEnd callback if defined + * + * @returns {undefined} + */ + callOnEnd: function() { + if (this.options.onEnd) { + var self = this; + $timeout(function() { + self.options.onEnd(); + }); + } + }, + + /** + * Update slider handles and label positions + * + * @param {string} which + * @param {number} newOffset + */ + updateHandles: function(which, newOffset) { + if (which === 'rzSliderModel') { + this.updateLowHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); + + if (this.range) { + this.updateCmbLabel(); + } + return; + } + + if (which === 'rzSliderHigh') { + this.updateHighHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); + + if (this.range) { + this.updateCmbLabel(); + } + return; + } + + // Update both + this.updateLowHandle(newOffset); + this.updateHighHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); this.updateCmbLabel(); - } - return; - } - - // Update both - this.updateLowHandle(newOffset); - this.updateHighHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); - this.updateCmbLabel(); - }, - - /** - * Update low slider handle position and label - * - * @param {number} newOffset - * @returns {undefined} - */ - updateLowHandle: function(newOffset) - { - this.setLeft(this.minH, newOffset); - this.translateFn(this.scope.rzSliderModel, this.minLab); - this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth); - - this.shFloorCeil(); - }, - - /** - * Update high slider handle position and label - * - * @param {number} newOffset - * @returns {undefined} - */ - updateHighHandle: function(newOffset) - { - this.setLeft(this.maxH, newOffset); - this.translateFn(this.scope.rzSliderHigh, this.maxLab); - this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth); + }, + + /** + * Update low slider handle position and label + * + * @param {number} newOffset + * @returns {undefined} + */ + updateLowHandle: function(newOffset) { + this.setLeft(this.minH, newOffset); + this.translateFn(this.scope.rzSliderModel, this.minLab); + this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth); + + this.shFloorCeil(); + }, + + /** + * Update high slider handle position and label + * + * @param {number} newOffset + * @returns {undefined} + */ + updateHighHandle: function(newOffset) { + this.setLeft(this.maxH, newOffset); + this.translateFn(this.scope.rzSliderHigh, this.maxLab); + this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth); + + this.shFloorCeil(); + }, + + /** + * Show / hide floor / ceiling label + * + * @returns {undefined} + */ + shFloorCeil: function() { + var flHidden = false, clHidden = false; + + if (this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5) { + flHidden = true; + this.hideEl(this.flrLab); + } + else { + flHidden = false; + this.showEl(this.flrLab); + } + + if (this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) { + clHidden = true; + this.hideEl(this.ceilLab); + } + else { + clHidden = false; + this.showEl(this.ceilLab); + } + + if (this.range) { + if (this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) { + this.hideEl(this.ceilLab); + } + else if (!clHidden) { + this.showEl(this.ceilLab); + } + + // Hide or show floor label + if (this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth) { + this.hideEl(this.flrLab); + } + else if (!flHidden) { + this.showEl(this.flrLab); + } + } + }, + + /** + * Update slider selection bar, combined label and range label + * + * @returns {undefined} + */ + updateSelectionBar: function() { + this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth); + this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0); + }, + + /** + * Update combined label position and value + * + * @returns {undefined} + */ + updateCmbLabel: function() { + var lowTr, highTr; + + if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) { + lowTr = this.getDisplayValue(this.scope.rzSliderModel); + highTr = this.getDisplayValue(this.scope.rzSliderHigh); + + this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false); + this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2); + this.hideEl(this.minLab); + this.hideEl(this.maxLab); + this.showEl(this.cmbLab); + } + else { + this.showEl(this.maxLab); + this.showEl(this.minLab); + this.hideEl(this.cmbLab); + } + }, + + /** + * Return the translated value if a translate function is provided else the original value + * @param value + * @returns {*} + */ + getDisplayValue: function(value) { + return this.customTrFn(value); + }, + + /** + * Round value to step and precision + * + * @param {number} value + * @returns {number} + */ + roundStep: function(value) { + var step = this.step, + remainder = +((value - this.minValue) % step).toFixed(3), + steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder; + + steppedValue = steppedValue.toFixed(this.precision); + return +steppedValue; + }, + + /** + * Hide element + * + * @param element + * @returns {jqLite} The jqLite wrapped DOM element + */ + hideEl: function(element) { + return element.css({opacity: 0}); + }, + + /** + * Show element + * + * @param element The jqLite wrapped DOM element + * @returns {jqLite} The jqLite + */ + showEl: function(element) { + if (!!element.rzAlwaysHide) { + return element; + } + + return element.css({opacity: 1}); + }, + + /** + * Set element left offset + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @param {number} left + * @returns {number} + */ + setLeft: function(elem, left) { + elem.rzsl = left; + elem.css({left: left + 'px'}); + return left; + }, + + /** + * Get element width + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @returns {number} + */ + getWidth: function(elem) { + var val = elem[0].getBoundingClientRect(); + elem.rzsw = val.right - val.left; + return elem.rzsw; + }, + + /** + * Set element width + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @param {number} width + * @returns {number} + */ + setWidth: function(elem, width) { + elem.rzsw = width; + elem.css({width: width + 'px'}); + return width; + }, + + /** + * Translate value to pixel offset + * + * @param {number} val + * @returns {number} + */ + valueToOffset: function(val) { + return (val - this.minValue) * this.maxLeft / this.valueRange || 0; + }, + + /** + * Translate offset to model value + * + * @param {number} offset + * @returns {number} + */ + offsetToValue: function(offset) { + return (offset / this.maxLeft) * this.valueRange + this.minValue; + }, + + // Events + + /** + * Get the X-coordinate of an event + * + * @param {Object} event The event + * @returns {number} + */ + getEventX: function(event) { + /* http://stackoverflow.com/a/12336075/282882 */ + //noinspection JSLint + if ('clientX' in event) { + return event.clientX; + } + + return event.originalEvent === undefined ? + event.touches[0].clientX + : event.originalEvent.touches[0].clientX; + }, + + /** + * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; + return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; + }, + + /** + * Bind mouse and touch events to slider handles + * + * @returns {undefined} + */ + bindEvents: function() { + if (this.options.readOnly || this.options.disabled) return; + var barTracking, barStart, barMove; + + if (this.options.draggableRange) { + barTracking = 'rzSliderDrag'; + barStart = this.onDragStart; + barMove = this.onDragMove; + } + else { + barTracking = 'rzSliderModel'; + barStart = this.onStart; + barMove = this.onMove; + } + + this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); + if (this.range) { + this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); + } + this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); + this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); + this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); + this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); + this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); + this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks)); + + this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); + if (this.range) { + this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); + } + this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); + this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); + this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); + this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); + this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); + this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); + + this.eventsBound = true; + }, + + /** + * 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(); + this.eventsBound = false; + }, + + /** + * 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(); + + if (this.tracking !== '') { + return; + } + + // 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 ? 'rzSliderModel' : 'rzSliderHigh'; + } + + pointer.addClass('rz-active'); + + 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.one(eventNames.endEvent, ehEnd); + this.callOnStart(); + }, + + /** + * onMove event handler + * + * @param {jqLite} pointer + * @param {Event} event The event + * @returns {undefined} + */ + onMove: function(pointer, event) { + var eventX = this.getEventX(event), + sliderLO, newOffset, newValue; + + sliderLO = this.sliderElem.rzsl; + newOffset = eventX - sliderLO - this.handleHalfWidth; + + if (newOffset <= 0) { + if (pointer.rzsl === 0) + return; + newValue = this.minValue; + newOffset = 0; + } + else if (newOffset >= this.maxLeft) { + if (pointer.rzsl === this.maxLeft) + return; + newValue = this.maxValue; + newOffset = this.maxLeft; + } + else { + newValue = this.offsetToValue(newOffset); + newValue = this.roundStep(newValue); + newOffset = this.valueToOffset(newValue); + } + this.positionTrackingHandle(newValue, newOffset); + }, + + /** + * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; + this.dragging = { + active: true, + value: this.offsetToValue(offset), + difference: this.scope.rzSliderHigh - this.scope.rzSliderModel, + offset: offset, + lowDist: offset - this.minH.rzsl, + highDist: this.maxH.rzsl - offset + }; + this.minH.addClass('rz-active'); + this.maxH.addClass('rz-active'); - this.shFloorCeil(); - }, + this.onStart(pointer, ref, event); + }, + + /** + * onDragMove event handler + * + * Handles dragging of the middle bar. + * + * @param {jqLite} pointer + * @param {Event} event The event + * @returns {undefined} + */ + onDragMove: function(pointer, event) { + var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth, + newMinOffset, newMaxOffset, + newMinValue, newMaxValue; + + if (newOffset <= this.dragging.lowDist) { + if (pointer.rzsl === this.dragging.lowDist) { + return; + } + newMinValue = this.minValue; + newMinOffset = 0; + newMaxValue = this.dragging.difference; + newMaxOffset = this.valueToOffset(newMaxValue); + } + else if (newOffset >= this.maxLeft - this.dragging.highDist) { + if (pointer.rzsl === this.dragging.highDist) { + return; + } + newMaxValue = this.maxValue; + newMaxOffset = this.maxLeft; + newMinValue = this.maxValue - this.dragging.difference; + newMinOffset = this.valueToOffset(newMinValue); + } + else { + newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist); + newMinValue = this.roundStep(newMinValue); + newMinOffset = this.valueToOffset(newMinValue); + newMaxValue = newMinValue + this.dragging.difference; + newMaxOffset = this.valueToOffset(newMaxValue); + } + + this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset); + }, + + /** + * Set the new value and offset for the entire bar + * + * @param {number} newMinValue the new minimum value + * @param {number} newMaxValue the new maximum value + * @param {number} newMinOffset the new minimum offset + * @param {number} newMaxOffset the new maximum offset + */ + positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) { + this.scope.rzSliderModel = newMinValue; + this.scope.rzSliderHigh = newMaxValue; + this.updateHandles('rzSliderModel', newMinOffset); + this.updateHandles('rzSliderHigh', newMaxOffset); + this.scope.$apply(); + this.callOnChange(); + }, + + /** + * Set the new value and offset to the current tracking handle + * + * @param {number} newValue new model value + * @param {number} newOffset new offset value + */ + positionTrackingHandle: function(newValue, newOffset) { + if (this.range) { + /* This is to check if we need to switch the min and max handles*/ + if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh) { + this.scope[this.tracking] = this.scope.rzSliderHigh; + this.updateHandles(this.tracking, this.maxH.rzsl); + this.tracking = 'rzSliderHigh'; + this.minH.removeClass('rz-active'); + this.maxH.addClass('rz-active'); + /* We need to apply here because we are not sure that we will enter the next block */ + this.scope.$apply(); + this.callOnChange(); + } + else if (this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) { + this.scope[this.tracking] = this.scope.rzSliderModel; + this.updateHandles(this.tracking, this.minH.rzsl); + this.tracking = 'rzSliderModel'; + this.maxH.removeClass('rz-active'); + this.minH.addClass('rz-active'); + /* We need to apply here because we are not sure that we will enter the next block */ + this.scope.$apply(); + this.callOnChange(); + } + } + + if (this.scope[this.tracking] !== newValue) { + this.scope[this.tracking] = newValue; + this.updateHandles(this.tracking, newOffset); + this.scope.$apply(); + this.callOnChange(); + } + }, + + /** + * onEnd event handler + * + * @param {Event} event The event + * @param {Function} ehMove The the bound move event handler + * @returns {undefined} + */ + onEnd: function(ehMove, event) { + var moveEventName = this.getEventNames(event).moveEvent; - /** - * Show / hide floor / ceiling label - * - * @returns {undefined} - */ - shFloorCeil: function() - { - var flHidden = false, clHidden = false; - - if(this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5) - { - flHidden = true; - this.hideEl(this.flrLab); - } - else - { - flHidden = false; - this.showEl(this.flrLab); - } - - if(this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) - { - clHidden = true; - this.hideEl(this.ceilLab); - } - else - { - clHidden = false; - this.showEl(this.ceilLab); - } - - if(this.range) - { - if(this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) - { - this.hideEl(this.ceilLab); - } - else if( ! clHidden) - { - this.showEl(this.ceilLab); - } + this.minH.removeClass('rz-active'); + this.maxH.removeClass('rz-active'); - // Hide or show floor label - if(this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth) - { - this.hideEl(this.flrLab); + $document.off(moveEventName, ehMove); + + this.scope.$emit('slideEnded'); + this.tracking = ''; + + this.dragging.active = false; + this.callOnEnd(); + }, + + /** + * 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 (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) { + eventNames.moveEvent = 'touchmove'; + eventNames.endEvent = 'touchend'; + } + else { + eventNames.moveEvent = 'mousemove'; + eventNames.endEvent = 'mouseup'; + } + + return eventNames; } - else if( ! flHidden) - { - this.showEl(this.flrLab); - } - } - }, - - /** - * Update slider selection bar, combined label and range label - * - * @returns {undefined} - */ - updateSelectionBar: function() - { - this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth); - this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0); - }, - - /** - * Update combined label position and value - * - * @returns {undefined} - */ - updateCmbLabel: function() - { - var lowTr, highTr; - - if(this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) - { - lowTr = this.getDisplayValue(this.scope.rzSliderModel); - highTr = this.getDisplayValue(this.scope.rzSliderHigh); - - this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false); - this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2); - this.hideEl(this.minLab); - this.hideEl(this.maxLab); - this.showEl(this.cmbLab); - } - else - { - this.showEl(this.maxLab); - this.showEl(this.minLab); - this.hideEl(this.cmbLab); - } - }, - - /** - * Return the translated value if a translate function is provided else the original value - * @param value - * @returns {*} - */ - getDisplayValue: function(value) { - return this.customTrFn ? this.customTrFn(value): value; - }, - - /** - * Round value to step and precision - * - * @param {number} value - * @returns {number} - */ - roundStep: function(value) - { - var step = this.step, - remainder = +((value - this.minValue) % step).toFixed(3), - steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder; - - steppedValue = steppedValue.toFixed(this.precision); - return +steppedValue; - }, - - /** - * Hide element - * - * @param element - * @returns {jqLite} The jqLite wrapped DOM element - */ - hideEl: function (element) - { - return element.css({opacity: 0}); - }, - - /** - * Show element - * - * @param element The jqLite wrapped DOM element - * @returns {jqLite} The jqLite - */ - showEl: function (element) - { - if(!!element.rzAlwaysHide) { return element; } - - return element.css({opacity: 1}); - }, - - /** - * Set element left offset - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @param {number} left - * @returns {number} - */ - setLeft: function (elem, left) - { - elem.rzsl = left; - elem.css({left: left + 'px'}); - return left; - }, - - /** - * Get element width - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @returns {number} - */ - getWidth: function(elem) - { - var val = elem[0].getBoundingClientRect(); - elem.rzsw = val.right - val.left; - return elem.rzsw; - }, - - /** - * Set element width - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @param {number} width - * @returns {number} - */ - setWidth: function(elem, width) - { - elem.rzsw = width; - elem.css({width: width + 'px'}); - return width; - }, - - /** - * Translate value to pixel offset - * - * @param {number} val - * @returns {number} - */ - valueToOffset: function(val) - { - return (val - this.minValue) * this.maxLeft / this.valueRange || 0; - }, - - /** - * Translate offset to model value - * - * @param {number} offset - * @returns {number} - */ - offsetToValue: function(offset) - { - return (offset / this.maxLeft) * this.valueRange + this.minValue; - }, - - // Events - - /** - * Get the X-coordinate of an event - * - * @param {Object} event The event - * @returns {number} - */ - getEventX: function(event) - { - /* http://stackoverflow.com/a/12336075/282882 */ - //noinspection JSLint - if('clientX' in event) - { - return event.clientX; - } - - return event.originalEvent === undefined ? - event.touches[0].clientX - : event.originalEvent.touches[0].clientX; - }, - - /** - * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; - return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; - }, - - /** - * Bind mouse and touch events to slider handles - * - * @returns {undefined} - */ - bindEvents: function() - { - if(this.presentOnly || this.disabled) return; - var barTracking, barStart, barMove; - - if (this.dragRange) - { - barTracking = 'rzSliderDrag'; - barStart = this.onDragStart; - barMove = this.onDragMove; - } - else - { - barTracking = 'rzSliderModel'; - barStart = this.onStart; - barMove = this.onMove; - } - - this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); - if(this.range) { this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); } - this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); - this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); - this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); - this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); - this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); - this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks)); - - this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); - if(this.range) { this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); } - this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); - this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); - this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); - this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); - this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); - this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); - }, - - /** - * 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(); - - if(this.tracking !== '') { return; } - - // 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 ? 'rzSliderModel' : 'rzSliderHigh'; - } - - pointer.addClass('rz-active'); - - 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.one(eventNames.endEvent, ehEnd); - this.callOnStart(); - }, - - /** - * onMove event handler - * - * @param {jqLite} pointer - * @param {Event} event The event - * @returns {undefined} - */ - onMove: function (pointer, event) - { - var eventX = this.getEventX(event), - sliderLO, newOffset, newValue; - - sliderLO = this.sliderElem.rzsl; - newOffset = eventX - sliderLO - this.handleHalfWidth; - - if(newOffset <= 0) - { - if(pointer.rzsl === 0) - return; - newValue = this.minValue; - newOffset = 0; - } - else if(newOffset >= this.maxLeft) - { - if(pointer.rzsl === this.maxLeft) - return; - newValue = this.maxValue; - newOffset = this.maxLeft; - } - else { - newValue = this.offsetToValue(newOffset); - newValue = this.roundStep(newValue); - newOffset = this.valueToOffset(newValue); - } - this.positionTrackingHandle(newValue, newOffset); - }, - - /** - * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; - this.dragging = { - active: true, - value: this.offsetToValue(offset), - difference: this.scope.rzSliderHigh - this.scope.rzSliderModel, - offset: offset, - lowDist: offset - this.minH.rzsl, - highDist: this.maxH.rzsl - offset }; - this.minH.addClass('rz-active'); - this.maxH.addClass('rz-active'); - - this.onStart(pointer, ref, event); - }, - - /** - * onDragMove event handler - * - * Handles dragging of the middle bar. - * - * @param {jqLite} pointer - * @param {Event} event The event - * @returns {undefined} - */ - onDragMove: function(pointer, event) - { - var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth, - newMinOffset, newMaxOffset, - newMinValue, newMaxValue; - - if (newOffset <= this.dragging.lowDist) - { - if (pointer.rzsl === this.dragging.lowDist) { return; } - newMinValue = this.minValue; - newMinOffset = 0; - newMaxValue = this.dragging.difference; - newMaxOffset = this.valueToOffset(newMaxValue); - } - else if (newOffset >= this.maxLeft - this.dragging.highDist) - { - if (pointer.rzsl === this.dragging.highDist) { return; } - newMaxValue = this.maxValue; - newMaxOffset = this.maxLeft; - newMinValue = this.maxValue - this.dragging.difference; - newMinOffset = this.valueToOffset(newMinValue); - } - else - { - newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist); - newMinValue = this.roundStep(newMinValue); - newMinOffset = this.valueToOffset(newMinValue); - newMaxValue = newMinValue + this.dragging.difference; - newMaxOffset = this.valueToOffset(newMaxValue); - } - - this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset); - }, - - /** - * Set the new value and offset for the entire bar - * - * @param {number} newMinValue the new minimum value - * @param {number} newMaxValue the new maximum value - * @param {number} newMinOffset the new minimum offset - * @param {number} newMaxOffset the new maximum offset - */ - positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) - { - this.scope.rzSliderModel = newMinValue; - this.scope.rzSliderHigh = newMaxValue; - this.updateHandles('rzSliderModel', newMinOffset); - this.updateHandles('rzSliderHigh', newMaxOffset); - this.scope.$apply(); - this.callOnChange(); - }, - /** - * Set the new value and offset to the current tracking handle - * - * @param {number} newValue new model value - * @param {number} newOffset new offset value - */ - positionTrackingHandle: function(newValue, newOffset) - { - if(this.range) - { - /* This is to check if we need to switch the min and max handles*/ - if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh) - { - this.scope[this.tracking] = this.scope.rzSliderHigh; - this.updateHandles(this.tracking, this.maxH.rzsl); - this.tracking = 'rzSliderHigh'; - this.minH.removeClass('rz-active'); - this.maxH.addClass('rz-active'); - /* We need to apply here because we are not sure that we will enter the next block */ - this.scope.$apply(); - this.callOnChange(); + return Slider; + }) + + .directive('rzslider', function(RzSlider) { + 'use strict'; + + return { + restrict: 'E', + 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) { + return new RzSlider(scope, elem); } - else if(this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) - { - this.scope[this.tracking] = this.scope.rzSliderModel; - this.updateHandles(this.tracking, this.minH.rzsl); - this.tracking = 'rzSliderModel'; - this.maxH.removeClass('rz-active'); - this.minH.addClass('rz-active'); - /* We need to apply here because we are not sure that we will enter the next block */ - this.scope.$apply(); - this.callOnChange(); - } - } - - if(this.scope[this.tracking] !== newValue) - { - this.scope[this.tracking] = newValue; - this.updateHandles(this.tracking, newOffset); - this.scope.$apply(); - this.callOnChange(); - } - }, - - /** - * onEnd event handler - * - * @param {Event} event The event - * @param {Function} ehMove The the bound move event handler - * @returns {undefined} - */ - onEnd: function(ehMove, event) - { - var moveEventName = this.getEventNames(event).moveEvent; - - this.minH.removeClass('rz-active'); - this.maxH.removeClass('rz-active'); - - $document.off(moveEventName, ehMove); - - this.scope.$emit('slideEnded'); - this.tracking = ''; - - this.dragging.active = false; - this.callOnEnd(); - }, - - /** - * 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(event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) - { - eventNames.moveEvent = 'touchmove'; - eventNames.endEvent = 'touchend'; - } - else - { - eventNames.moveEvent = 'mousemove'; - eventNames.endEvent = 'mouseup'; - } - - return eventNames; - } - }; - - return Slider; -}) - -.directive('rzslider', function(RzSlider) -{ - 'use strict'; - - return { - restrict: 'E', - scope: { - rzSliderFloor: '=?', - rzSliderCeil: '=?', - rzSliderStep: '@', - rzSliderPrecision: '@', - rzSliderModel: '=?', - rzSliderHigh: '=?', - rzSliderDraggable: '@', - rzSliderTranslate: '&', - rzSliderHideLimitLabels: '=?', - rzSliderAlwaysShowBar: '=?', - rzSliderPresentOnly: '@', - rzSliderOnStart: '&', - rzSliderOnChange: '&', - rzSliderOnEnd: '&', - rzSliderShowTicks: '=?', - rzSliderShowTicksValue: '=?', - rzSliderDisabled: '=?', - rzSliderInterval: '=?', - }, - - /** - * 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, attr) - { - return new RzSlider(scope, elem, attr); - } - }; -}); + }; + }); // IDE assist -/** - * @name ngScope - * - * @property {number} rzSliderModel - * @property {number} rzSliderHigh - * @property {number} rzSliderCeil - */ + /** + * @name ngScope + * + * @property {number} rzSliderModel + * @property {number} rzSliderHigh + * @property {Object} rzSliderOptions + */ -/** - * @name jqLite - * - * @property {number|undefined} rzsl rzslider label left offset - * @property {number|undefined} rzsw rzslider element width - * @property {string|undefined} rzsv rzslider label value/text - * @property {Function} css - * @property {Function} text - */ + /** + * @name jqLite + * + * @property {number|undefined} rzsl rzslider label left offset + * @property {number|undefined} rzsw rzslider element width + * @property {string|undefined} rzsv rzslider label value/text + * @property {Function} css + * @property {Function} text + */ -/** - * @name Event - * @property {Array} touches - * @property {Event} originalEvent - */ + /** + * @name Event + * @property {Array} touches + * @property {Event} originalEvent + */ -/** - * @name ThrottleOptions - * - * @property {boolean} leading - * @property {boolean} trailing - */ + /** + * @name ThrottleOptions + * + * @property {boolean} leading + * @property {boolean} trailing + */ /*templateReplacement*/ From a3aacff34a1747ddb842251aec99a512beea4f37 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Fri, 23 Oct 2015 16:01:22 +0200 Subject: [PATCH 02/15] Improve the example page. Increase the updatability of the slider options. --- demo/demo.css | 11 +- demo/demo.js | 107 ++++++++++++++++++-- demo/index.html | 56 ++++++++++- demo/sliderModal.html | 29 ++++++ dist/rzslider.css | 6 +- dist/rzslider.js | 229 ++++++++++++++++++++---------------------- dist/rzslider.min.css | 2 +- dist/rzslider.min.js | 2 +- src/rzslider.js | 229 ++++++++++++++++++++---------------------- src/rzslider.less | 7 +- 10 files changed, 421 insertions(+), 257 deletions(-) create mode 100644 demo/sliderModal.html diff --git a/demo/demo.css b/demo/demo.css index 7c3752a..9bc2ac7 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -1,8 +1,13 @@ * { margin: 0; padding: 0; } -body { font-family: 'Open Sans', sans-serif; color: #1f2636; font-size: 14px; } +body { font-family: 'Open Sans', sans-serif; color: #1f2636; font-size: 14px; padding-bottom: 40px; } header { background: #0db9f0; color: #fff; margin: -40px; margin-bottom: 40px; text-align: center; padding: 40px 0; } h1 { font-weight: 300; } h2 {margin-bottom:10px;} .wrapper { background: #fff; padding: 40px; } -article { margin-bottom: 40px; } - +article { margin-bottom: 10px; } +.tab-pane{ + padding-top: 10px; +} +.field-title { + width: 100px; +} diff --git a/demo/demo.js b/demo/demo.js index 9f3011f..37bc8a9 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -1,6 +1,6 @@ -var app = angular.module('rzSliderDemo', ['rzModule']); +var app = angular.module('rzSliderDemo', ['rzModule', 'ui.bootstrap']); -app.controller('MainCtrl', function($scope, $timeout) { +app.controller('MainCtrl', function($scope, $rootScope, $timeout, $modal) { //Minimal slider config $scope.minSlider = { value: 10 @@ -142,8 +142,15 @@ app.controller('MainCtrl', function($scope, $timeout) { } }; + // Slider inside ng-show $scope.visible = false; - + $scope.slider_toggle = { + value: 5, + options: { + ceil: 10, + floor: 0 + } + }; $scope.toggle = function() { $scope.visible = !$scope.visible; $timeout(function() { @@ -151,11 +158,99 @@ app.controller('MainCtrl', function($scope, $timeout) { }); }; - $scope.slider_toggle = { - value: 5, + //Slider inside modal + $scope.percentages = { + normal: { + low: 15 + }, + range: { + low: 10, + high: 50 + } + }; + $scope.openModal = function() { + var modalInstance = $modal.open({ + templateUrl: 'sliderModal.html', + controller: function($scope, $modalInstance, values) { + $scope.percentages = JSON.parse(JSON.stringify(values)); //Copy of the object in order to keep original values in $scope.percentages in parent controller. + + + var formatToPercentage = function(value) { + return value + '%'; + }; + + $scope.percentages.normal.options = { + floor: 0, + ceil: 100, + translate: formatToPercentage, + showSelectionBar: true + }; + $scope.percentages.range.options = { + floor: 0, + ceil: 100, + translate: formatToPercentage + }; + $scope.ok = function() { + $modalInstance.close($scope.percentages); + }; + $scope.cancel = function() { + $modalInstance.dismiss(); + }; + }, + resolve: { + values: function() { + return $scope.percentages; + } + } + }); + modalInstance.result.then(function(percentages) { + $scope.percentages = percentages; + }); + modalInstance.rendered.then(function() { + $rootScope.$broadcast('rzSliderForceRender');//Force refresh sliders on render. Otherwise bullets are aligned at left side. + }); + }; + + + //Slider inside tabs + $scope.tabSliders = { + slider1: { + value: 100 + }, + slider2: { + value: 200 + } + }; + $scope.refreshSlider = function() { + $timeout(function() { + $scope.$broadcast('rzSliderForceRender'); + }); + }; + + + //Slider with draggable range + $scope.slider_all_options = { + minValue: 2, options: { + floor: 0, ceil: 10, - floor: 0 + step: 1, + precision: 0, + draggableRange: false, + showSelectionBar: false, + hideLimitLabels: false, + readOnly: false, + disabled: false, + showTicks: false, + showTicksValues: false } }; + $scope.toggleHighValue = function() { + if($scope.slider_all_options.maxValue != null) { + $scope.slider_all_options.maxValue = undefined; + } + else { + $scope.slider_all_options.maxValue = 8; + } + } }); diff --git a/demo/index.html b/demo/index.html index 71c7e9d..f8994eb 100644 --- a/demo/index.html +++ b/demo/index.html @@ -5,8 +5,10 @@ AngularJS Touch Slider - + + + @@ -142,11 +144,63 @@

        Toggle slider example

        +
        +

        Sliders inside a modal

        + Normal slider value: {{percentages.normal.low}}% +
        + Range slider values: {{percentages.range.low}}% and {{percentages.range.high}}% +
        + +
        + +
        +

        Sliders inside tabs

        +

        Price 1: {{tabSliders.slider1.value}}

        +

        Price 2: {{tabSliders.slider2.value}}

        + + + + + + + + +
        +
        +

        Slider with all options demo

        +
        +
        +
        + +
        +
        +
        +
        +
        +
        +
        +
        + +
        +
        +
        +
        +
        + +
        +
        + +
        + diff --git a/demo/sliderModal.html b/demo/sliderModal.html new file mode 100644 index 0000000..7384214 --- /dev/null +++ b/demo/sliderModal.html @@ -0,0 +1,29 @@ + diff --git a/dist/rzslider.css b/dist/rzslider.css index 6a92320..032fd97 100644 --- a/dist/rzslider.css +++ b/dist/rzslider.css @@ -16,7 +16,7 @@ rzslider { display: inline-block; width: 100%; height: 4px; - margin: 30px 0 15px 0; + margin: 35px 0 15px 0; vertical-align: middle; -webkit-user-select: none; -moz-user-select: none; @@ -55,6 +55,10 @@ rzslider .rz-bar-wrapper { box-sizing: border-box; } +rzslider .rz-bar-wrapper.rz-draggable { + cursor: move; +} + rzslider .rz-bar { left: 0; z-index: 1; diff --git a/dist/rzslider.js b/dist/rzslider.js index 89194ff..e4ae8c4 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -207,13 +207,6 @@ */ this.initHasRun = false; - - /** If the slider events are already bound to the slider - * - * @type {boolean} - */ - this.eventsBound = false; - // Slider DOM elements wrapped in jqLite this.fullBar = null; // The whole slider bar this.selBar = null; // Highlight between two handles @@ -245,6 +238,7 @@ this.applyOptions(); this.initElemHandles(); + this.manageElementsStyle(); this.addAccessibility(); this.manageEventsBindings(); this.setDisabledState(); @@ -307,7 +301,12 @@ this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { if (newValue === oldValue) return; - thrHigh(); + if (newValue != null) + thrHigh(); + if (self.range && newValue == null || !self.range && newValue != null) { + self.applyOptions(); + self.resetSlider(); + } }); this.scope.$watch('rzSliderOptions', function(newValue, oldValue) { @@ -335,6 +334,9 @@ else this.options[option_name] = userOpts[option_name]; } + 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.showTicks = this.options.showTicks || this.options.showTicksValues; if (this.options.translate) @@ -351,23 +353,117 @@ * @returns {undefined} */ resetSlider: function() { + this.manageElementsStyle(); this.setMinAndMax(); this.updateCeilLab(); this.updateFloorLab(); + this.unbindEvents(); this.manageEventsBindings(); this.setDisabledState(); this.calcViewDimensions(); }, + /** + * 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 offset cache properties + this.selBar.rzsl = 0; + this.minH.rzsl = 0; + this.maxH.rzsl = 0; + this.flrLab.rzsl = 0; + this.ceilLab.rzsl = 0; + this.minLab.rzsl = 0; + this.maxLab.rzsl = 0; + this.cmbLab.rzsl = 0; + }, + + /** Update each elements style based on options + * + */ + manageElementsStyle: function() { + + if (!this.range) + this.maxH.css('display', 'none'); + else + this.maxH.css('display', null); + + this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels); + this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels); + this.alwaysHide(this.minLab, this.options.showTicksValues); + this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range); + this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range); + this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar); + + if (!this.options.showTicks) + this.ticks.html(''); + + if (this.options.draggableRange) + this.selBar.addClass('rz-draggable'); + else + this.selBar.removeClass('rz-draggable'); + }, + + 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.eventsBound) + if (this.options.disabled || this.options.readOnly) this.unbindEvents(); - else if ((!this.options.disabled || !this.options.readOnly) && !this.eventsBound) + else if (!this.options.disabled || !this.options.readOnly) this.bindEvents(); }, @@ -383,10 +479,6 @@ else { this.sliderElem.attr('disabled', null); } - if (this.options.disabled && this.eventsBound) - this.unbindEvents(); - else if (!this.options.disabled && !this.eventsBound) - this.bindEvents(); }, /** @@ -462,14 +554,14 @@ if (this.range) this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); - this.minValue = +this.options.floor; + this.minValue = this.roundStep(+this.options.floor); if (this.scope.rzSliderModel < this.minValue) this.scope.rzSliderModel = this.minValue; if (this.range && this.scope.rzSliderHigh < this.minValue) this.scope.rzSliderHigh = this.minValue; if (this.options.ceil) { - this.maxValue = +this.options.ceil; + this.maxValue = this.roundStep(+this.options.ceil); if (this.scope.rzSliderModel > this.maxValue) this.scope.rzSliderModel = this.maxValue; if (this.range && this.scope.rzSliderHigh > this.maxValue) @@ -481,108 +573,6 @@ this.valueRange = this.maxValue - this.minValue; }, - /** - * 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 offset cache properties - this.selBar.rzsl = 0; - this.minH.rzsl = 0; - this.maxH.rzsl = 0; - this.flrLab.rzsl = 0; - this.ceilLab.rzsl = 0; - this.minLab.rzsl = 0; - this.maxLab.rzsl = 0; - this.cmbLab.rzsl = 0; - - // Hide limit labels - if (this.options.hideLimitLabels) { - this.flrLab.rzAlwaysHide = true; - this.ceilLab.rzAlwaysHide = true; - this.hideEl(this.flrLab); - this.hideEl(this.ceilLab); - } - - if (this.options.showTicksValues) { - this.flrLab.rzAlwaysHide = true; - this.ceilLab.rzAlwaysHide = true; - this.minLab.rzAlwaysHide = true; - this.maxLab.rzAlwaysHide = true; - this.cmbLab.rzAlwaysHide = true; - this.hideEl(this.flrLab); - this.hideEl(this.ceilLab); - this.hideEl(this.minLab); - this.hideEl(this.maxLab); - this.hideEl(this.cmbLab); - } - - // Remove stuff not needed in single slider - if (this.range === false) { - this.cmbLab.remove(); - this.maxLab.remove(); - - // Hide max handle - this.maxH.rzAlwaysHide = true; - this.maxH[0].style.zIndex = '-1000'; - this.hideEl(this.maxH); - } - - // Show selection bar for single slider or not - if (this.range === false && this.options.showSelectionBar === false) { - this.maxH.remove(); - this.selBar.remove(); - } - - // If using draggable range, use appropriate cursor for this.selBar. - if (this.options.draggableRange) { - this.selBar.css('cursor', 'move'); - this.selBar.addClass('rz-draggable'); - } - }, - /** * Adds accessibility atributes * @@ -1037,8 +1027,6 @@ this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); - - this.eventsBound = true; }, /** @@ -1052,7 +1040,6 @@ this.fullBar.off(); this.selBar.off(); this.ticks.off(); - this.eventsBound = false; }, /** diff --git a/dist/rzslider.min.css b/dist/rzslider.min.css index 03c5704..534ebf9 100644 --- a/dist/rzslider.min.css +++ b/dist/rzslider.min.css @@ -1,2 +1,2 @@ /*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:30px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}rzslider[disabled]{cursor:not-allowed}rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}rzslider span{position:absolute;display:inline-block;white-space:nowrap}rzslider .rz-base{width:100%;height:100%;padding:0}rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}rzslider .rz-pointer:hover:after{background-color:#fff}rzslider .rz-pointer.rz-active:after{background-color:#451aff}rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}rzslider .rz-bubble.rz-selection{top:16px}rzslider .rz-bubble.rz-limit{color:#55637d}rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:flex;width:100%;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;justify-content:space-between}rzslider .rz-ticks .tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}rzslider .rz-ticks .tick.selected{background:#0db9f0}rzslider .rz-ticks .tick .tick-value{position:absolute;top:-30px;transform:translate(-50%,0)} \ No newline at end of file +rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:35px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}rzslider[disabled]{cursor:not-allowed}rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}rzslider span{position:absolute;display:inline-block;white-space:nowrap}rzslider .rz-base{width:100%;height:100%;padding:0}rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}rzslider .rz-bar-wrapper.rz-draggable{cursor:move}rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}rzslider .rz-pointer:hover:after{background-color:#fff}rzslider .rz-pointer.rz-active:after{background-color:#451aff}rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}rzslider .rz-bubble.rz-selection{top:16px}rzslider .rz-bubble.rz-limit{color:#55637d}rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:flex;width:100%;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;justify-content:space-between}rzslider .rz-ticks .tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}rzslider .rz-ticks .tick.selected{background:#0db9f0}rzslider .rz-ticks .tick .tick-value{position:absolute;top:-30px;transform:translate(-50%,0)} \ No newline at end of file diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index 63ff9e1..f2f63ec 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).value("defaultOptions",{floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,onStart:null,onChange:null,onEnd:null}).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","defaultOptions","throttle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.eventsBound=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&e()}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){var a=this.scope.rzSliderOptions;this.options={};for(var b in e)a&&void 0!==a[b]?this.options[b]=a[b]:this.options[b]=e[b];this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},manageEventsBindings:function(){(this.options.disabled||this.options.readOnly)&&this.eventsBound?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.eventsBound||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null),this.options.disabled&&this.eventsBound?this.unbindEvents():this.options.disabled||this.eventsBound||this.bindEvents()},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=+this.options.floor,this.scope.rzSliderModelthis.maxValue&&(this.scope.rzSliderModel=this.maxValue),this.range&&this.scope.rzSliderHigh>this.maxValue&&(this.scope.rzSliderHigh=this.maxValue)):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0,this.options.hideLimitLabels&&(this.flrLab.rzAlwaysHide=!0,this.ceilLab.rzAlwaysHide=!0,this.hideEl(this.flrLab),this.hideEl(this.ceilLab)),this.options.showTicksValues&&(this.flrLab.rzAlwaysHide=!0,this.ceilLab.rzAlwaysHide=!0,this.minLab.rzAlwaysHide=!0,this.maxLab.rzAlwaysHide=!0,this.cmbLab.rzAlwaysHide=!0,this.hideEl(this.flrLab),this.hideEl(this.ceilLab),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.hideEl(this.cmbLab)),this.range===!1&&(this.cmbLab.remove(),this.maxLab.remove(),this.maxH.rzAlwaysHide=!0,this.maxH[0].style.zIndex="-1000",this.hideEl(this.maxH)),this.range===!1&&this.options.showSelectionBar===!1&&(this.maxH.remove(),this.selBar.remove()),this.options.draggableRange&&(this.selBar.css("cursor","move"),this.selBar.addClass("rz-draggable"))},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
      • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
      • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=b.right-b.left,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(a-this.minValue)*this.maxLeft/this.valueRange||0},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
          ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).value("defaultOptions",{floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,onStart:null,onChange:null,onEnd:null}).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","defaultOptions","throttle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){var a=this.scope.rzSliderOptions;this.options={};for(var b in e)a&&void 0!==a[b]?this.options[b]=a[b]:this.options[b]=e[b];this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.scope.rzSliderModelthis.maxValue&&(this.scope.rzSliderModel=this.maxValue),this.range&&this.scope.rzSliderHigh>this.maxValue&&(this.scope.rzSliderHigh=this.maxValue)):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
        • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
        • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=b.right-b.left,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(a-this.minValue)*this.maxLeft/this.valueRange||0},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
            ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index 028625d..0bcf9b5 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -207,13 +207,6 @@ */ this.initHasRun = false; - - /** If the slider events are already bound to the slider - * - * @type {boolean} - */ - this.eventsBound = false; - // Slider DOM elements wrapped in jqLite this.fullBar = null; // The whole slider bar this.selBar = null; // Highlight between two handles @@ -245,6 +238,7 @@ this.applyOptions(); this.initElemHandles(); + this.manageElementsStyle(); this.addAccessibility(); this.manageEventsBindings(); this.setDisabledState(); @@ -307,7 +301,12 @@ this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { if (newValue === oldValue) return; - thrHigh(); + if (newValue != null) + thrHigh(); + if (self.range && newValue == null || !self.range && newValue != null) { + self.applyOptions(); + self.resetSlider(); + } }); this.scope.$watch('rzSliderOptions', function(newValue, oldValue) { @@ -335,6 +334,9 @@ else this.options[option_name] = userOpts[option_name]; } + 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.showTicks = this.options.showTicks || this.options.showTicksValues; if (this.options.translate) @@ -351,23 +353,117 @@ * @returns {undefined} */ resetSlider: function() { + this.manageElementsStyle(); this.setMinAndMax(); this.updateCeilLab(); this.updateFloorLab(); + this.unbindEvents(); this.manageEventsBindings(); this.setDisabledState(); this.calcViewDimensions(); }, + /** + * 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 offset cache properties + this.selBar.rzsl = 0; + this.minH.rzsl = 0; + this.maxH.rzsl = 0; + this.flrLab.rzsl = 0; + this.ceilLab.rzsl = 0; + this.minLab.rzsl = 0; + this.maxLab.rzsl = 0; + this.cmbLab.rzsl = 0; + }, + + /** Update each elements style based on options + * + */ + manageElementsStyle: function() { + + if (!this.range) + this.maxH.css('display', 'none'); + else + this.maxH.css('display', null); + + this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels); + this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels); + this.alwaysHide(this.minLab, this.options.showTicksValues); + this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range); + this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range); + this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar); + + if (!this.options.showTicks) + this.ticks.html(''); + + if (this.options.draggableRange) + this.selBar.addClass('rz-draggable'); + else + this.selBar.removeClass('rz-draggable'); + }, + + 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.eventsBound) + if (this.options.disabled || this.options.readOnly) this.unbindEvents(); - else if ((!this.options.disabled || !this.options.readOnly) && !this.eventsBound) + else if (!this.options.disabled || !this.options.readOnly) this.bindEvents(); }, @@ -383,10 +479,6 @@ else { this.sliderElem.attr('disabled', null); } - if (this.options.disabled && this.eventsBound) - this.unbindEvents(); - else if (!this.options.disabled && !this.eventsBound) - this.bindEvents(); }, /** @@ -462,14 +554,14 @@ if (this.range) this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); - this.minValue = +this.options.floor; + this.minValue = this.roundStep(+this.options.floor); if (this.scope.rzSliderModel < this.minValue) this.scope.rzSliderModel = this.minValue; if (this.range && this.scope.rzSliderHigh < this.minValue) this.scope.rzSliderHigh = this.minValue; if (this.options.ceil) { - this.maxValue = +this.options.ceil; + this.maxValue = this.roundStep(+this.options.ceil); if (this.scope.rzSliderModel > this.maxValue) this.scope.rzSliderModel = this.maxValue; if (this.range && this.scope.rzSliderHigh > this.maxValue) @@ -481,108 +573,6 @@ this.valueRange = this.maxValue - this.minValue; }, - /** - * 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 offset cache properties - this.selBar.rzsl = 0; - this.minH.rzsl = 0; - this.maxH.rzsl = 0; - this.flrLab.rzsl = 0; - this.ceilLab.rzsl = 0; - this.minLab.rzsl = 0; - this.maxLab.rzsl = 0; - this.cmbLab.rzsl = 0; - - // Hide limit labels - if (this.options.hideLimitLabels) { - this.flrLab.rzAlwaysHide = true; - this.ceilLab.rzAlwaysHide = true; - this.hideEl(this.flrLab); - this.hideEl(this.ceilLab); - } - - if (this.options.showTicksValues) { - this.flrLab.rzAlwaysHide = true; - this.ceilLab.rzAlwaysHide = true; - this.minLab.rzAlwaysHide = true; - this.maxLab.rzAlwaysHide = true; - this.cmbLab.rzAlwaysHide = true; - this.hideEl(this.flrLab); - this.hideEl(this.ceilLab); - this.hideEl(this.minLab); - this.hideEl(this.maxLab); - this.hideEl(this.cmbLab); - } - - // Remove stuff not needed in single slider - if (this.range === false) { - this.cmbLab.remove(); - this.maxLab.remove(); - - // Hide max handle - this.maxH.rzAlwaysHide = true; - this.maxH[0].style.zIndex = '-1000'; - this.hideEl(this.maxH); - } - - // Show selection bar for single slider or not - if (this.range === false && this.options.showSelectionBar === false) { - this.maxH.remove(); - this.selBar.remove(); - } - - // If using draggable range, use appropriate cursor for this.selBar. - if (this.options.draggableRange) { - this.selBar.css('cursor', 'move'); - this.selBar.addClass('rz-draggable'); - } - }, - /** * Adds accessibility atributes * @@ -1037,8 +1027,6 @@ this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); - - this.eventsBound = true; }, /** @@ -1052,7 +1040,6 @@ this.fullBar.off(); this.selBar.off(); this.ticks.off(); - this.eventsBound = false; }, /** diff --git a/src/rzslider.less b/src/rzslider.less index 6d24529..bfdb582 100644 --- a/src/rzslider.less +++ b/src/rzslider.less @@ -14,7 +14,7 @@ rzslider { position: relative; height: @barHeight; width: 100%; - margin: 30px 0 15px 0; + margin: 35px 0 15px 0; vertical-align: middle; -webkit-user-select: none; -moz-user-select: none; @@ -49,6 +49,9 @@ rzslider { width: 100%; height: @handleSize; z-index: 1; + &.rz-draggable { + cursor: move; + } } .rz-bar { @@ -140,4 +143,4 @@ rzslider { } } } -} \ No newline at end of file +} From 271b49676c03a961c5d8ab403fe45e57b7808b6a Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Fri, 23 Oct 2015 16:56:13 +0200 Subject: [PATCH 03/15] Add a global options method to apply custom default options to all sliders --- dist/rzslider.js | 76 +++++++++++++++++++++++++++----------------- dist/rzslider.min.js | 2 +- src/rzslider.js | 76 +++++++++++++++++++++++++++----------------- 3 files changed, 95 insertions(+), 59 deletions(-) diff --git a/dist/rzslider.js b/dist/rzslider.js index e4ae8c4..b2e4316 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -31,23 +31,47 @@ 'use strict'; var module = angular.module('rzModule', []) - .value('defaultOptions', { - floor: 0, - ceil: null, //defaults to rz-slider-model - step: 1, - precision: 0, - translate: null, - draggableRange: false, - showSelectionBar: false, - hideLimitLabels: false, - readOnly: false, - disabled: false, - interval: 350, - showTicks: false, - showTicksValues: false, - onStart: null, - onChange: null, - onEnd: null + .factory('RzSliderOptions', function() { + var defaultOptions = { + floor: 0, + ceil: null, //defaults to rz-slider-model + step: 1, + precision: 0, + translate: null, + draggableRange: false, + showSelectionBar: false, + hideLimitLabels: false, + readOnly: false, + disabled: false, + interval: 350, + showTicks: false, + showTicksValues: false, + scale: 1, + onStart: null, + onChange: null, + onEnd: 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; }) .value('throttle', @@ -97,7 +121,7 @@ }; }) - .factory('RzSlider', ['$timeout', '$document', '$window', 'defaultOptions', 'throttle', function($timeout, $document, $window, defaultOptions, throttle) { + .factory('RzSlider', ['$timeout', '$document', '$window', 'RzSliderOptions', 'throttle', function($timeout, $document, $window, RzSliderOptions, throttle) { 'use strict'; /** @@ -326,14 +350,8 @@ * Read the user options and apply them to the slider model */ applyOptions: function() { - var userOpts = this.scope.rzSliderOptions; - this.options = {}; - for (var option_name in defaultOptions) { - if (!userOpts || userOpts[option_name] === undefined) - this.options[option_name] = defaultOptions[option_name]; - else - this.options[option_name] = userOpts[option_name]; - } + this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions); + if (this.options.step <= 0) this.options.step = 1; this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined; @@ -915,7 +933,7 @@ */ getWidth: function(elem) { var val = elem[0].getBoundingClientRect(); - elem.rzsw = val.right - val.left; + elem.rzsw = (val.right - val.left) * this.options.scale; return elem.rzsw; }, @@ -982,7 +1000,7 @@ if (!this.range) { return this.minH; } - var offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; + var offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale; return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; }, @@ -1095,7 +1113,7 @@ sliderLO, newOffset, newValue; sliderLO = this.sliderElem.rzsl; - newOffset = eventX - sliderLO - this.handleHalfWidth; + newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale; if (newOffset <= 0) { if (pointer.rzsl === 0) diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index f2f63ec..ad6983f 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).value("defaultOptions",{floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,onStart:null,onChange:null,onEnd:null}).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","defaultOptions","throttle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){var a=this.scope.rzSliderOptions;this.options={};for(var b in e)a&&void 0!==a[b]?this.options[b]=a[b]:this.options[b]=e[b];this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.scope.rzSliderModelthis.maxValue&&(this.scope.rzSliderModel=this.maxValue),this.range&&this.scope.rzSliderHigh>this.maxValue&&(this.scope.rzSliderHigh=this.maxValue)):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
          • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
          • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=b.right-b.left,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(a-this.minValue)*this.maxLeft/this.valueRange||0},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
              ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","throttle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.scope.rzSliderModelthis.maxValue&&(this.scope.rzSliderModel=this.maxValue),this.range&&this.scope.rzSliderHigh>this.maxValue&&(this.scope.rzSliderHigh=this.maxValue)):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
            • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
            • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(a-this.minValue)*this.maxLeft/this.valueRange||0},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index 0bcf9b5..d0657ff 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -31,23 +31,47 @@ 'use strict'; var module = angular.module('rzModule', []) - .value('defaultOptions', { - floor: 0, - ceil: null, //defaults to rz-slider-model - step: 1, - precision: 0, - translate: null, - draggableRange: false, - showSelectionBar: false, - hideLimitLabels: false, - readOnly: false, - disabled: false, - interval: 350, - showTicks: false, - showTicksValues: false, - onStart: null, - onChange: null, - onEnd: null + .factory('RzSliderOptions', function() { + var defaultOptions = { + floor: 0, + ceil: null, //defaults to rz-slider-model + step: 1, + precision: 0, + translate: null, + draggableRange: false, + showSelectionBar: false, + hideLimitLabels: false, + readOnly: false, + disabled: false, + interval: 350, + showTicks: false, + showTicksValues: false, + scale: 1, + onStart: null, + onChange: null, + onEnd: 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; }) .value('throttle', @@ -97,7 +121,7 @@ }; }) - .factory('RzSlider', function($timeout, $document, $window, defaultOptions, throttle) { + .factory('RzSlider', function($timeout, $document, $window, RzSliderOptions, throttle) { 'use strict'; /** @@ -326,14 +350,8 @@ * Read the user options and apply them to the slider model */ applyOptions: function() { - var userOpts = this.scope.rzSliderOptions; - this.options = {}; - for (var option_name in defaultOptions) { - if (!userOpts || userOpts[option_name] === undefined) - this.options[option_name] = defaultOptions[option_name]; - else - this.options[option_name] = userOpts[option_name]; - } + this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions); + if (this.options.step <= 0) this.options.step = 1; this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined; @@ -915,7 +933,7 @@ */ getWidth: function(elem) { var val = elem[0].getBoundingClientRect(); - elem.rzsw = val.right - val.left; + elem.rzsw = (val.right - val.left) * this.options.scale; return elem.rzsw; }, @@ -982,7 +1000,7 @@ if (!this.range) { return this.minH; } - var offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; + var offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale; return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; }, @@ -1095,7 +1113,7 @@ sliderLO, newOffset, newValue; sliderLO = this.sliderElem.rzsl; - newOffset = eventX - sliderLO - this.handleHalfWidth; + newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale; if (newOffset <= 0) { if (pointer.rzsl === 0) From 72c7e74cd503ee8e5cb708998883d1fba6f8280b Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Sun, 25 Oct 2015 19:02:37 +0100 Subject: [PATCH 04/15] Improve behavior when scope value is out of slider range Implement the solution described in #159 . --- dist/rzslider.js | 25 ++++++++++++++----------- dist/rzslider.min.js | 2 +- src/rzslider.js | 25 ++++++++++++++----------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/dist/rzslider.js b/dist/rzslider.js index b2e4316..7df7420 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -568,23 +568,15 @@ this.step = +this.options.step; this.precision = +this.options.precision; + this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel); if (this.range) this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); this.minValue = this.roundStep(+this.options.floor); - if (this.scope.rzSliderModel < this.minValue) - this.scope.rzSliderModel = this.minValue; - if (this.range && this.scope.rzSliderHigh < this.minValue) - this.scope.rzSliderHigh = this.minValue; - if (this.options.ceil) { + if (this.options.ceil) this.maxValue = this.roundStep(+this.options.ceil); - if (this.scope.rzSliderModel > this.maxValue) - this.scope.rzSliderModel = this.maxValue; - if (this.range && this.scope.rzSliderHigh > this.maxValue) - this.scope.rzSliderHigh = this.maxValue; - } else this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; @@ -957,7 +949,18 @@ * @returns {number} */ valueToOffset: function(val) { - return (val - this.minValue) * this.maxLeft / this.valueRange || 0; + return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0; + }, + + /** + * Ensure that the position rendered is within the slider bounds, even if the value is not + * + * @param {number} val + * @returns {number} + */ + sanitizeOffsetValue: function(val) + { + return Math.min(Math.max(val, this.minValue), this.maxValue); }, /** diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index ad6983f..a0e47cf 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","throttle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.scope.rzSliderModelthis.maxValue&&(this.scope.rzSliderModel=this.maxValue),this.range&&this.scope.rzSliderHigh>this.maxValue&&(this.scope.rzSliderHigh=this.maxValue)):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
              • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
              • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(a-this.minValue)*this.maxLeft/this.valueRange||0},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                  ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","throttle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                    ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index d0657ff..850e074 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -568,23 +568,15 @@ this.step = +this.options.step; this.precision = +this.options.precision; + this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel); if (this.range) this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); this.minValue = this.roundStep(+this.options.floor); - if (this.scope.rzSliderModel < this.minValue) - this.scope.rzSliderModel = this.minValue; - if (this.range && this.scope.rzSliderHigh < this.minValue) - this.scope.rzSliderHigh = this.minValue; - if (this.options.ceil) { + if (this.options.ceil) this.maxValue = this.roundStep(+this.options.ceil); - if (this.scope.rzSliderModel > this.maxValue) - this.scope.rzSliderModel = this.maxValue; - if (this.range && this.scope.rzSliderHigh > this.maxValue) - this.scope.rzSliderHigh = this.maxValue; - } else this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; @@ -957,7 +949,18 @@ * @returns {number} */ valueToOffset: function(val) { - return (val - this.minValue) * this.maxLeft / this.valueRange || 0; + return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0; + }, + + /** + * Ensure that the position rendered is within the slider bounds, even if the value is not + * + * @param {number} val + * @returns {number} + */ + sanitizeOffsetValue: function(val) + { + return Math.min(Math.max(val, this.minValue), this.maxValue); }, /** From cc329a2abe947a880de112181d751ef3d168c93e Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Sat, 31 Oct 2015 13:21:17 +0100 Subject: [PATCH 05/15] Add a rz prefix to the throttle function As mentioned in #162 --- dist/rzslider.js | 10 +++++----- dist/rzslider.min.js | 2 +- src/rzslider.js | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/dist/rzslider.js b/dist/rzslider.js index 7df7420..aaf71b7 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -74,9 +74,9 @@ return factory; }) - .value('throttle', + .value('rzThrottle', /** - * throttle + * rzThrottle * * Taken from underscore project * @@ -121,7 +121,7 @@ }; }) - .factory('RzSlider', ['$timeout', '$document', '$window', 'RzSliderOptions', 'throttle', function($timeout, $document, $window, RzSliderOptions, throttle) { + .factory('RzSlider', ['$timeout', '$document', '$window', 'RzSliderOptions', 'rzThrottle', function($timeout, $document, $window, RzSliderOptions, rzThrottle) { 'use strict'; /** @@ -286,7 +286,7 @@ // Watch for changes to the model - thrLow = throttle(function() { + thrLow = rzThrottle(function() { self.setMinAndMax(); self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); self.updateSelectionBar(); @@ -298,7 +298,7 @@ }, self.interval); - thrHigh = throttle(function() { + thrHigh = rzThrottle(function() { self.setMinAndMax(); self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); self.updateSelectionBar(); diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index a0e47cf..a54fb41 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("throttle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","throttle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                  • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                  • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                      ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                    • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                    • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                        ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index 850e074..f7a7427 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -74,9 +74,9 @@ return factory; }) - .value('throttle', + .value('rzThrottle', /** - * throttle + * rzThrottle * * Taken from underscore project * @@ -121,7 +121,7 @@ }; }) - .factory('RzSlider', function($timeout, $document, $window, RzSliderOptions, throttle) { + .factory('RzSlider', function($timeout, $document, $window, RzSliderOptions, rzThrottle) { 'use strict'; /** @@ -286,7 +286,7 @@ // Watch for changes to the model - thrLow = throttle(function() { + thrLow = rzThrottle(function() { self.setMinAndMax(); self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); self.updateSelectionBar(); @@ -298,7 +298,7 @@ }, self.interval); - thrHigh = throttle(function() { + thrHigh = rzThrottle(function() { self.setMinAndMax(); self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); self.updateSelectionBar(); From 1843254eb624440664010df9612f28e9af533366 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Sun, 1 Nov 2015 13:33:49 +0100 Subject: [PATCH 06/15] Add a stepsArray option This is to ease to use of non linear/number steps. As suggested in #163 --- demo/demo.js | 11 ++--------- dist/rzslider.js | 12 +++++++++++- dist/rzslider.min.js | 2 +- src/rzslider.js | 12 +++++++++++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/demo/demo.js b/demo/demo.js index 37bc8a9..0424c8e 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -65,18 +65,11 @@ app.controller('MainCtrl', function($scope, $rootScope, $timeout, $modal) { } }; - //Slider config with custom display function displaying letters - var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + //Slider config with steps array of letters $scope.slider_alphabet = { value: 0, options: { - ceil: alphabet.length - 1, - floor: 0, - translate: function(value) { - if (value >= 0 && value < alphabet.length) - return alphabet[value]; - return ''; - } + stepsArray:'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('') } }; diff --git a/dist/rzslider.js b/dist/rzslider.js index aaf71b7..5116820 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -38,6 +38,7 @@ step: 1, precision: 0, translate: null, + stepsArray: null, draggableRange: false, showSelectionBar: false, hideLimitLabels: false, @@ -357,7 +358,16 @@ this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined; this.options.draggableRange = this.range && this.options.draggableRange; this.options.showTicks = this.options.showTicks || this.options.showTicksValues; - if (this.options.translate) + + if(this.options.stepsArray) { + this.options.floor = 0; + this.options.ceil = this.options.stepsArray.length -1; + this.options.step = 1; + this.customTrFn = function(value) { + return this.options.stepsArray[value]; + }; + } + else if (this.options.translate) this.customTrFn = this.options.translate; else this.customTrFn = function(value) { diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index a54fb41..028e082 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,translate:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                      • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                      • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                          ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                        • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                        • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                            ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index f7a7427..65102a9 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -38,6 +38,7 @@ step: 1, precision: 0, translate: null, + stepsArray: null, draggableRange: false, showSelectionBar: false, hideLimitLabels: false, @@ -357,7 +358,16 @@ this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined; this.options.draggableRange = this.range && this.options.draggableRange; this.options.showTicks = this.options.showTicks || this.options.showTicksValues; - if (this.options.translate) + + if(this.options.stepsArray) { + this.options.floor = 0; + this.options.ceil = this.options.stepsArray.length -1; + this.options.step = 1; + this.customTrFn = function(value) { + return this.options.stepsArray[value]; + }; + } + else if (this.options.translate) this.customTrFn = this.options.translate; else this.customTrFn = function(value) { From 831212b27dd034c90cdeb060399be58ecf66ed53 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Sun, 1 Nov 2015 18:03:34 +0100 Subject: [PATCH 07/15] Add an id option that is passed to the translate function as second arg As discussed in #161 --- dist/rzslider.js | 7 ++++--- dist/rzslider.min.js | 2 +- src/rzslider.js | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dist/rzslider.js b/dist/rzslider.js index 5116820..6a5bf1c 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -37,9 +37,10 @@ ceil: null, //defaults to rz-slider-model step: 1, precision: 0, + id: null, translate: null, stepsArray: null, - draggableRange: false, + draggableRange: false, showSelectionBar: false, hideLimitLabels: false, readOnly: false, @@ -553,7 +554,7 @@ translateFn: function(value, label, useCustomTr) { useCustomTr = useCustomTr === undefined ? true : useCustomTr; - var valStr = String((useCustomTr ? this.customTrFn(value) : value)), + var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)), getWidth = false; if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) { @@ -872,7 +873,7 @@ * @returns {*} */ getDisplayValue: function(value) { - return this.customTrFn(value); + return this.customTrFn(value, this.options.id); }, /** diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index 028e082..197b2aa 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                          • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                          • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                              ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                            • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                            • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index 65102a9..8615a79 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -37,9 +37,10 @@ ceil: null, //defaults to rz-slider-model step: 1, precision: 0, + id: null, translate: null, stepsArray: null, - draggableRange: false, + draggableRange: false, showSelectionBar: false, hideLimitLabels: false, readOnly: false, @@ -553,7 +554,7 @@ translateFn: function(value, label, useCustomTr) { useCustomTr = useCustomTr === undefined ? true : useCustomTr; - var valStr = String((useCustomTr ? this.customTrFn(value) : value)), + var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)), getWidth = false; if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) { @@ -872,7 +873,7 @@ * @returns {*} */ getDisplayValue: function(value) { - return this.customTrFn(value); + return this.customTrFn(value, this.options.id); }, /** From 22a28e8003eb962da51ced8a0224c8e555780697 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Sun, 1 Nov 2015 19:15:22 +0100 Subject: [PATCH 08/15] Update the read-me to match new options --- README.md | 206 +++++++++++++++++++++++++++++------------------------- 1 file changed, 109 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 8f401b7..85be76c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ Slider directive implementation for AngularJS, without any dependencies. ## Examples - **Various examples:** [http://rzajac.github.io/angularjs-slider/](http://rzajac.github.io/angularjs-slider/index.html) -- **Slider inside Angular UI tabs:** http://jsfiddle.net/7w755fLv/ ## Reporting issues Make sure the report is accompanied by a reproducible demo. The ideal demo is created by forking [our standard jsFiddle](http://jsfiddle.net/1ruqgnhk/), adding your own code and stripping it down to an absolute minimum needed to demonstrate the bug. @@ -33,7 +32,7 @@ $ bower install --save angularjs-slider ### Module ```javascript -angular.module('', ['rzModule']); +angular.module('yourApp', ['rzModule']); ``` ### Single slider @@ -49,42 +48,56 @@ $scope.priceSlider = 150; ``` -Above example would render a slider from 0 to 150. If you need floor and ceiling values use `rz-slider-floor` and `rz-slider-ceil` attributes. +Above example would render a slider from 0 to 150. If you need floor and ceiling values use `rz-slider-options` attribute and provide an object with `floor`and `ceil`. ```html
                                - - + rz-slider-model="slider.value" + rz-slider-options="slider.options"> +
                                +``` +```js +$scope.slider = { + value: 150, + options: { + floor: 0, + ceil: 450 + } +}; +``` +If you don't want to bother with an object set in your javascript file, you can pass an anonymous object literal to the slider options: +```html +
                                - + rz-slider-model="value" + rz-slider-options="{floor: 0, ceil: 450}">
                                ``` +```js +$scope.value = 150; +``` ### Range slider ```javascript // In your controller -$scope.priceSlider = { - min: 100, - max: 180, - ceil: 500, - floor: 0 +$scope.slider = { + min: 100, + max: 180, + options: { + floor: 0, + ceil: 450 + } }; ``` ```html + rz-slider-model="slider.min" + rz-slider-high="slider.max" + rz-slider-options="slider.options"> ``` ## Directive attributes @@ -95,107 +108,107 @@ $scope.priceSlider = { **rz-slider-high** -> Model for high value slider. Providing both _rz-slider-high_ and _rz-slider-model_ will render range slider. - -**rz-slider-floor** - -> Minimum value for a slider. - -**rz-slider-ceil** - -> Maximum value for a slider. - -**rz-slider-step** - -> slider step. - -**rz-slider-precision** - -> The precision to display values with. The `toFixed()` is used internally for this. - -**rz-slider-hide-limit-labels** - -> Set to true to hide min / max labels - -**rz-slider-always-show-bar** - -> Set to true to always show selection bar +> Model for high value slider. Providing both _rz-slider-model_ and _rz-slider-high_ will render range slider. -**rz-slider-present-only** +**rz-slider-options** -> When set to true slider is used in presentation mode. No handle dragging. +> An object with all the other options of the slider. Each option can be updated at runtime and the slider will automatically be re-rendered. -**rz-slider-draggable-range** +The default options are: +```js +{ + floor: 0, + ceil: null, //defaults to rz-slider-model + step: 1, + precision: 0, + translate: null, + id: null, + stepsArray: null, + draggableRange: false, + showSelectionBar: false, + hideLimitLabels: false, + readOnly: false, + disabled: false, + interval: 350, + showTicks: false, + showTicksValues: false, + scale: 1, + onStart: null, + onChange: null, + onEnd: null +} +```` -> When set to true and using a range slider, the range can be dragged by the selection bar. +**floor** - _Number (defaults to 0)_: Minimum value for a slider. -**rz-slider-translate** +**ceil** - _Number (defaults to `rz-slider-model`value)_: Maximum value for a slider. -> Custom translate function. Use this if you want to translate values displayed on the slider. For example if you want to display dollar amounts instead of just numbers do this: +**step** - _Number (defaults to 1)_: Step between each value. -**rz-slider-on-start** +**precision** - _Number (defaults to 0)_: The precision to display values with. The `toFixed()` is used internally for this. -> Function to be called when a slider update is started. +**translate** - _Function(value, sliderId)_: Custom translate function. Use this if you want to translate values displayed on the slider. For example if you want to display dollar amounts instead of just numbers: +```html +
                                + +
                                +``` +```js +$scope.slider = { + value: 0, + options: { + floor: 0, + ceil: 100, + translate: function(value) { + return '$' + value; + } + } +}; +``` -**rz-slider-on-change** +**id** - _Any (defaults to null)_: If you want to use the same `translate` function for several sliders, just set the `id` to anything you want, and it will be passed to the `translate(value, sliderId)` function as a second argument. -> Function to be called when rz-slider-model or rz-slider-high change. +**stepsArray** - _Array_: If you want to display a slider with non linear/number steps. Just pass an array with each slider value and that's it; the floor, ceil and step settings of the slider will be computed automatically. The `rz-slider-model` value will be the index of the selected item in the stepsArray. -**rz-slider-on-end** +**draggableRange** - _Boolean (defaults to false)_: When set to true and using a range slider, the range can be dragged by the selection bar. -> Function to be called when a slider update is ended. +**showSelectionBar** - _Boolean (defaults to false)_: Set to true to always show the selection bar. -**rz-slider-show-ticks** +**hideLimitLabels** - _Boolean (defaults to false)_: Set to true to hide min / max labels -> Display a tick for each value. +**readOnly** - _Boolean (defaults to false)_: Set to true to make the slider read-only. -**rz-slider-show-ticks-value** +**disabled** - _Boolean (defaults to false)_: Set to true to disable the slider. -> Display a tick for each value and the value of the tick. +**interval** - _Number in ms (defaults to 350)_: Internally, a `throttle` function (See http://underscorejs.org/#throttle) is used when the model or high values of the slider are changed from outside the slider. This is to prevent from re-rendering the slider too many times in a row. `interval` is the number of milliseconds to wait between two updates of the slider. -**rz-slider-disabled** +**showTicks** - _Boolean (defaults to false)_: Set to true to display a tick for each step of the slider. -> Disable the slider (apply a special style and unbind events) +**showTicksValues** - _Boolean (defaults to false)_: Set to true to display a tick and the step value for each step of the slider. -**rz-slider-interval** +**scale** - _Number (defaults to 1)_: If you display the slider in an element that uses `transform: scale(0.5)`, set the `scale` value to 2 so that the slider is rendered properly and the events are handled correctly. -> The interval (in ms) at which the slider DOM element updates when rz-slider-model or rz-slider-high change from outside the slider. Defaults to 350. +**onStart** - _Function()_: Function to be called when a slider update is started. -```javascript -// In your controller +**onChange** - _Function()_: Function to be called when rz-slider-model or rz-slider-high change. -$scope.priceSlider = { - min: 100, - max: 180, - ceil: 500, - floor: 0 -}; - -$scope.translate = function(value) -{ - return '$' + value; -} - -$scope.onSliderChange = function() -{ - console.log('changed', $scope.priceSlider); -} -``` +**onEnd** - _Function()_: Function to be called when a slider update is ended. -```html - +## Change default options +If you want the change the default options for all the sliders displayed in your application, you can set them using the `RzSliderOptions.options()` method: +```js +angular.module('App', ['rzModule']) + .run(function( RzSliderOptions ) { + // show ticks for all sliders + RzSliderOptions.options( { showTicks: true } ); + }); ``` ## Slider events -To force slider to recalculate dimensions broadcast **reCalcViewDimensions** event from parent scope. This is useful for example when you use slider inside a widget where the content is hidden at start - see the "Sliders into modal" example [on the demo site](http://rzajac.github.io/angularjs-slider/). +To force slider to recalculate dimensions, broadcast **reCalcViewDimensions** event from parent scope. This is useful for example when you use slider inside a widget where the content is hidden at start - see the "Sliders into modal" example [on the demo site](http://rzajac.github.io/angularjs-slider/). You can also force redraw with **rzSliderForceRender** event. @@ -220,13 +233,12 @@ $scope.$on("slideEnded", function() { ## Browser support -I use Slider on couple of my projects and it's being tested on desktop versions of Chrome, Firefox, Safari, IE 9/10. +I use Slider on couple of my projects and it's being tested on desktop versions of Chrome, Firefox, Safari, IE 9/10 (Ticks are displayed using flex display so they don't work on IE9). Slider is also tested on Android and iPhone using all browsers available on those platforms. ## Disclaimer -This project is based on [https://github.com/prajwalkman/angular-slider](https://github.com/prajwalkman/angular-slider). It has been rewritten from scratch in JavaScript - (the original source was written in CoffeeScript). +This project is based on [https://github.com/prajwalkman/angular-slider](https://github.com/prajwalkman/angular-slider). It has been rewritten from scratch in JavaScript (the original source was written in CoffeeScript). ## License From 3c234fc0e72063ec31b23144de6878f433a6b8b0 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Mon, 9 Nov 2015 18:39:31 +0100 Subject: [PATCH 09/15] Bump version number to 2.0.0 --- README.md | 2 -- bower.json | 2 +- dist/rzslider.js | 6 +++--- dist/rzslider.min.css | 2 +- dist/rzslider.min.js | 2 +- package.json | 2 +- src/rzslider.js | 6 +++--- 7 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 85be76c..23a53af 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ ## AngularJS slider directive with no external dependencies -> **A refactoring is on-going, any feedback is welcome:** https://github.com/rzajac/angularjs-slider/pull/158 - Slider directive implementation for AngularJS, without any dependencies. - Mobile friendly diff --git a/bower.json b/bower.json index 7a5ae46..ab22db5 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angularjs-slider", - "version": "1.1.0", + "version": "2.0.0", "homepage": "https://github.com/rzajac/angularjs-slider", "authors": [ "Rafal Zajac ", diff --git a/dist/rzslider.js b/dist/rzslider.js index 6a5bf1c..5bf36bc 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -4,7 +4,7 @@ * (c) Rafal Zajac * http://github.com/rzajac/angularjs-slider * - * Version: v1.1.0 + * Version: v2.0.0 * * Licensed under the MIT license */ @@ -40,7 +40,7 @@ id: null, translate: null, stepsArray: null, - draggableRange: false, + draggableRange: false, showSelectionBar: false, hideLimitLabels: false, readOnly: false, @@ -359,7 +359,7 @@ this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined; this.options.draggableRange = this.range && this.options.draggableRange; this.options.showTicks = this.options.showTicks || this.options.showTicksValues; - + if(this.options.stepsArray) { this.options.floor = 0; this.options.ceil = this.options.stepsArray.length -1; diff --git a/dist/rzslider.min.css b/dist/rzslider.min.css index 534ebf9..34da7a9 100644 --- a/dist/rzslider.min.css +++ b/dist/rzslider.min.css @@ -1,2 +1,2 @@ -/*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ +/*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:35px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}rzslider[disabled]{cursor:not-allowed}rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}rzslider span{position:absolute;display:inline-block;white-space:nowrap}rzslider .rz-base{width:100%;height:100%;padding:0}rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}rzslider .rz-bar-wrapper.rz-draggable{cursor:move}rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}rzslider .rz-pointer:hover:after{background-color:#fff}rzslider .rz-pointer.rz-active:after{background-color:#451aff}rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}rzslider .rz-bubble.rz-selection{top:16px}rzslider .rz-bubble.rz-limit{color:#55637d}rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:flex;width:100%;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;justify-content:space-between}rzslider .rz-ticks .tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}rzslider .rz-ticks .tick.selected{background:#0db9f0}rzslider .rz-ticks .tick .tick-value{position:absolute;top:-30px;transform:translate(-50%,0)} \ No newline at end of file diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index 197b2aa..53aab74 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ -/*! angularjs-slider - v1.1.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ +/*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ !function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                              • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                              • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                  ')}]),b}); \ No newline at end of file diff --git a/package.json b/package.json index 439b4e1..476c953 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angularjs-slider", - "version": "1.1.0", + "version": "2.0.0", "description": "AngularJS slider directive with no external dependencies. Mobile friendly!.", "main": "dist/rzslider.js", "repository": { diff --git a/src/rzslider.js b/src/rzslider.js index 8615a79..aab5057 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -4,7 +4,7 @@ * (c) Rafal Zajac * http://github.com/rzajac/angularjs-slider * - * Version: v1.1.0 + * Version: v2.0.0 * * Licensed under the MIT license */ @@ -40,7 +40,7 @@ id: null, translate: null, stepsArray: null, - draggableRange: false, + draggableRange: false, showSelectionBar: false, hideLimitLabels: false, readOnly: false, @@ -359,7 +359,7 @@ this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined; this.options.draggableRange = this.range && this.options.draggableRange; this.options.showTicks = this.options.showTicks || this.options.showTicksValues; - + if(this.options.stepsArray) { this.options.floor = 0; this.options.ceil = this.options.stepsArray.length -1; From 61d4aef3b23256f29db17aee8fd53189047148b0 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Mon, 9 Nov 2015 18:51:03 +0100 Subject: [PATCH 10/15] Update changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de055ad..8bfb0ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 2.0.0 (2015-11-XX) +## Breaking changes +- All attributes except `rzSliderModel` and `rzSliderHigh` are moved to `rzSliderOptions`. (See the new documentation in ReadMe) + +## Features +- Add a `rzSliderOptions` attribute to pass options to the slider. +- Add a `RzSliderOptions.options()` method to set global options. +- Add a `scale` option to fix sliders displayed in an element that uses `transform: scale(0.5)`. +- Add a `stepsArray` option (#163) +- Add an `id` option that is passed to the translate function as second arg (#161) + # 1.1.0 (2015-11-07) ## Features - Configurable update interval (#153) From f553a623007d40f9c8db4f38ce6223edce3a1418 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Mon, 9 Nov 2015 19:07:13 +0100 Subject: [PATCH 11/15] Fix the interval options. --- dist/rzslider.js | 6 +++--- dist/rzslider.min.js | 2 +- src/rzslider.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dist/rzslider.js b/dist/rzslider.js index 5bf36bc..b4d0d61 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -4,7 +4,7 @@ * (c) Rafal Zajac * http://github.com/rzajac/angularjs-slider * - * Version: v2.0.0 + * Version: v1.0.0 * * Licensed under the MIT license */ @@ -298,7 +298,7 @@ self.updateCmbLabel(); } - }, self.interval); + }, self.options.interval); thrHigh = rzThrottle(function() { self.setMinAndMax(); @@ -306,7 +306,7 @@ self.updateSelectionBar(); self.updateTicksScale(); self.updateCmbLabel(); - }, self.interval); + }, self.options.interval); this.scope.$on('rzSliderForceRender', function() { self.resetLabelsValue(); diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index 53aab74..5cf7df0 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                                • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                                • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                    ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.options.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.options.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                                  • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                                  • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                      ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index aab5057..ed160fe 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -4,7 +4,7 @@ * (c) Rafal Zajac * http://github.com/rzajac/angularjs-slider * - * Version: v2.0.0 + * Version: v1.0.0 * * Licensed under the MIT license */ @@ -298,7 +298,7 @@ self.updateCmbLabel(); } - }, self.interval); + }, self.options.interval); thrHigh = rzThrottle(function() { self.setMinAndMax(); @@ -306,7 +306,7 @@ self.updateSelectionBar(); self.updateTicksScale(); self.updateCmbLabel(); - }, self.interval); + }, self.options.interval); this.scope.$on('rzSliderForceRender', function() { self.resetLabelsValue(); From 680b2a27a58e1de0360f9635a1d96fbefd6d3515 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Tue, 10 Nov 2015 14:58:03 +0100 Subject: [PATCH 12/15] add ticksValuesTooltip example --- CHANGELOG.md | 1 + README.md | 8 +++++--- demo/demo.js | 5 ++++- demo/index.html | 4 ++-- dist/rzslider.js | 23 +++++++++++++++-------- dist/rzslider.min.js | 2 +- src/rzslider.js | 23 +++++++++++++++-------- 7 files changed, 43 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bfb0ae..d76a98e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Add a `scale` option to fix sliders displayed in an element that uses `transform: scale(0.5)`. - Add a `stepsArray` option (#163) - Add an `id` option that is passed to the translate function as second arg (#161) +- Add a `ticksValuesTooltip` option that is used to display a tooltip on the ticks values (requires angular-ui bootstrap). # 1.1.0 (2015-11-07) ## Features diff --git a/README.md b/README.md index 23a53af..dd8f737 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ The default options are: translate: null, id: null, stepsArray: null, - draggableRange: false, + draggableRange: false, showSelectionBar: false, hideLimitLabels: false, readOnly: false, @@ -186,6 +186,8 @@ $scope.slider = { **showTicksValues** - _Boolean (defaults to false)_: Set to true to display a tick and the step value for each step of the slider. +**ticksValuesTooltip** - _Function(value) (defaults to null)_: (requires angular-ui bootstrap) Used to display a tooltip when a tick value is hovered. Set to a function that returns the tooltip content for a given value. + **scale** - _Number (defaults to 1)_: If you display the slider in an element that uses `transform: scale(0.5)`, set the `scale` value to 2 so that the slider is rendered properly and the events are handled correctly. **onStart** - _Function()_: Function to be called when a slider update is started. @@ -210,11 +212,11 @@ To force slider to recalculate dimensions, broadcast **reCalcViewDimensions** ev You can also force redraw with **rzSliderForceRender** event. -At the end of each "slide" slider emits `slideEnded` event. +At the end of each "slide" slider emits `slideEnded` event. ```javascript $scope.$on("slideEnded", function() { - // user finished sliding a handle + // user finished sliding a handle }); ``` diff --git a/demo/demo.js b/demo/demo.js index 0424c8e..6cc0c66 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -89,7 +89,10 @@ app.controller('MainCtrl', function($scope, $rootScope, $timeout, $modal) { options: { ceil: 10, floor: 0, - showTicksValues: true + showTicksValues: true, + ticksValuesTooltip: function(v) { + return 'Tooltip for ' + v; + } } }; diff --git a/demo/index.html b/demo/index.html index f8994eb..fc58437 100644 --- a/demo/index.html +++ b/demo/index.html @@ -90,7 +90,7 @@

                                      Slider with ticks

                                      -

                                      Slider with ticks and values

                                      +

                                      Slider with ticks and values (and tooltips)

                                      Slider with all options demo - + diff --git a/dist/rzslider.js b/dist/rzslider.js index b4d0d61..0a15384 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -48,6 +48,7 @@ interval: 350, showTicks: false, showTicksValues: false, + ticksValuesTooltip: null, scale: 1, onStart: null, onChange: null, @@ -123,7 +124,7 @@ }; }) - .factory('RzSlider', ['$timeout', '$document', '$window', 'RzSliderOptions', 'rzThrottle', function($timeout, $document, $window, RzSliderOptions, rzThrottle) { + .factory('RzSlider', ['$timeout', '$document', '$window', '$compile', 'RzSliderOptions', 'rzThrottle', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) { 'use strict'; /** @@ -360,9 +361,9 @@ this.options.draggableRange = this.range && this.options.draggableRange; this.options.showTicks = this.options.showTicks || this.options.showTicksValues; - if(this.options.stepsArray) { + if (this.options.stepsArray) { this.options.floor = 0; - this.options.ceil = this.options.stepsArray.length -1; + this.options.ceil = this.options.stepsArray.length - 1; this.options.step = 1; this.customTrFn = function(value) { return this.options.stepsArray[value]; @@ -645,11 +646,18 @@ var value = this.roundStep(this.minValue + i * this.step); var selectedClass = this.isTickSelected(value) ? 'selected' : ''; positions += '
                                    • '; - if (this.options.showTicksValues) - positions += '' + this.getDisplayValue(value) + ''; + if (this.options.showTicksValues) { + var tooltip = ''; + if (this.options.ticksValuesTooltip) { + tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"'; + } + positions += '' + this.getDisplayValue(value) + ''; + } positions += '
                                    • '; } this.ticks.html(positions); + if (this.options.ticksValuesTooltip) + $compile(this.ticks.contents())(this.scope); }, isTickSelected: function(value) { @@ -963,14 +971,13 @@ return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0; }, - /** + /** * Ensure that the position rendered is within the slider bounds, even if the value is not * * @param {number} val * @returns {number} */ - sanitizeOffsetValue: function(val) - { + sanitizeOffsetValue: function(val) { return Math.min(Math.max(val, this.minValue), this.maxValue); }, diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index 5cf7df0..ce3fb88 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","RzSliderOptions","rzThrottle",function(b,c,d,e,f){var g=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return g.prototype={init:function(){var c,e,g=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",g),a.element(d).on("resize",g),this.initHasRun=!0,c=f(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.options.interval),e=f(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.options.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",g)})},applyOptions:function(){this.options=e.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),e=this.isTickSelected(d)?"selected":"";a+='
                                    • ',this.options.showTicksValues&&(a+=''+this.getDisplayValue(d)+""),a+="
                                    • "}this.ticks.html(a)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},g}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                        ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,ticksValuesTooltip:null,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","$compile","RzSliderOptions","rzThrottle",function(b,c,d,e,f,g){var h=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return h.prototype={init:function(){var c,e,f=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",f),a.element(d).on("resize",f),this.initHasRun=!0,c=g(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.options.interval),e=g(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.options.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",f)})},applyOptions:function(){this.options=f.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),f=this.isTickSelected(d)?"selected":"";if(a+='
                                      • ',this.options.showTicksValues){var g="";this.options.ticksValuesTooltip&&(g='uib-tooltip="'+this.options.ticksValuesTooltip(d)+'"'),a+="'+this.getDisplayValue(d)+""}a+="
                                      • "}this.ticks.html(a),this.options.ticksValuesTooltip&&e(this.ticks.contents())(this.scope)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},h}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                          ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index ed160fe..4ae8166 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -48,6 +48,7 @@ interval: 350, showTicks: false, showTicksValues: false, + ticksValuesTooltip: null, scale: 1, onStart: null, onChange: null, @@ -123,7 +124,7 @@ }; }) - .factory('RzSlider', function($timeout, $document, $window, RzSliderOptions, rzThrottle) { + .factory('RzSlider', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) { 'use strict'; /** @@ -360,9 +361,9 @@ this.options.draggableRange = this.range && this.options.draggableRange; this.options.showTicks = this.options.showTicks || this.options.showTicksValues; - if(this.options.stepsArray) { + if (this.options.stepsArray) { this.options.floor = 0; - this.options.ceil = this.options.stepsArray.length -1; + this.options.ceil = this.options.stepsArray.length - 1; this.options.step = 1; this.customTrFn = function(value) { return this.options.stepsArray[value]; @@ -645,11 +646,18 @@ var value = this.roundStep(this.minValue + i * this.step); var selectedClass = this.isTickSelected(value) ? 'selected' : ''; positions += '
                                        • '; - if (this.options.showTicksValues) - positions += '' + this.getDisplayValue(value) + ''; + if (this.options.showTicksValues) { + var tooltip = ''; + if (this.options.ticksValuesTooltip) { + tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"'; + } + positions += '' + this.getDisplayValue(value) + ''; + } positions += '
                                        • '; } this.ticks.html(positions); + if (this.options.ticksValuesTooltip) + $compile(this.ticks.contents())(this.scope); }, isTickSelected: function(value) { @@ -963,14 +971,13 @@ return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0; }, - /** + /** * Ensure that the position rendered is within the slider bounds, even if the value is not * * @param {number} val * @returns {number} */ - sanitizeOffsetValue: function(val) - { + sanitizeOffsetValue: function(val) { return Math.min(Math.max(val, this.minValue), this.maxValue); }, From a7c7f0e3d2f9e798df5652f43a8cff0a6aa23cf7 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Wed, 11 Nov 2015 10:49:32 +0100 Subject: [PATCH 13/15] Fix draggable range when floor is not 0 As explained in #172 . --- README.md | 2 +- dist/rzslider.js | 2 +- dist/rzslider.min.js | 2 +- src/rzslider.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index dd8f737..eab166d 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ $scope.slider = { **stepsArray** - _Array_: If you want to display a slider with non linear/number steps. Just pass an array with each slider value and that's it; the floor, ceil and step settings of the slider will be computed automatically. The `rz-slider-model` value will be the index of the selected item in the stepsArray. -**draggableRange** - _Boolean (defaults to false)_: When set to true and using a range slider, the range can be dragged by the selection bar. +**draggableRange** - _Boolean (defaults to false)_: When set to true and using a range slider, the range can be dragged by the selection bar. _This doesn't work when ticks are shown._ **showSelectionBar** - _Boolean (defaults to false)_: Set to true to always show the selection bar. diff --git a/dist/rzslider.js b/dist/rzslider.js index 0a15384..0411700 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -1202,7 +1202,7 @@ } newMinValue = this.minValue; newMinOffset = 0; - newMaxValue = this.dragging.difference; + newMaxValue = this.minValue + this.dragging.difference; newMaxOffset = this.valueToOffset(newMaxValue); } else if (newOffset >= this.maxLeft - this.dragging.highDist) { diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index ce3fb88..3ff878e 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ /*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,ticksValuesTooltip:null,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","$compile","RzSliderOptions","rzThrottle",function(b,c,d,e,f,g){var h=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return h.prototype={init:function(){var c,e,f=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",f),a.element(d).on("resize",f),this.initHasRun=!0,c=g(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.options.interval),e=g(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.options.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",f)})},applyOptions:function(){this.options=f.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),f=this.isTickSelected(d)?"selected":"";if(a+='
                                        • ',this.options.showTicksValues){var g="";this.options.ticksValuesTooltip&&(g='uib-tooltip="'+this.options.ticksValuesTooltip(d)+'"'),a+="'+this.getDisplayValue(d)+""}a+="
                                        • "}this.ticks.html(a),this.options.ticksValuesTooltip&&e(this.ticks.contents())(this.scope)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},h}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                            ')}]),b}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,ticksValuesTooltip:null,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","$compile","RzSliderOptions","rzThrottle",function(b,c,d,e,f,g){var h=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return h.prototype={init:function(){var c,e,f=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",f),a.element(d).on("resize",f),this.initHasRun=!0,c=g(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.options.interval),e=g(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.options.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",f)})},applyOptions:function(){this.options=f.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),f=this.isTickSelected(d)?"selected":"";if(a+='
                                          • ',this.options.showTicksValues){var g="";this.options.ticksValuesTooltip&&(g='uib-tooltip="'+this.options.ticksValuesTooltip(d)+'"'),a+="'+this.getDisplayValue(d)+""}a+="
                                          • "}this.ticks.html(a),this.options.ticksValuesTooltip&&e(this.ticks.contents())(this.scope)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.minValue+this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},h}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                              ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index 4ae8166..6fb7d17 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -1202,7 +1202,7 @@ } newMinValue = this.minValue; newMinOffset = 0; - newMaxValue = this.dragging.difference; + newMaxValue = this.minValue + this.dragging.difference; newMaxOffset = this.valueToOffset(newMaxValue); } else if (newOffset >= this.maxLeft - this.dragging.highDist) { From d03ac54c2ef0d533471e0e74e60d5e03459d7883 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Wed, 11 Nov 2015 13:05:11 +0100 Subject: [PATCH 14/15] Apply editorconfig formatting --- Gruntfile.js | 238 ++--- dist/rzslider.js | 2332 +++++++++++++++++++++++----------------------- src/rzslider.js | 2332 +++++++++++++++++++++++----------------------- 3 files changed, 2447 insertions(+), 2455 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 205e510..fd6d2d3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,137 +1,137 @@ module.exports = function(grunt) { - // Project configuration. - grunt.initConfig({ + // Project configuration. + grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), + pkg: grunt.file.readJSON('package.json'), - minBanner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + - '(c) <%= pkg.author %>, <%= pkg.repository.url %> - ' + - '<%= grunt.template.today("yyyy-mm-dd") %> */\n', + minBanner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + + '(c) <%= pkg.author %>, <%= pkg.repository.url %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %> */\n', - recess: { - options: { - compile: true - }, + recess: { + options: { + compile: true + }, - slider: { - src: ['src/rzslider.less'], - dest: 'dist/rzslider.css' - }, + slider: { + src: ['src/rzslider.less'], + dest: 'dist/rzslider.css' + }, - min: { - options: { - compress: true, - banner: '<%= minBanner %>' - }, - src: ['dist/rzslider.css'], - dest: 'dist/rzslider.min.css' - } + min: { + options: { + compress: true, + banner: '<%= minBanner %>' }, + src: ['dist/rzslider.css'], + dest: 'dist/rzslider.min.css' + } + }, - uglify: { - options: { - report: 'min', - banner: '<%= minBanner %>' - }, - rzslider: { - files: { - 'dist/rzslider.min.js': [ - 'dist/rzslider.js' - ] - } - } - }, + uglify: { + options: { + report: 'min', + banner: '<%= minBanner %>' + }, + rzslider: { + files: { + 'dist/rzslider.min.js': [ + 'dist/rzslider.js' + ] + } + } + }, - ngtemplates: { - app: { - src: 'src/**.html', - dest: 'temp/templates.js', - options: { - htmlmin: { - collapseBooleanAttributes: true, - collapseWhitespace: true, - removeAttributeQuotes: true, - removeComments: true, // Only if you don't use comment directives! - removeEmptyAttributes: true, - removeRedundantAttributes: true, - removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributes: true - }, - module: 'rzModule', - url: function(url) { - return url.replace('src/', ''); - }, - bootstrap: function(module, script) { - return 'module.run(function($templateCache) {\n' + script + '\n});'; - } - } - } - }, + ngtemplates: { + app: { + src: 'src/**.html', + dest: 'temp/templates.js', + options: { + htmlmin: { + collapseBooleanAttributes: true, + collapseWhitespace: true, + removeAttributeQuotes: true, + removeComments: true, // Only if you don't use comment directives! + removeEmptyAttributes: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true + }, + module: 'rzModule', + url: function(url) { + return url.replace('src/', ''); + }, + bootstrap: function(module, script) { + return 'module.run(function($templateCache) {\n' + script + '\n});'; + } + } + } + }, - replace: { - dist: { - options: { - patterns: [ - { - match: /\/\*templateReplacement\*\//, - replacement: '<%= grunt.file.read("temp/templates.js") %>' - } - ] - }, - files: [ - {expand: true, flatten: true, src: ['src/rzslider.js'], dest: 'dist/'} - ] - } + replace: { + dist: { + options: { + patterns: [{ + match: /\/\*templateReplacement\*\//, + replacement: '<%= grunt.file.read("temp/templates.js") %>' + }] }, + files: [{ + expand: true, + flatten: true, + src: ['src/rzslider.js'], + dest: 'dist/' + }] + } + }, - ngAnnotate: { - options: { - singleQuotes: true, - }, - rzslider: { - files: [{ - 'dist/rzslider.js': 'dist/rzslider.js' - }, { - expand: true, - src: ['dist/rzslider.js'] - } - ] - } - }, - watch: { - all: { - files: ['dist/*', 'demo/*'], - options: { - livereload: true - } - }, - js: { - files: ['src/*js', 'src/*.html'], - tasks: ['js'] - }, - less: { - files: ['src/*.less'], - tasks: ['css'] - } - }, - serve: { - options: { - port: 9000 - } + ngAnnotate: { + options: { + singleQuotes: true, + }, + rzslider: { + files: [{ + 'dist/rzslider.js': 'dist/rzslider.js' + }, { + expand: true, + src: ['dist/rzslider.js'] + }] + } + }, + watch: { + all: { + files: ['dist/*', 'demo/*'], + options: { + livereload: true } + }, + js: { + files: ['src/*js', 'src/*.html'], + tasks: ['js'] + }, + less: { + files: ['src/*.less'], + tasks: ['css'] + } + }, + serve: { + options: { + port: 9000 + } + } - }); + }); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-recess'); - grunt.loadNpmTasks('grunt-angular-templates'); - grunt.loadNpmTasks('grunt-replace'); - grunt.loadNpmTasks('grunt-ng-annotate'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-serve'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-recess'); + grunt.loadNpmTasks('grunt-angular-templates'); + grunt.loadNpmTasks('grunt-replace'); + grunt.loadNpmTasks('grunt-ng-annotate'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-serve'); - grunt.registerTask('default', ['css', 'js']); + grunt.registerTask('default', ['css', 'js']); - grunt.registerTask('css', ['recess']); - grunt.registerTask('js', ['ngtemplates', 'replace', 'ngAnnotate', 'uglify']); + grunt.registerTask('css', ['recess']); + grunt.registerTask('js', ['ngtemplates', 'replace', 'ngAnnotate', 'uglify']); }; diff --git a/dist/rzslider.js b/dist/rzslider.js index 0411700..655f223 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -31,53 +31,53 @@ '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, - id: null, - translate: null, - stepsArray: null, - draggableRange: false, - showSelectionBar: false, - hideLimitLabels: false, - readOnly: false, - disabled: false, - interval: 350, - showTicks: false, - showTicksValues: false, - ticksValuesTooltip: null, - scale: 1, - onStart: null, - onChange: null, - onEnd: 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('RzSliderOptions', function() { + var defaultOptions = { + floor: 0, + ceil: null, //defaults to rz-slider-model + step: 1, + precision: 0, + id: null, + translate: null, + stepsArray: null, + draggableRange: false, + showSelectionBar: false, + hideLimitLabels: false, + readOnly: false, + disabled: false, + interval: 350, + showTicks: false, + showTicksValues: false, + ticksValuesTooltip: null, + scale: 1, + onStart: null, + onChange: null, + onEnd: 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); - }; + factory.getOptions = function(options) { + return angular.extend({}, defaultOptions, globalOptions, options); + }; - return factory; - }) + return factory; + }) - .value('rzThrottle', + .value('rzThrottle', /** * rzThrottle * @@ -124,1242 +124,1238 @@ }; }) - .factory('RzSlider', ['$timeout', '$document', '$window', '$compile', 'RzSliderOptions', 'rzThrottle', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) { - 'use strict'; + .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) { /** - * Slider + * The slider's scope * - * @param {ngScope} scope The AngularJS scope - * @param {Element} sliderElem The slider directive element wrapped in jqLite - * @constructor + * @type {ngScope} */ - var Slider = function(scope, sliderElem) { - /** - * The slider's scope - * - * @type {ngScope} - */ - this.scope = scope; + this.scope = scope; - /** - * Slider element wrapped in jqLite - * - * @type {jqLite} - */ - this.sliderElem = sliderElem; + /** + * 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; + /** + * 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, - offset: 0, - lowDist: 0, - highDist: 0 - }; + /** + * Values recorded when first dragging the bar + * + * @type {Object} + */ + this.dragging = { + active: false, + value: 0, + difference: 0, + offset: 0, + lowDist: 0, + highDist: 0 + }; - /** - * Half of the width of the slider handles - * - * @type {number} - */ - this.handleHalfWidth = 0; + /** + * Half of the width of the slider handles + * + * @type {number} + */ + this.handleHalfWidth = 0; - /** - * Maximum left the slider handle can have - * - * @type {number} - */ - this.maxLeft = 0; + /** + * Maximum left the slider handle can have + * + * @type {number} + */ + this.maxLeft = 0; - /** - * Precision - * - * @type {number} - */ - this.precision = 0; + /** + * Precision + * + * @type {number} + */ + this.precision = 0; - /** - * Step - * - * @type {number} - */ - this.step = 0; + /** + * Step + * + * @type {number} + */ + this.step = 0; - /** - * The name of the handle we are currently tracking - * - * @type {string} - */ - this.tracking = ''; + /** + * The name of the handle we are currently tracking + * + * @type {string} + */ + this.tracking = ''; - /** - * Minimum value (floor) of the model - * - * @type {number} - */ - this.minValue = 0; + /** + * Minimum value (floor) of the model + * + * @type {number} + */ + this.minValue = 0; - /** - * Maximum value (ceiling) of the model - * - * @type {number} - */ - this.maxValue = 0; + /** + * Maximum value (ceiling) of the model + * + * @type {number} + */ + this.maxValue = 0; - /** - * The delta between min and max value - * - * @type {number} - */ - this.valueRange = 0; + /** + * The delta between min and max value + * + * @type {number} + */ + this.valueRange = 0; - /** - * Set to true if init method already executed - * - * @type {boolean} - */ - this.initHasRun = false; - - // 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(); - }; + /** + * Set to true if init method already executed + * + * @type {boolean} + */ + this.initHasRun = false; + + // 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 = { - // Add instance methods - Slider.prototype = { + /** + * Initialize slider + * + * @returns {undefined} + */ + init: function() { + var thrLow, thrHigh, + calcDimFn = angular.bind(this, this.calcViewDimensions), + self = this; + + this.applyOptions(); + this.initElemHandles(); + this.manageElementsStyle(); + this.addAccessibility(); + this.manageEventsBindings(); + this.setDisabledState(); + this.calcViewDimensions(); + this.setMinAndMax(); + + $timeout(function() { + self.updateCeilLab(); + self.updateFloorLab(); + self.initHandles(); + self.bindEvents(); + }); + + // 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.setMinAndMax(); + self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); + self.updateSelectionBar(); + self.updateTicksScale(); + + if (self.range) { + self.updateCmbLabel(); + } - /** - * Initialize slider - * - * @returns {undefined} - */ - init: function() { - var thrLow, thrHigh, - calcDimFn = angular.bind(this, this.calcViewDimensions), - self = this; - - this.applyOptions(); - this.initElemHandles(); - this.manageElementsStyle(); - this.addAccessibility(); - this.manageEventsBindings(); - this.setDisabledState(); - this.calcViewDimensions(); - this.setMinAndMax(); + }, self.options.interval); + + thrHigh = rzThrottle(function() { + self.setMinAndMax(); + self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); + self.updateSelectionBar(); + self.updateTicksScale(); + self.updateCmbLabel(); + }, self.options.interval); + + this.scope.$on('rzSliderForceRender', function() { + self.resetLabelsValue(); + thrLow(); + if (self.range) { + thrHigh(); + } + self.resetSlider(); + }); - $timeout(function() { - self.updateCeilLab(); - self.updateFloorLab(); - self.initHandles(); - self.bindEvents(); - }); + // Watchers + this.scope.$watch('rzSliderModel', function(newValue, oldValue) { + if (newValue === oldValue) + return; + thrLow(); + }); - // Recalculate slider view dimensions - this.scope.$on('reCalcViewDimensions', calcDimFn); + this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { + if (newValue === oldValue) + return; + if (newValue != null) + thrHigh(); + if (self.range && newValue == null || !self.range && newValue != null) { + self.applyOptions(); + self.resetSlider(); + } + }); - // Recalculate stuff if view port dimensions have changed - angular.element($window).on('resize', calcDimFn); + this.scope.$watch('rzSliderOptions', function(newValue, oldValue) { + if (newValue === oldValue) + return; + self.applyOptions(); + self.resetSlider(); + }, true); - this.initHasRun = true; + this.scope.$on('$destroy', function() { + self.unbindEvents(); + angular.element($window).off('resize', calcDimFn); + }); + }, - // Watch for changes to the model + /** + * Read the user options and apply them to the slider model + */ + applyOptions: function() { + this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions); - thrLow = rzThrottle(function() { - self.setMinAndMax(); - self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); - self.updateSelectionBar(); - self.updateTicksScale(); + 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.showTicks = this.options.showTicks || this.options.showTicksValues; + + if (this.options.stepsArray) { + this.options.floor = 0; + this.options.ceil = this.options.stepsArray.length - 1; + this.options.step = 1; + this.customTrFn = function(value) { + return this.options.stepsArray[value]; + }; + } else if (this.options.translate) + this.customTrFn = this.options.translate; + else + this.customTrFn = function(value) { + return String(value); + }; + }, - if (self.range) { - self.updateCmbLabel(); - } + /** + * Resets slider + * + * @returns {undefined} + */ + resetSlider: function() { + this.manageElementsStyle(); + this.setMinAndMax(); + this.updateCeilLab(); + this.updateFloorLab(); + this.unbindEvents(); + this.manageEventsBindings(); + this.setDisabledState(); + this.calcViewDimensions(); + }, - }, self.options.interval); + /** + * 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; + } - thrHigh = rzThrottle(function() { - self.setMinAndMax(); - self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); - self.updateSelectionBar(); - self.updateTicksScale(); - self.updateCmbLabel(); - }, self.options.interval); + }, this); - this.scope.$on('rzSliderForceRender', function() { - self.resetLabelsValue(); - thrLow(); - if (self.range) { - thrHigh(); - } - self.resetSlider(); - }); + // Initialize offset cache properties + this.selBar.rzsl = 0; + this.minH.rzsl = 0; + this.maxH.rzsl = 0; + this.flrLab.rzsl = 0; + this.ceilLab.rzsl = 0; + this.minLab.rzsl = 0; + this.maxLab.rzsl = 0; + this.cmbLab.rzsl = 0; + }, - // Watchers - this.scope.$watch('rzSliderModel', function(newValue, oldValue) { - if (newValue === oldValue) - return; - thrLow(); - }); + /** Update each elements style based on options + * + */ + manageElementsStyle: function() { + + if (!this.range) + this.maxH.css('display', 'none'); + else + this.maxH.css('display', null); + + this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels); + this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels); + this.alwaysHide(this.minLab, this.options.showTicksValues); + this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range); + this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range); + this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar); + + if (!this.options.showTicks) + this.ticks.html(''); + + if (this.options.draggableRange) + this.selBar.addClass('rz-draggable'); + else + this.selBar.removeClass('rz-draggable'); + }, + + alwaysHide: function(el, hide) { + el.rzAlwaysHide = hide; + if (hide) + this.hideEl(el); + else + this.showEl(el) + }, - this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { - if (newValue === oldValue) - return; - if (newValue != null) - thrHigh(); - if (self.range && newValue == null || !self.range && newValue != null) { - self.applyOptions(); - self.resetSlider(); - } - }); + /** + * Manage the events bindings based on readOnly and disabled options + * + * @returns {undefined} + */ + manageEventsBindings: function() { + if (this.options.disabled || this.options.readOnly) + this.unbindEvents(); + else if (!this.options.disabled || !this.options.readOnly) + this.bindEvents(); + }, - this.scope.$watch('rzSliderOptions', function(newValue, oldValue) { - if (newValue === oldValue) - return; - self.applyOptions(); - self.resetSlider(); - }, true); + /** + * 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); + } + }, - this.scope.$on('$destroy', function() { - self.unbindEvents(); - angular.element($window).off('resize', calcDimFn); - }); - }, + /** + * Reset label values + * + * @return {undefined} + */ + resetLabelsValue: function() { + this.minLab.rzsv = undefined; + this.maxLab.rzsv = undefined; + }, - /** - * Read the user options and apply them to the slider model - */ - applyOptions: function() { - this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions); - - 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.showTicks = this.options.showTicks || this.options.showTicksValues; - - if (this.options.stepsArray) { - this.options.floor = 0; - this.options.ceil = this.options.stepsArray.length - 1; - this.options.step = 1; - this.customTrFn = function(value) { - return this.options.stepsArray[value]; - }; - } - else if (this.options.translate) - this.customTrFn = this.options.translate; - else - this.customTrFn = function(value) { - return String(value); - }; - }, - - /** - * Resets slider - * - * @returns {undefined} - */ - resetSlider: function() { - this.manageElementsStyle(); - this.setMinAndMax(); - this.updateCeilLab(); - this.updateFloorLab(); - this.unbindEvents(); - this.manageEventsBindings(); - this.setDisabledState(); - this.calcViewDimensions(); - }, - - /** - * 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; - } + /** + * 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.valueToOffset(this.scope.rzSliderModel)); - }, this); - - // Initialize offset cache properties - this.selBar.rzsl = 0; - this.minH.rzsl = 0; - this.maxH.rzsl = 0; - this.flrLab.rzsl = 0; - this.ceilLab.rzsl = 0; - this.minLab.rzsl = 0; - this.maxLab.rzsl = 0; - this.cmbLab.rzsl = 0; - }, - - /** Update each elements style based on options - * - */ - manageElementsStyle: function() { - - if (!this.range) - this.maxH.css('display', 'none'); - else - this.maxH.css('display', null); - - this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels); - this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels); - this.alwaysHide(this.minLab, this.options.showTicksValues); - this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range); - this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range); - this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar); - - if (!this.options.showTicks) - this.ticks.html(''); - - if (this.options.draggableRange) - this.selBar.addClass('rz-draggable'); - else - this.selBar.removeClass('rz-draggable'); - }, - - 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} + /* + the order here is important since the selection bar should be + updated after the high handle but before the combined label */ - manageEventsBindings: function() { - if (this.options.disabled || this.options.readOnly) - this.unbindEvents(); - else if (!this.options.disabled || !this.options.readOnly) - 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); - } - }, + if (this.range) + this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)); + this.updateSelectionBar(); + if (this.range) + this.updateCmbLabel(); - /** - * 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.valueToOffset(this.scope.rzSliderModel)); - - /* - 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.valueToOffset(this.scope.rzSliderHigh)); - this.updateSelectionBar(); - if (this.range) - this.updateCmbLabel(); + this.updateTicksScale(); + }, - this.updateTicksScale(); - }, - - /** - * Translate value to human readable format - * - * @param {number|string} value - * @param {jqLite} label - * @param {boolean} [useCustomTr] - * @returns {undefined} - */ - translateFn: function(value, label, useCustomTr) { - useCustomTr = useCustomTr === undefined ? true : useCustomTr; + /** + * Translate value to human readable format + * + * @param {number|string} value + * @param {jqLite} label + * @param {boolean} [useCustomTr] + * @returns {undefined} + */ + translateFn: function(value, label, useCustomTr) { + useCustomTr = useCustomTr === undefined ? true : useCustomTr; - var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)), - getWidth = false; + var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)), + getWidth = false; - if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) { - getWidth = true; - label.rzsv = valStr; - } + if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) { + getWidth = true; + label.rzsv = valStr; + } - label.text(valStr); + label.text(valStr); - // Update width only when length of the label have changed - if (getWidth) { - this.getWidth(label); - } - }, + // Update width only when length of the label have changed + if (getWidth) { + this.getWidth(label); + } + }, - /** - * Set maximum and minimum values for the slider and ensure the model and high - * value match these limits - * @returns {undefined} - */ - setMinAndMax: function() { + /** + * 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.step = +this.options.step; + this.precision = +this.options.precision; - this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel); - if (this.range) - this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); + this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel); + if (this.range) + this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); - this.minValue = this.roundStep(+this.options.floor); + this.minValue = this.roundStep(+this.options.floor); - if (this.options.ceil) - this.maxValue = this.roundStep(+this.options.ceil); - else - this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; + if (this.options.ceil) + this.maxValue = this.roundStep(+this.options.ceil); + else + this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; - this.valueRange = this.maxValue - this.minValue; - }, + this.valueRange = this.maxValue - this.minValue; + }, - /** - * Adds accessibility atributes - * - * Run only once during initialization - * - * @returns {undefined} - */ - addAccessibility: function() { - this.sliderElem.attr("role", "slider"); - }, - - /** - * 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.getWidth(this.minH); + /** + * Adds accessibility atributes + * + * Run only once during initialization + * + * @returns {undefined} + */ + addAccessibility: function() { + this.sliderElem.attr("role", "slider"); + }, - this.handleHalfWidth = handleWidth / 2; - this.barWidth = this.getWidth(this.fullBar); + /** + * 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.getWidth(this.minH); - this.maxLeft = this.barWidth - handleWidth; + this.handleHalfWidth = handleWidth / 2; + this.barWidth = this.getWidth(this.fullBar); - this.getWidth(this.sliderElem); - this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left; + this.maxLeft = this.barWidth - handleWidth; - if (this.initHasRun) { - this.updateFloorLab(); - this.updateCeilLab(); - this.initHandles(); - } - }, + this.getWidth(this.sliderElem); + this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left; - /** - * Update the ticks position - * - * @returns {undefined} - */ - updateTicksScale: function() { - if (!this.options.showTicks) return; - if (!this.step) return; //if step is 0, the following loop will be endless. - - var positions = '', - ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1; - for (var i = 0; i < ticksCount; i++) { - var value = this.roundStep(this.minValue + i * this.step); - var selectedClass = this.isTickSelected(value) ? 'selected' : ''; - positions += '
                                            • '; - if (this.options.showTicksValues) { - var tooltip = ''; - if (this.options.ticksValuesTooltip) { - tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"'; - } - positions += '' + this.getDisplayValue(value) + ''; + if (this.initHasRun) { + this.updateFloorLab(); + this.updateCeilLab(); + this.initHandles(); + } + }, + + /** + * Update the ticks position + * + * @returns {undefined} + */ + updateTicksScale: function() { + if (!this.options.showTicks) return; + if (!this.step) return; //if step is 0, the following loop will be endless. + + var positions = '', + ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1; + for (var i = 0; i < ticksCount; i++) { + var value = this.roundStep(this.minValue + i * this.step); + var selectedClass = this.isTickSelected(value) ? 'selected' : ''; + positions += '
                                            • '; + if (this.options.showTicksValues) { + var tooltip = ''; + if (this.options.ticksValuesTooltip) { + tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"'; } - positions += '
                                            • '; - } - this.ticks.html(positions); - if (this.options.ticksValuesTooltip) - $compile(this.ticks.contents())(this.scope); - }, - - isTickSelected: function(value) { - if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel) - return true; - if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh) - return true; - return false; - }, - - /** - * Update position of the ceiling label - * - * @returns {undefined} - */ - updateCeilLab: function() { - this.translateFn(this.maxValue, this.ceilLab); - this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw); - this.getWidth(this.ceilLab); - }, - - /** - * Update position of the floor label - * - * @returns {undefined} - */ - updateFloorLab: function() { - this.translateFn(this.minValue, this.flrLab); - this.getWidth(this.flrLab); - }, - - /** - * Call the onStart callback if defined - * - * @returns {undefined} - */ - callOnStart: function() { - if (this.options.onStart) { - var self = this; - $timeout(function() { - self.options.onStart(); - }); + positions += '' + this.getDisplayValue(value) + ''; } - }, + positions += ''; + } + this.ticks.html(positions); + if (this.options.ticksValuesTooltip) + $compile(this.ticks.contents())(this.scope); + }, + + isTickSelected: function(value) { + if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel) + return true; + if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh) + return true; + return false; + }, - /** - * Call the onChange callback if defined - * - * @returns {undefined} - */ - callOnChange: function() { - if (this.options.onChange) { - var self = this; - $timeout(function() { - self.options.onChange(); - }); - } - }, + /** + * Update position of the ceiling label + * + * @returns {undefined} + */ + updateCeilLab: function() { + this.translateFn(this.maxValue, this.ceilLab); + this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw); + this.getWidth(this.ceilLab); + }, - /** - * Call the onEnd callback if defined - * - * @returns {undefined} - */ - callOnEnd: function() { - if (this.options.onEnd) { - var self = this; - $timeout(function() { - self.options.onEnd(); - }); - } - }, + /** + * Update position of the floor label + * + * @returns {undefined} + */ + updateFloorLab: function() { + this.translateFn(this.minValue, this.flrLab); + this.getWidth(this.flrLab); + }, - /** - * Update slider handles and label positions - * - * @param {string} which - * @param {number} newOffset - */ - updateHandles: function(which, newOffset) { - if (which === 'rzSliderModel') { - this.updateLowHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); - - if (this.range) { - this.updateCmbLabel(); - } - return; - } + /** + * Call the onStart callback if defined + * + * @returns {undefined} + */ + callOnStart: function() { + if (this.options.onStart) { + var self = this; + $timeout(function() { + self.options.onStart(); + }); + } + }, - if (which === 'rzSliderHigh') { - this.updateHighHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); + /** + * Call the onChange callback if defined + * + * @returns {undefined} + */ + callOnChange: function() { + if (this.options.onChange) { + var self = this; + $timeout(function() { + self.options.onChange(); + }); + } + }, - if (this.range) { - this.updateCmbLabel(); - } - return; - } + /** + * Call the onEnd callback if defined + * + * @returns {undefined} + */ + callOnEnd: function() { + if (this.options.onEnd) { + var self = this; + $timeout(function() { + self.options.onEnd(); + }); + } + }, - // Update both + /** + * Update slider handles and label positions + * + * @param {string} which + * @param {number} newOffset + */ + updateHandles: function(which, newOffset) { + if (which === 'rzSliderModel') { this.updateLowHandle(newOffset); - this.updateHighHandle(newOffset); this.updateSelectionBar(); this.updateTicksScale(); - this.updateCmbLabel(); - }, - - /** - * Update low slider handle position and label - * - * @param {number} newOffset - * @returns {undefined} - */ - updateLowHandle: function(newOffset) { - this.setLeft(this.minH, newOffset); - this.translateFn(this.scope.rzSliderModel, this.minLab); - this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth); - - this.shFloorCeil(); - }, - - /** - * Update high slider handle position and label - * - * @param {number} newOffset - * @returns {undefined} - */ - updateHighHandle: function(newOffset) { - this.setLeft(this.maxH, newOffset); - this.translateFn(this.scope.rzSliderHigh, this.maxLab); - this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth); - - this.shFloorCeil(); - }, - - /** - * Show / hide floor / ceiling label - * - * @returns {undefined} - */ - shFloorCeil: function() { - var flHidden = false, clHidden = false; - if (this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5) { - flHidden = true; - this.hideEl(this.flrLab); + if (this.range) { + this.updateCmbLabel(); } - else { - flHidden = false; - this.showEl(this.flrLab); + return; + } + + if (which === 'rzSliderHigh') { + this.updateHighHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); + + if (this.range) { + this.updateCmbLabel(); } + return; + } + + // Update both + this.updateLowHandle(newOffset); + this.updateHighHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); + this.updateCmbLabel(); + }, + + /** + * Update low slider handle position and label + * + * @param {number} newOffset + * @returns {undefined} + */ + updateLowHandle: function(newOffset) { + this.setLeft(this.minH, newOffset); + this.translateFn(this.scope.rzSliderModel, this.minLab); + this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth); + + this.shFloorCeil(); + }, + + /** + * Update high slider handle position and label + * + * @param {number} newOffset + * @returns {undefined} + */ + updateHighHandle: function(newOffset) { + this.setLeft(this.maxH, newOffset); + this.translateFn(this.scope.rzSliderHigh, this.maxLab); + this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth); + + this.shFloorCeil(); + }, + + /** + * Show / hide floor / ceiling label + * + * @returns {undefined} + */ + shFloorCeil: function() { + var flHidden = false, + clHidden = false; + + if (this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5) { + flHidden = true; + this.hideEl(this.flrLab); + } else { + flHidden = false; + this.showEl(this.flrLab); + } + + if (this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) { + clHidden = true; + this.hideEl(this.ceilLab); + } else { + clHidden = false; + this.showEl(this.ceilLab); + } - if (this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) { - clHidden = true; + if (this.range) { + if (this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) { this.hideEl(this.ceilLab); - } - else { - clHidden = false; + } else if (!clHidden) { this.showEl(this.ceilLab); } - if (this.range) { - if (this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) { - this.hideEl(this.ceilLab); - } - else if (!clHidden) { - this.showEl(this.ceilLab); - } - - // Hide or show floor label - if (this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth) { - this.hideEl(this.flrLab); - } - else if (!flHidden) { - this.showEl(this.flrLab); - } + // Hide or show floor label + if (this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth) { + this.hideEl(this.flrLab); + } else if (!flHidden) { + this.showEl(this.flrLab); } - }, + } + }, - /** - * Update slider selection bar, combined label and range label - * - * @returns {undefined} - */ - updateSelectionBar: function() { - this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth); - this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0); - }, - - /** - * Update combined label position and value - * - * @returns {undefined} - */ - updateCmbLabel: function() { - var lowTr, highTr; - - if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) { - lowTr = this.getDisplayValue(this.scope.rzSliderModel); - highTr = this.getDisplayValue(this.scope.rzSliderHigh); - - this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false); - this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2); - this.hideEl(this.minLab); - this.hideEl(this.maxLab); - this.showEl(this.cmbLab); - } - else { - this.showEl(this.maxLab); - this.showEl(this.minLab); - this.hideEl(this.cmbLab); - } - }, + /** + * Update slider selection bar, combined label and range label + * + * @returns {undefined} + */ + updateSelectionBar: function() { + this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth); + this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0); + }, - /** - * Return the translated value if a translate function is provided else the original value - * @param value - * @returns {*} - */ - getDisplayValue: function(value) { - return this.customTrFn(value, this.options.id); - }, - - /** - * Round value to step and precision - * - * @param {number} value - * @returns {number} - */ - roundStep: function(value) { - var step = this.step, - remainder = +((value - this.minValue) % step).toFixed(3), - steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder; - - steppedValue = steppedValue.toFixed(this.precision); - return +steppedValue; - }, - - /** - * Hide element - * - * @param element - * @returns {jqLite} The jqLite wrapped DOM element - */ - hideEl: function(element) { - return element.css({opacity: 0}); - }, - - /** - * Show element - * - * @param element The jqLite wrapped DOM element - * @returns {jqLite} The jqLite - */ - showEl: function(element) { - if (!!element.rzAlwaysHide) { - return element; - } + /** + * Update combined label position and value + * + * @returns {undefined} + */ + updateCmbLabel: function() { + var lowTr, highTr; + + if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) { + lowTr = this.getDisplayValue(this.scope.rzSliderModel); + highTr = this.getDisplayValue(this.scope.rzSliderHigh); + + this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false); + this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2); + this.hideEl(this.minLab); + this.hideEl(this.maxLab); + this.showEl(this.cmbLab); + } else { + this.showEl(this.maxLab); + this.showEl(this.minLab); + this.hideEl(this.cmbLab); + } + }, - return element.css({opacity: 1}); - }, + /** + * Return the translated value if a translate function is provided else the original value + * @param value + * @returns {*} + */ + getDisplayValue: function(value) { + return this.customTrFn(value, this.options.id); + }, - /** - * Set element left offset - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @param {number} left - * @returns {number} - */ - setLeft: function(elem, left) { - elem.rzsl = left; - elem.css({left: left + 'px'}); - return left; - }, - - /** - * Get element width - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @returns {number} - */ - getWidth: function(elem) { - var val = elem[0].getBoundingClientRect(); - elem.rzsw = (val.right - val.left) * this.options.scale; - return elem.rzsw; - }, - - /** - * Set element width - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @param {number} width - * @returns {number} - */ - setWidth: function(elem, width) { - elem.rzsw = width; - elem.css({width: width + 'px'}); - return width; - }, - - /** - * Translate value to pixel offset - * - * @param {number} val - * @returns {number} - */ - valueToOffset: function(val) { - return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0; - }, - - /** - * Ensure that the position rendered is within the slider bounds, even if the value is not - * - * @param {number} val - * @returns {number} - */ - sanitizeOffsetValue: function(val) { - return Math.min(Math.max(val, this.minValue), this.maxValue); - }, - - /** - * Translate offset to model value - * - * @param {number} offset - * @returns {number} - */ - offsetToValue: function(offset) { - return (offset / this.maxLeft) * this.valueRange + this.minValue; - }, + /** + * Round value to step and precision + * + * @param {number} value + * @returns {number} + */ + roundStep: function(value) { + var step = this.step, + remainder = +((value - this.minValue) % step).toFixed(3), + steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder; - // Events + steppedValue = steppedValue.toFixed(this.precision); + return +steppedValue; + }, - /** - * Get the X-coordinate of an event - * - * @param {Object} event The event - * @returns {number} - */ - getEventX: function(event) { - /* http://stackoverflow.com/a/12336075/282882 */ - //noinspection JSLint - if ('clientX' in event) { - return event.clientX; - } + /** + * Hide element + * + * @param element + * @returns {jqLite} The jqLite wrapped DOM element + */ + hideEl: function(element) { + return element.css({ + opacity: 0 + }); + }, - return event.originalEvent === undefined ? - event.touches[0].clientX - : event.originalEvent.touches[0].clientX; - }, + /** + * Show element + * + * @param element The jqLite wrapped DOM element + * @returns {jqLite} The jqLite + */ + showEl: function(element) { + if (!!element.rzAlwaysHide) { + return element; + } - /** - * 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 offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale; - return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; - }, - - /** - * Bind mouse and touch events to slider handles - * - * @returns {undefined} - */ - bindEvents: function() { - if (this.options.readOnly || this.options.disabled) return; - var barTracking, barStart, barMove; - - if (this.options.draggableRange) { - barTracking = 'rzSliderDrag'; - barStart = this.onDragStart; - barMove = this.onDragMove; - } - else { - barTracking = 'rzSliderModel'; - barStart = this.onStart; - barMove = this.onMove; - } + return element.css({ + opacity: 1 + }); + }, - this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); - if (this.range) { - this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); - } - this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); - this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); - this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); - this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); - this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); - this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks)); - - this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); - if (this.range) { - this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); - } - this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); - this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); - this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); - this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); - this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); - this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); - }, - - /** - * 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); + /** + * Set element left offset + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @param {number} left + * @returns {number} + */ + setLeft: function(elem, left) { + elem.rzsl = left; + elem.css({ + left: left + 'px' + }); + return left; + }, - event.stopPropagation(); - event.preventDefault(); + /** + * Get element width + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @returns {number} + */ + getWidth: function(elem) { + var val = elem[0].getBoundingClientRect(); + elem.rzsw = (val.right - val.left) * this.options.scale; + return elem.rzsw; + }, - if (this.tracking !== '') { - return; - } + /** + * Set element width + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @param {number} width + * @returns {number} + */ + setWidth: function(elem, width) { + elem.rzsw = width; + elem.css({ + width: width + 'px' + }); + return width; + }, - // We have to do this in case the HTML where the sliders are on - // have been animated into view. - this.calcViewDimensions(); + /** + * Translate value to pixel offset + * + * @param {number} val + * @returns {number} + */ + valueToOffset: function(val) { + return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0; + }, - if (pointer) { - this.tracking = ref; - } - else { - pointer = this.getNearestHandle(event); - this.tracking = pointer === this.minH ? 'rzSliderModel' : 'rzSliderHigh'; - } + /** + * Ensure that the position rendered is within the slider bounds, even if the value is not + * + * @param {number} val + * @returns {number} + */ + sanitizeOffsetValue: function(val) { + return Math.min(Math.max(val, this.minValue), this.maxValue); + }, - pointer.addClass('rz-active'); + /** + * Translate offset to model value + * + * @param {number} offset + * @returns {number} + */ + offsetToValue: function(offset) { + return (offset / this.maxLeft) * this.valueRange + this.minValue; + }, - ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer); - ehEnd = angular.bind(this, this.onEnd, ehMove); + // Events - $document.on(eventNames.moveEvent, ehMove); - $document.one(eventNames.endEvent, ehEnd); - this.callOnStart(); - }, + /** + * Get the X-coordinate of an event + * + * @param {Object} event The event + * @returns {number} + */ + getEventX: function(event) { + /* http://stackoverflow.com/a/12336075/282882 */ + //noinspection JSLint + if ('clientX' in event) { + return event.clientX; + } - /** - * onMove event handler - * - * @param {jqLite} pointer - * @param {Event} event The event - * @returns {undefined} - */ - onMove: function(pointer, event) { - var eventX = this.getEventX(event), - sliderLO, newOffset, newValue; - - sliderLO = this.sliderElem.rzsl; - newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale; - - if (newOffset <= 0) { - if (pointer.rzsl === 0) - return; - newValue = this.minValue; - newOffset = 0; - } - else if (newOffset >= this.maxLeft) { - if (pointer.rzsl === this.maxLeft) - return; - newValue = this.maxValue; - newOffset = this.maxLeft; - } - else { - newValue = this.offsetToValue(newOffset); - newValue = this.roundStep(newValue); - newOffset = this.valueToOffset(newValue); - } - this.positionTrackingHandle(newValue, newOffset); - }, - - /** - * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; - this.dragging = { - active: true, - value: this.offsetToValue(offset), - difference: this.scope.rzSliderHigh - this.scope.rzSliderModel, - offset: offset, - lowDist: offset - this.minH.rzsl, - highDist: this.maxH.rzsl - offset - }; - this.minH.addClass('rz-active'); - this.maxH.addClass('rz-active'); - - this.onStart(pointer, ref, event); - }, - - /** - * onDragMove event handler - * - * Handles dragging of the middle bar. - * - * @param {jqLite} pointer - * @param {Event} event The event - * @returns {undefined} - */ - onDragMove: function(pointer, event) { - var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth, - newMinOffset, newMaxOffset, - newMinValue, newMaxValue; - - if (newOffset <= this.dragging.lowDist) { - if (pointer.rzsl === this.dragging.lowDist) { - return; - } - newMinValue = this.minValue; - newMinOffset = 0; - newMaxValue = this.minValue + this.dragging.difference; - newMaxOffset = this.valueToOffset(newMaxValue); - } - else if (newOffset >= this.maxLeft - this.dragging.highDist) { - if (pointer.rzsl === this.dragging.highDist) { - return; - } - newMaxValue = this.maxValue; - newMaxOffset = this.maxLeft; - newMinValue = this.maxValue - this.dragging.difference; - newMinOffset = this.valueToOffset(newMinValue); - } - else { - newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist); - newMinValue = this.roundStep(newMinValue); - newMinOffset = this.valueToOffset(newMinValue); - newMaxValue = newMinValue + this.dragging.difference; - newMaxOffset = this.valueToOffset(newMaxValue); - } + return event.originalEvent === undefined ? + event.touches[0].clientX : event.originalEvent.touches[0].clientX; + }, - this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset); - }, + /** + * 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 offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale; + return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; + }, - /** - * Set the new value and offset for the entire bar - * - * @param {number} newMinValue the new minimum value - * @param {number} newMaxValue the new maximum value - * @param {number} newMinOffset the new minimum offset - * @param {number} newMaxOffset the new maximum offset - */ - positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) { - this.scope.rzSliderModel = newMinValue; - this.scope.rzSliderHigh = newMaxValue; - this.updateHandles('rzSliderModel', newMinOffset); - this.updateHandles('rzSliderHigh', newMaxOffset); - this.scope.$apply(); - this.callOnChange(); - }, + /** + * Bind mouse and touch events to slider handles + * + * @returns {undefined} + */ + bindEvents: function() { + if (this.options.readOnly || this.options.disabled) return; + var barTracking, barStart, barMove; + + if (this.options.draggableRange) { + barTracking = 'rzSliderDrag'; + barStart = this.onDragStart; + barMove = this.onDragMove; + } else { + barTracking = 'rzSliderModel'; + barStart = this.onStart; + barMove = this.onMove; + } - /** - * Set the new value and offset to the current tracking handle - * - * @param {number} newValue new model value - * @param {number} newOffset new offset value - */ - positionTrackingHandle: function(newValue, newOffset) { - if (this.range) { - /* This is to check if we need to switch the min and max handles*/ - if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh) { - this.scope[this.tracking] = this.scope.rzSliderHigh; - this.updateHandles(this.tracking, this.maxH.rzsl); - this.tracking = 'rzSliderHigh'; - this.minH.removeClass('rz-active'); - this.maxH.addClass('rz-active'); - /* We need to apply here because we are not sure that we will enter the next block */ - this.scope.$apply(); - this.callOnChange(); - } - else if (this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) { - this.scope[this.tracking] = this.scope.rzSliderModel; - this.updateHandles(this.tracking, this.minH.rzsl); - this.tracking = 'rzSliderModel'; - this.maxH.removeClass('rz-active'); - this.minH.addClass('rz-active'); - /* We need to apply here because we are not sure that we will enter the next block */ - this.scope.$apply(); - this.callOnChange(); - } + this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); + if (this.range) { + this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); + } + this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); + this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); + this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); + this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); + this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); + this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks)); + + this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); + if (this.range) { + this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); + } + this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); + this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); + this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); + this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); + this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); + this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); + }, + + /** + * 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(); + + if (this.tracking !== '') { + return; + } + + // 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 ? 'rzSliderModel' : 'rzSliderHigh'; + } + + pointer.addClass('rz-active'); + + 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.one(eventNames.endEvent, ehEnd); + this.callOnStart(); + }, + + /** + * onMove event handler + * + * @param {jqLite} pointer + * @param {Event} event The event + * @returns {undefined} + */ + onMove: function(pointer, event) { + var eventX = this.getEventX(event), + sliderLO, newOffset, newValue; + + sliderLO = this.sliderElem.rzsl; + newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale; + + if (newOffset <= 0) { + if (pointer.rzsl === 0) + return; + newValue = this.minValue; + newOffset = 0; + } else if (newOffset >= this.maxLeft) { + if (pointer.rzsl === this.maxLeft) + return; + newValue = this.maxValue; + newOffset = this.maxLeft; + } else { + newValue = this.offsetToValue(newOffset); + newValue = this.roundStep(newValue); + newOffset = this.valueToOffset(newValue); + } + this.positionTrackingHandle(newValue, newOffset); + }, + + /** + * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; + this.dragging = { + active: true, + value: this.offsetToValue(offset), + difference: this.scope.rzSliderHigh - this.scope.rzSliderModel, + offset: offset, + lowDist: offset - this.minH.rzsl, + highDist: this.maxH.rzsl - offset + }; + this.minH.addClass('rz-active'); + this.maxH.addClass('rz-active'); + + this.onStart(pointer, ref, event); + }, + + /** + * onDragMove event handler + * + * Handles dragging of the middle bar. + * + * @param {jqLite} pointer + * @param {Event} event The event + * @returns {undefined} + */ + onDragMove: function(pointer, event) { + var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth, + newMinOffset, newMaxOffset, + newMinValue, newMaxValue; + + if (newOffset <= this.dragging.lowDist) { + if (pointer.rzsl === this.dragging.lowDist) { + return; + } + newMinValue = this.minValue; + newMinOffset = 0; + newMaxValue = this.minValue + this.dragging.difference; + newMaxOffset = this.valueToOffset(newMaxValue); + } else if (newOffset >= this.maxLeft - this.dragging.highDist) { + if (pointer.rzsl === this.dragging.highDist) { + return; } + newMaxValue = this.maxValue; + newMaxOffset = this.maxLeft; + newMinValue = this.maxValue - this.dragging.difference; + newMinOffset = this.valueToOffset(newMinValue); + } else { + newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist); + newMinValue = this.roundStep(newMinValue); + newMinOffset = this.valueToOffset(newMinValue); + newMaxValue = newMinValue + this.dragging.difference; + newMaxOffset = this.valueToOffset(newMaxValue); + } + + this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset); + }, + + /** + * Set the new value and offset for the entire bar + * + * @param {number} newMinValue the new minimum value + * @param {number} newMaxValue the new maximum value + * @param {number} newMinOffset the new minimum offset + * @param {number} newMaxOffset the new maximum offset + */ + positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) { + this.scope.rzSliderModel = newMinValue; + this.scope.rzSliderHigh = newMaxValue; + this.updateHandles('rzSliderModel', newMinOffset); + this.updateHandles('rzSliderHigh', newMaxOffset); + this.scope.$apply(); + this.callOnChange(); + }, - if (this.scope[this.tracking] !== newValue) { - this.scope[this.tracking] = newValue; - this.updateHandles(this.tracking, newOffset); + /** + * Set the new value and offset to the current tracking handle + * + * @param {number} newValue new model value + * @param {number} newOffset new offset value + */ + positionTrackingHandle: function(newValue, newOffset) { + if (this.range) { + /* This is to check if we need to switch the min and max handles*/ + if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh) { + this.scope[this.tracking] = this.scope.rzSliderHigh; + this.updateHandles(this.tracking, this.maxH.rzsl); + this.tracking = 'rzSliderHigh'; + this.minH.removeClass('rz-active'); + this.maxH.addClass('rz-active'); + /* We need to apply here because we are not sure that we will enter the next block */ + this.scope.$apply(); + this.callOnChange(); + } else if (this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) { + this.scope[this.tracking] = this.scope.rzSliderModel; + this.updateHandles(this.tracking, this.minH.rzsl); + this.tracking = 'rzSliderModel'; + this.maxH.removeClass('rz-active'); + this.minH.addClass('rz-active'); + /* We need to apply here because we are not sure that we will enter the next block */ this.scope.$apply(); this.callOnChange(); } - }, - - /** - * onEnd event handler - * - * @param {Event} event The event - * @param {Function} ehMove The the bound move event handler - * @returns {undefined} - */ - onEnd: function(ehMove, event) { - var moveEventName = this.getEventNames(event).moveEvent; + } - this.minH.removeClass('rz-active'); - this.maxH.removeClass('rz-active'); + if (this.scope[this.tracking] !== newValue) { + this.scope[this.tracking] = newValue; + this.updateHandles(this.tracking, newOffset); + this.scope.$apply(); + this.callOnChange(); + } + }, - $document.off(moveEventName, ehMove); + /** + * onEnd event handler + * + * @param {Event} event The event + * @param {Function} ehMove The the bound move event handler + * @returns {undefined} + */ + onEnd: function(ehMove, event) { + var moveEventName = this.getEventNames(event).moveEvent; - this.scope.$emit('slideEnded'); - this.tracking = ''; + this.minH.removeClass('rz-active'); + this.maxH.removeClass('rz-active'); - this.dragging.active = false; - this.callOnEnd(); - }, + $document.off(moveEventName, ehMove); - /** - * 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: ''}; + this.scope.$emit('slideEnded'); + this.tracking = ''; - if (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) { - eventNames.moveEvent = 'touchmove'; - eventNames.endEvent = 'touchend'; - } - else { - eventNames.moveEvent = 'mousemove'; - eventNames.endEvent = 'mouseup'; - } + this.dragging.active = false; + this.callOnEnd(); + }, - return eventNames; + /** + * 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 (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) { + eventNames.moveEvent = 'touchmove'; + eventNames.endEvent = 'touchend'; + } else { + eventNames.moveEvent = 'mousemove'; + eventNames.endEvent = 'mouseup'; } - }; - return Slider; - }]) + return eventNames; + } + }; - .directive('rzslider', ['RzSlider', function(RzSlider) { - 'use strict'; + return Slider; + }]) - return { - restrict: 'E', - 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'; - }, + .directive('rzslider', ['RzSlider', function(RzSlider) { + 'use strict'; - link: function(scope, elem) { - return new RzSlider(scope, elem); - } - }; - }]); + return { + restrict: 'E', + scope: { + rzSliderModel: '=?', + rzSliderHigh: '=?', + rzSliderOptions: '=?', + rzSliderTplUrl: '@' + }, -// IDE assist + /** + * 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) { + return new RzSlider(scope, elem); + } + }; + }]); + + // IDE assist /** * @name ngScope diff --git a/src/rzslider.js b/src/rzslider.js index 6fb7d17..56af511 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -31,53 +31,53 @@ '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, - id: null, - translate: null, - stepsArray: null, - draggableRange: false, - showSelectionBar: false, - hideLimitLabels: false, - readOnly: false, - disabled: false, - interval: 350, - showTicks: false, - showTicksValues: false, - ticksValuesTooltip: null, - scale: 1, - onStart: null, - onChange: null, - onEnd: 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('RzSliderOptions', function() { + var defaultOptions = { + floor: 0, + ceil: null, //defaults to rz-slider-model + step: 1, + precision: 0, + id: null, + translate: null, + stepsArray: null, + draggableRange: false, + showSelectionBar: false, + hideLimitLabels: false, + readOnly: false, + disabled: false, + interval: 350, + showTicks: false, + showTicksValues: false, + ticksValuesTooltip: null, + scale: 1, + onStart: null, + onChange: null, + onEnd: 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); - }; + factory.getOptions = function(options) { + return angular.extend({}, defaultOptions, globalOptions, options); + }; - return factory; - }) + return factory; + }) - .value('rzThrottle', + .value('rzThrottle', /** * rzThrottle * @@ -124,1242 +124,1238 @@ }; }) - .factory('RzSlider', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) { - 'use strict'; + .factory('RzSlider', 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) { /** - * Slider + * The slider's scope * - * @param {ngScope} scope The AngularJS scope - * @param {Element} sliderElem The slider directive element wrapped in jqLite - * @constructor + * @type {ngScope} */ - var Slider = function(scope, sliderElem) { - /** - * The slider's scope - * - * @type {ngScope} - */ - this.scope = scope; + this.scope = scope; - /** - * Slider element wrapped in jqLite - * - * @type {jqLite} - */ - this.sliderElem = sliderElem; + /** + * 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; + /** + * 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, - offset: 0, - lowDist: 0, - highDist: 0 - }; + /** + * Values recorded when first dragging the bar + * + * @type {Object} + */ + this.dragging = { + active: false, + value: 0, + difference: 0, + offset: 0, + lowDist: 0, + highDist: 0 + }; - /** - * Half of the width of the slider handles - * - * @type {number} - */ - this.handleHalfWidth = 0; + /** + * Half of the width of the slider handles + * + * @type {number} + */ + this.handleHalfWidth = 0; - /** - * Maximum left the slider handle can have - * - * @type {number} - */ - this.maxLeft = 0; + /** + * Maximum left the slider handle can have + * + * @type {number} + */ + this.maxLeft = 0; - /** - * Precision - * - * @type {number} - */ - this.precision = 0; + /** + * Precision + * + * @type {number} + */ + this.precision = 0; - /** - * Step - * - * @type {number} - */ - this.step = 0; + /** + * Step + * + * @type {number} + */ + this.step = 0; - /** - * The name of the handle we are currently tracking - * - * @type {string} - */ - this.tracking = ''; + /** + * The name of the handle we are currently tracking + * + * @type {string} + */ + this.tracking = ''; - /** - * Minimum value (floor) of the model - * - * @type {number} - */ - this.minValue = 0; + /** + * Minimum value (floor) of the model + * + * @type {number} + */ + this.minValue = 0; - /** - * Maximum value (ceiling) of the model - * - * @type {number} - */ - this.maxValue = 0; + /** + * Maximum value (ceiling) of the model + * + * @type {number} + */ + this.maxValue = 0; - /** - * The delta between min and max value - * - * @type {number} - */ - this.valueRange = 0; + /** + * The delta between min and max value + * + * @type {number} + */ + this.valueRange = 0; - /** - * Set to true if init method already executed - * - * @type {boolean} - */ - this.initHasRun = false; - - // 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(); - }; + /** + * Set to true if init method already executed + * + * @type {boolean} + */ + this.initHasRun = false; + + // 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 = { - // Add instance methods - Slider.prototype = { + /** + * Initialize slider + * + * @returns {undefined} + */ + init: function() { + var thrLow, thrHigh, + calcDimFn = angular.bind(this, this.calcViewDimensions), + self = this; + + this.applyOptions(); + this.initElemHandles(); + this.manageElementsStyle(); + this.addAccessibility(); + this.manageEventsBindings(); + this.setDisabledState(); + this.calcViewDimensions(); + this.setMinAndMax(); + + $timeout(function() { + self.updateCeilLab(); + self.updateFloorLab(); + self.initHandles(); + self.bindEvents(); + }); + + // 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.setMinAndMax(); + self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); + self.updateSelectionBar(); + self.updateTicksScale(); + + if (self.range) { + self.updateCmbLabel(); + } - /** - * Initialize slider - * - * @returns {undefined} - */ - init: function() { - var thrLow, thrHigh, - calcDimFn = angular.bind(this, this.calcViewDimensions), - self = this; - - this.applyOptions(); - this.initElemHandles(); - this.manageElementsStyle(); - this.addAccessibility(); - this.manageEventsBindings(); - this.setDisabledState(); - this.calcViewDimensions(); - this.setMinAndMax(); + }, self.options.interval); + + thrHigh = rzThrottle(function() { + self.setMinAndMax(); + self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); + self.updateSelectionBar(); + self.updateTicksScale(); + self.updateCmbLabel(); + }, self.options.interval); + + this.scope.$on('rzSliderForceRender', function() { + self.resetLabelsValue(); + thrLow(); + if (self.range) { + thrHigh(); + } + self.resetSlider(); + }); - $timeout(function() { - self.updateCeilLab(); - self.updateFloorLab(); - self.initHandles(); - self.bindEvents(); - }); + // Watchers + this.scope.$watch('rzSliderModel', function(newValue, oldValue) { + if (newValue === oldValue) + return; + thrLow(); + }); - // Recalculate slider view dimensions - this.scope.$on('reCalcViewDimensions', calcDimFn); + this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { + if (newValue === oldValue) + return; + if (newValue != null) + thrHigh(); + if (self.range && newValue == null || !self.range && newValue != null) { + self.applyOptions(); + self.resetSlider(); + } + }); - // Recalculate stuff if view port dimensions have changed - angular.element($window).on('resize', calcDimFn); + this.scope.$watch('rzSliderOptions', function(newValue, oldValue) { + if (newValue === oldValue) + return; + self.applyOptions(); + self.resetSlider(); + }, true); - this.initHasRun = true; + this.scope.$on('$destroy', function() { + self.unbindEvents(); + angular.element($window).off('resize', calcDimFn); + }); + }, - // Watch for changes to the model + /** + * Read the user options and apply them to the slider model + */ + applyOptions: function() { + this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions); - thrLow = rzThrottle(function() { - self.setMinAndMax(); - self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel)); - self.updateSelectionBar(); - self.updateTicksScale(); + 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.showTicks = this.options.showTicks || this.options.showTicksValues; + + if (this.options.stepsArray) { + this.options.floor = 0; + this.options.ceil = this.options.stepsArray.length - 1; + this.options.step = 1; + this.customTrFn = function(value) { + return this.options.stepsArray[value]; + }; + } else if (this.options.translate) + this.customTrFn = this.options.translate; + else + this.customTrFn = function(value) { + return String(value); + }; + }, - if (self.range) { - self.updateCmbLabel(); - } + /** + * Resets slider + * + * @returns {undefined} + */ + resetSlider: function() { + this.manageElementsStyle(); + this.setMinAndMax(); + this.updateCeilLab(); + this.updateFloorLab(); + this.unbindEvents(); + this.manageEventsBindings(); + this.setDisabledState(); + this.calcViewDimensions(); + }, - }, self.options.interval); + /** + * 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; + } - thrHigh = rzThrottle(function() { - self.setMinAndMax(); - self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh)); - self.updateSelectionBar(); - self.updateTicksScale(); - self.updateCmbLabel(); - }, self.options.interval); + }, this); - this.scope.$on('rzSliderForceRender', function() { - self.resetLabelsValue(); - thrLow(); - if (self.range) { - thrHigh(); - } - self.resetSlider(); - }); + // Initialize offset cache properties + this.selBar.rzsl = 0; + this.minH.rzsl = 0; + this.maxH.rzsl = 0; + this.flrLab.rzsl = 0; + this.ceilLab.rzsl = 0; + this.minLab.rzsl = 0; + this.maxLab.rzsl = 0; + this.cmbLab.rzsl = 0; + }, - // Watchers - this.scope.$watch('rzSliderModel', function(newValue, oldValue) { - if (newValue === oldValue) - return; - thrLow(); - }); + /** Update each elements style based on options + * + */ + manageElementsStyle: function() { + + if (!this.range) + this.maxH.css('display', 'none'); + else + this.maxH.css('display', null); + + this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels); + this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels); + this.alwaysHide(this.minLab, this.options.showTicksValues); + this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range); + this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range); + this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar); + + if (!this.options.showTicks) + this.ticks.html(''); + + if (this.options.draggableRange) + this.selBar.addClass('rz-draggable'); + else + this.selBar.removeClass('rz-draggable'); + }, + + alwaysHide: function(el, hide) { + el.rzAlwaysHide = hide; + if (hide) + this.hideEl(el); + else + this.showEl(el) + }, - this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { - if (newValue === oldValue) - return; - if (newValue != null) - thrHigh(); - if (self.range && newValue == null || !self.range && newValue != null) { - self.applyOptions(); - self.resetSlider(); - } - }); + /** + * Manage the events bindings based on readOnly and disabled options + * + * @returns {undefined} + */ + manageEventsBindings: function() { + if (this.options.disabled || this.options.readOnly) + this.unbindEvents(); + else if (!this.options.disabled || !this.options.readOnly) + this.bindEvents(); + }, - this.scope.$watch('rzSliderOptions', function(newValue, oldValue) { - if (newValue === oldValue) - return; - self.applyOptions(); - self.resetSlider(); - }, true); + /** + * 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); + } + }, - this.scope.$on('$destroy', function() { - self.unbindEvents(); - angular.element($window).off('resize', calcDimFn); - }); - }, + /** + * Reset label values + * + * @return {undefined} + */ + resetLabelsValue: function() { + this.minLab.rzsv = undefined; + this.maxLab.rzsv = undefined; + }, - /** - * Read the user options and apply them to the slider model - */ - applyOptions: function() { - this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions); - - 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.showTicks = this.options.showTicks || this.options.showTicksValues; - - if (this.options.stepsArray) { - this.options.floor = 0; - this.options.ceil = this.options.stepsArray.length - 1; - this.options.step = 1; - this.customTrFn = function(value) { - return this.options.stepsArray[value]; - }; - } - else if (this.options.translate) - this.customTrFn = this.options.translate; - else - this.customTrFn = function(value) { - return String(value); - }; - }, - - /** - * Resets slider - * - * @returns {undefined} - */ - resetSlider: function() { - this.manageElementsStyle(); - this.setMinAndMax(); - this.updateCeilLab(); - this.updateFloorLab(); - this.unbindEvents(); - this.manageEventsBindings(); - this.setDisabledState(); - this.calcViewDimensions(); - }, - - /** - * 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; - } + /** + * 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.valueToOffset(this.scope.rzSliderModel)); - }, this); - - // Initialize offset cache properties - this.selBar.rzsl = 0; - this.minH.rzsl = 0; - this.maxH.rzsl = 0; - this.flrLab.rzsl = 0; - this.ceilLab.rzsl = 0; - this.minLab.rzsl = 0; - this.maxLab.rzsl = 0; - this.cmbLab.rzsl = 0; - }, - - /** Update each elements style based on options - * - */ - manageElementsStyle: function() { - - if (!this.range) - this.maxH.css('display', 'none'); - else - this.maxH.css('display', null); - - this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels); - this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels); - this.alwaysHide(this.minLab, this.options.showTicksValues); - this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range); - this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range); - this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar); - - if (!this.options.showTicks) - this.ticks.html(''); - - if (this.options.draggableRange) - this.selBar.addClass('rz-draggable'); - else - this.selBar.removeClass('rz-draggable'); - }, - - 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} + /* + the order here is important since the selection bar should be + updated after the high handle but before the combined label */ - manageEventsBindings: function() { - if (this.options.disabled || this.options.readOnly) - this.unbindEvents(); - else if (!this.options.disabled || !this.options.readOnly) - 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); - } - }, + if (this.range) + this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)); + this.updateSelectionBar(); + if (this.range) + this.updateCmbLabel(); - /** - * 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.valueToOffset(this.scope.rzSliderModel)); - - /* - 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.valueToOffset(this.scope.rzSliderHigh)); - this.updateSelectionBar(); - if (this.range) - this.updateCmbLabel(); + this.updateTicksScale(); + }, - this.updateTicksScale(); - }, - - /** - * Translate value to human readable format - * - * @param {number|string} value - * @param {jqLite} label - * @param {boolean} [useCustomTr] - * @returns {undefined} - */ - translateFn: function(value, label, useCustomTr) { - useCustomTr = useCustomTr === undefined ? true : useCustomTr; + /** + * Translate value to human readable format + * + * @param {number|string} value + * @param {jqLite} label + * @param {boolean} [useCustomTr] + * @returns {undefined} + */ + translateFn: function(value, label, useCustomTr) { + useCustomTr = useCustomTr === undefined ? true : useCustomTr; - var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)), - getWidth = false; + var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)), + getWidth = false; - if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) { - getWidth = true; - label.rzsv = valStr; - } + if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) { + getWidth = true; + label.rzsv = valStr; + } - label.text(valStr); + label.text(valStr); - // Update width only when length of the label have changed - if (getWidth) { - this.getWidth(label); - } - }, + // Update width only when length of the label have changed + if (getWidth) { + this.getWidth(label); + } + }, - /** - * Set maximum and minimum values for the slider and ensure the model and high - * value match these limits - * @returns {undefined} - */ - setMinAndMax: function() { + /** + * 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.step = +this.options.step; + this.precision = +this.options.precision; - this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel); - if (this.range) - this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); + this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel); + if (this.range) + this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh); - this.minValue = this.roundStep(+this.options.floor); + this.minValue = this.roundStep(+this.options.floor); - if (this.options.ceil) - this.maxValue = this.roundStep(+this.options.ceil); - else - this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; + if (this.options.ceil) + this.maxValue = this.roundStep(+this.options.ceil); + else + this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; - this.valueRange = this.maxValue - this.minValue; - }, + this.valueRange = this.maxValue - this.minValue; + }, - /** - * Adds accessibility atributes - * - * Run only once during initialization - * - * @returns {undefined} - */ - addAccessibility: function() { - this.sliderElem.attr("role", "slider"); - }, - - /** - * 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.getWidth(this.minH); + /** + * Adds accessibility atributes + * + * Run only once during initialization + * + * @returns {undefined} + */ + addAccessibility: function() { + this.sliderElem.attr("role", "slider"); + }, - this.handleHalfWidth = handleWidth / 2; - this.barWidth = this.getWidth(this.fullBar); + /** + * 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.getWidth(this.minH); - this.maxLeft = this.barWidth - handleWidth; + this.handleHalfWidth = handleWidth / 2; + this.barWidth = this.getWidth(this.fullBar); - this.getWidth(this.sliderElem); - this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left; + this.maxLeft = this.barWidth - handleWidth; - if (this.initHasRun) { - this.updateFloorLab(); - this.updateCeilLab(); - this.initHandles(); - } - }, + this.getWidth(this.sliderElem); + this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left; - /** - * Update the ticks position - * - * @returns {undefined} - */ - updateTicksScale: function() { - if (!this.options.showTicks) return; - if (!this.step) return; //if step is 0, the following loop will be endless. - - var positions = '', - ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1; - for (var i = 0; i < ticksCount; i++) { - var value = this.roundStep(this.minValue + i * this.step); - var selectedClass = this.isTickSelected(value) ? 'selected' : ''; - positions += '
                                            • '; - if (this.options.showTicksValues) { - var tooltip = ''; - if (this.options.ticksValuesTooltip) { - tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"'; - } - positions += '' + this.getDisplayValue(value) + ''; + if (this.initHasRun) { + this.updateFloorLab(); + this.updateCeilLab(); + this.initHandles(); + } + }, + + /** + * Update the ticks position + * + * @returns {undefined} + */ + updateTicksScale: function() { + if (!this.options.showTicks) return; + if (!this.step) return; //if step is 0, the following loop will be endless. + + var positions = '', + ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1; + for (var i = 0; i < ticksCount; i++) { + var value = this.roundStep(this.minValue + i * this.step); + var selectedClass = this.isTickSelected(value) ? 'selected' : ''; + positions += '
                                            • '; + if (this.options.showTicksValues) { + var tooltip = ''; + if (this.options.ticksValuesTooltip) { + tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"'; } - positions += '
                                            • '; - } - this.ticks.html(positions); - if (this.options.ticksValuesTooltip) - $compile(this.ticks.contents())(this.scope); - }, - - isTickSelected: function(value) { - if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel) - return true; - if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh) - return true; - return false; - }, - - /** - * Update position of the ceiling label - * - * @returns {undefined} - */ - updateCeilLab: function() { - this.translateFn(this.maxValue, this.ceilLab); - this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw); - this.getWidth(this.ceilLab); - }, - - /** - * Update position of the floor label - * - * @returns {undefined} - */ - updateFloorLab: function() { - this.translateFn(this.minValue, this.flrLab); - this.getWidth(this.flrLab); - }, - - /** - * Call the onStart callback if defined - * - * @returns {undefined} - */ - callOnStart: function() { - if (this.options.onStart) { - var self = this; - $timeout(function() { - self.options.onStart(); - }); + positions += '' + this.getDisplayValue(value) + ''; } - }, + positions += ''; + } + this.ticks.html(positions); + if (this.options.ticksValuesTooltip) + $compile(this.ticks.contents())(this.scope); + }, + + isTickSelected: function(value) { + if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel) + return true; + if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh) + return true; + return false; + }, - /** - * Call the onChange callback if defined - * - * @returns {undefined} - */ - callOnChange: function() { - if (this.options.onChange) { - var self = this; - $timeout(function() { - self.options.onChange(); - }); - } - }, + /** + * Update position of the ceiling label + * + * @returns {undefined} + */ + updateCeilLab: function() { + this.translateFn(this.maxValue, this.ceilLab); + this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw); + this.getWidth(this.ceilLab); + }, - /** - * Call the onEnd callback if defined - * - * @returns {undefined} - */ - callOnEnd: function() { - if (this.options.onEnd) { - var self = this; - $timeout(function() { - self.options.onEnd(); - }); - } - }, + /** + * Update position of the floor label + * + * @returns {undefined} + */ + updateFloorLab: function() { + this.translateFn(this.minValue, this.flrLab); + this.getWidth(this.flrLab); + }, - /** - * Update slider handles and label positions - * - * @param {string} which - * @param {number} newOffset - */ - updateHandles: function(which, newOffset) { - if (which === 'rzSliderModel') { - this.updateLowHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); - - if (this.range) { - this.updateCmbLabel(); - } - return; - } + /** + * Call the onStart callback if defined + * + * @returns {undefined} + */ + callOnStart: function() { + if (this.options.onStart) { + var self = this; + $timeout(function() { + self.options.onStart(); + }); + } + }, - if (which === 'rzSliderHigh') { - this.updateHighHandle(newOffset); - this.updateSelectionBar(); - this.updateTicksScale(); + /** + * Call the onChange callback if defined + * + * @returns {undefined} + */ + callOnChange: function() { + if (this.options.onChange) { + var self = this; + $timeout(function() { + self.options.onChange(); + }); + } + }, - if (this.range) { - this.updateCmbLabel(); - } - return; - } + /** + * Call the onEnd callback if defined + * + * @returns {undefined} + */ + callOnEnd: function() { + if (this.options.onEnd) { + var self = this; + $timeout(function() { + self.options.onEnd(); + }); + } + }, - // Update both + /** + * Update slider handles and label positions + * + * @param {string} which + * @param {number} newOffset + */ + updateHandles: function(which, newOffset) { + if (which === 'rzSliderModel') { this.updateLowHandle(newOffset); - this.updateHighHandle(newOffset); this.updateSelectionBar(); this.updateTicksScale(); - this.updateCmbLabel(); - }, - /** - * Update low slider handle position and label - * - * @param {number} newOffset - * @returns {undefined} - */ - updateLowHandle: function(newOffset) { - this.setLeft(this.minH, newOffset); - this.translateFn(this.scope.rzSliderModel, this.minLab); - this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth); - - this.shFloorCeil(); - }, - - /** - * Update high slider handle position and label - * - * @param {number} newOffset - * @returns {undefined} - */ - updateHighHandle: function(newOffset) { - this.setLeft(this.maxH, newOffset); - this.translateFn(this.scope.rzSliderHigh, this.maxLab); - this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth); - - this.shFloorCeil(); - }, - - /** - * Show / hide floor / ceiling label - * - * @returns {undefined} - */ - shFloorCeil: function() { - var flHidden = false, clHidden = false; - - if (this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5) { - flHidden = true; - this.hideEl(this.flrLab); + if (this.range) { + this.updateCmbLabel(); } - else { - flHidden = false; - this.showEl(this.flrLab); + return; + } + + if (which === 'rzSliderHigh') { + this.updateHighHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); + + if (this.range) { + this.updateCmbLabel(); } + return; + } + + // Update both + this.updateLowHandle(newOffset); + this.updateHighHandle(newOffset); + this.updateSelectionBar(); + this.updateTicksScale(); + this.updateCmbLabel(); + }, + + /** + * Update low slider handle position and label + * + * @param {number} newOffset + * @returns {undefined} + */ + updateLowHandle: function(newOffset) { + this.setLeft(this.minH, newOffset); + this.translateFn(this.scope.rzSliderModel, this.minLab); + this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth); + + this.shFloorCeil(); + }, + + /** + * Update high slider handle position and label + * + * @param {number} newOffset + * @returns {undefined} + */ + updateHighHandle: function(newOffset) { + this.setLeft(this.maxH, newOffset); + this.translateFn(this.scope.rzSliderHigh, this.maxLab); + this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth); + + this.shFloorCeil(); + }, + + /** + * Show / hide floor / ceiling label + * + * @returns {undefined} + */ + shFloorCeil: function() { + var flHidden = false, + clHidden = false; + + if (this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5) { + flHidden = true; + this.hideEl(this.flrLab); + } else { + flHidden = false; + this.showEl(this.flrLab); + } - if (this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) { - clHidden = true; + if (this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) { + clHidden = true; + this.hideEl(this.ceilLab); + } else { + clHidden = false; + this.showEl(this.ceilLab); + } + + if (this.range) { + if (this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) { this.hideEl(this.ceilLab); - } - else { - clHidden = false; + } else if (!clHidden) { this.showEl(this.ceilLab); } - if (this.range) { - if (this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) { - this.hideEl(this.ceilLab); - } - else if (!clHidden) { - this.showEl(this.ceilLab); - } - - // Hide or show floor label - if (this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth) { - this.hideEl(this.flrLab); - } - else if (!flHidden) { - this.showEl(this.flrLab); - } + // Hide or show floor label + if (this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth) { + this.hideEl(this.flrLab); + } else if (!flHidden) { + this.showEl(this.flrLab); } - }, + } + }, - /** - * Update slider selection bar, combined label and range label - * - * @returns {undefined} - */ - updateSelectionBar: function() { - this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth); - this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0); - }, - - /** - * Update combined label position and value - * - * @returns {undefined} - */ - updateCmbLabel: function() { - var lowTr, highTr; - - if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) { - lowTr = this.getDisplayValue(this.scope.rzSliderModel); - highTr = this.getDisplayValue(this.scope.rzSliderHigh); - - this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false); - this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2); - this.hideEl(this.minLab); - this.hideEl(this.maxLab); - this.showEl(this.cmbLab); - } - else { - this.showEl(this.maxLab); - this.showEl(this.minLab); - this.hideEl(this.cmbLab); - } - }, + /** + * Update slider selection bar, combined label and range label + * + * @returns {undefined} + */ + updateSelectionBar: function() { + this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth); + this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0); + }, - /** - * Return the translated value if a translate function is provided else the original value - * @param value - * @returns {*} - */ - getDisplayValue: function(value) { - return this.customTrFn(value, this.options.id); - }, - - /** - * Round value to step and precision - * - * @param {number} value - * @returns {number} - */ - roundStep: function(value) { - var step = this.step, - remainder = +((value - this.minValue) % step).toFixed(3), - steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder; - - steppedValue = steppedValue.toFixed(this.precision); - return +steppedValue; - }, - - /** - * Hide element - * - * @param element - * @returns {jqLite} The jqLite wrapped DOM element - */ - hideEl: function(element) { - return element.css({opacity: 0}); - }, - - /** - * Show element - * - * @param element The jqLite wrapped DOM element - * @returns {jqLite} The jqLite - */ - showEl: function(element) { - if (!!element.rzAlwaysHide) { - return element; - } + /** + * Update combined label position and value + * + * @returns {undefined} + */ + updateCmbLabel: function() { + var lowTr, highTr; + + if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) { + lowTr = this.getDisplayValue(this.scope.rzSliderModel); + highTr = this.getDisplayValue(this.scope.rzSliderHigh); + + this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false); + this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2); + this.hideEl(this.minLab); + this.hideEl(this.maxLab); + this.showEl(this.cmbLab); + } else { + this.showEl(this.maxLab); + this.showEl(this.minLab); + this.hideEl(this.cmbLab); + } + }, - return element.css({opacity: 1}); - }, + /** + * Return the translated value if a translate function is provided else the original value + * @param value + * @returns {*} + */ + getDisplayValue: function(value) { + return this.customTrFn(value, this.options.id); + }, - /** - * Set element left offset - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @param {number} left - * @returns {number} - */ - setLeft: function(elem, left) { - elem.rzsl = left; - elem.css({left: left + 'px'}); - return left; - }, - - /** - * Get element width - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @returns {number} - */ - getWidth: function(elem) { - var val = elem[0].getBoundingClientRect(); - elem.rzsw = (val.right - val.left) * this.options.scale; - return elem.rzsw; - }, - - /** - * Set element width - * - * @param {jqLite} elem The jqLite wrapped DOM element - * @param {number} width - * @returns {number} - */ - setWidth: function(elem, width) { - elem.rzsw = width; - elem.css({width: width + 'px'}); - return width; - }, - - /** - * Translate value to pixel offset - * - * @param {number} val - * @returns {number} - */ - valueToOffset: function(val) { - return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0; - }, - - /** - * Ensure that the position rendered is within the slider bounds, even if the value is not - * - * @param {number} val - * @returns {number} - */ - sanitizeOffsetValue: function(val) { - return Math.min(Math.max(val, this.minValue), this.maxValue); - }, - - /** - * Translate offset to model value - * - * @param {number} offset - * @returns {number} - */ - offsetToValue: function(offset) { - return (offset / this.maxLeft) * this.valueRange + this.minValue; - }, + /** + * Round value to step and precision + * + * @param {number} value + * @returns {number} + */ + roundStep: function(value) { + var step = this.step, + remainder = +((value - this.minValue) % step).toFixed(3), + steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder; - // Events + steppedValue = steppedValue.toFixed(this.precision); + return +steppedValue; + }, - /** - * Get the X-coordinate of an event - * - * @param {Object} event The event - * @returns {number} - */ - getEventX: function(event) { - /* http://stackoverflow.com/a/12336075/282882 */ - //noinspection JSLint - if ('clientX' in event) { - return event.clientX; - } + /** + * Hide element + * + * @param element + * @returns {jqLite} The jqLite wrapped DOM element + */ + hideEl: function(element) { + return element.css({ + opacity: 0 + }); + }, - return event.originalEvent === undefined ? - event.touches[0].clientX - : event.originalEvent.touches[0].clientX; - }, + /** + * Show element + * + * @param element The jqLite wrapped DOM element + * @returns {jqLite} The jqLite + */ + showEl: function(element) { + if (!!element.rzAlwaysHide) { + return element; + } - /** - * 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 offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale; - return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; - }, - - /** - * Bind mouse and touch events to slider handles - * - * @returns {undefined} - */ - bindEvents: function() { - if (this.options.readOnly || this.options.disabled) return; - var barTracking, barStart, barMove; - - if (this.options.draggableRange) { - barTracking = 'rzSliderDrag'; - barStart = this.onDragStart; - barMove = this.onDragMove; - } - else { - barTracking = 'rzSliderModel'; - barStart = this.onStart; - barMove = this.onMove; - } + return element.css({ + opacity: 1 + }); + }, - this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); - if (this.range) { - this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); - } - this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); - this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); - this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); - this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); - this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); - this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks)); - - this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); - if (this.range) { - this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); - } - this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); - this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); - this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); - this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); - this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); - this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); - }, - - /** - * 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); + /** + * Set element left offset + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @param {number} left + * @returns {number} + */ + setLeft: function(elem, left) { + elem.rzsl = left; + elem.css({ + left: left + 'px' + }); + return left; + }, - event.stopPropagation(); - event.preventDefault(); + /** + * Get element width + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @returns {number} + */ + getWidth: function(elem) { + var val = elem[0].getBoundingClientRect(); + elem.rzsw = (val.right - val.left) * this.options.scale; + return elem.rzsw; + }, - if (this.tracking !== '') { - return; - } + /** + * Set element width + * + * @param {jqLite} elem The jqLite wrapped DOM element + * @param {number} width + * @returns {number} + */ + setWidth: function(elem, width) { + elem.rzsw = width; + elem.css({ + width: width + 'px' + }); + return width; + }, - // We have to do this in case the HTML where the sliders are on - // have been animated into view. - this.calcViewDimensions(); + /** + * Translate value to pixel offset + * + * @param {number} val + * @returns {number} + */ + valueToOffset: function(val) { + return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0; + }, - if (pointer) { - this.tracking = ref; - } - else { - pointer = this.getNearestHandle(event); - this.tracking = pointer === this.minH ? 'rzSliderModel' : 'rzSliderHigh'; - } + /** + * Ensure that the position rendered is within the slider bounds, even if the value is not + * + * @param {number} val + * @returns {number} + */ + sanitizeOffsetValue: function(val) { + return Math.min(Math.max(val, this.minValue), this.maxValue); + }, - pointer.addClass('rz-active'); + /** + * Translate offset to model value + * + * @param {number} offset + * @returns {number} + */ + offsetToValue: function(offset) { + return (offset / this.maxLeft) * this.valueRange + this.minValue; + }, - ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer); - ehEnd = angular.bind(this, this.onEnd, ehMove); + // Events - $document.on(eventNames.moveEvent, ehMove); - $document.one(eventNames.endEvent, ehEnd); - this.callOnStart(); - }, + /** + * Get the X-coordinate of an event + * + * @param {Object} event The event + * @returns {number} + */ + getEventX: function(event) { + /* http://stackoverflow.com/a/12336075/282882 */ + //noinspection JSLint + if ('clientX' in event) { + return event.clientX; + } - /** - * onMove event handler - * - * @param {jqLite} pointer - * @param {Event} event The event - * @returns {undefined} - */ - onMove: function(pointer, event) { - var eventX = this.getEventX(event), - sliderLO, newOffset, newValue; - - sliderLO = this.sliderElem.rzsl; - newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale; - - if (newOffset <= 0) { - if (pointer.rzsl === 0) - return; - newValue = this.minValue; - newOffset = 0; - } - else if (newOffset >= this.maxLeft) { - if (pointer.rzsl === this.maxLeft) - return; - newValue = this.maxValue; - newOffset = this.maxLeft; - } - else { - newValue = this.offsetToValue(newOffset); - newValue = this.roundStep(newValue); - newOffset = this.valueToOffset(newValue); - } - this.positionTrackingHandle(newValue, newOffset); - }, - - /** - * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; - this.dragging = { - active: true, - value: this.offsetToValue(offset), - difference: this.scope.rzSliderHigh - this.scope.rzSliderModel, - offset: offset, - lowDist: offset - this.minH.rzsl, - highDist: this.maxH.rzsl - offset - }; - this.minH.addClass('rz-active'); - this.maxH.addClass('rz-active'); - - this.onStart(pointer, ref, event); - }, - - /** - * onDragMove event handler - * - * Handles dragging of the middle bar. - * - * @param {jqLite} pointer - * @param {Event} event The event - * @returns {undefined} - */ - onDragMove: function(pointer, event) { - var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth, - newMinOffset, newMaxOffset, - newMinValue, newMaxValue; - - if (newOffset <= this.dragging.lowDist) { - if (pointer.rzsl === this.dragging.lowDist) { - return; - } - newMinValue = this.minValue; - newMinOffset = 0; - newMaxValue = this.minValue + this.dragging.difference; - newMaxOffset = this.valueToOffset(newMaxValue); - } - else if (newOffset >= this.maxLeft - this.dragging.highDist) { - if (pointer.rzsl === this.dragging.highDist) { - return; - } - newMaxValue = this.maxValue; - newMaxOffset = this.maxLeft; - newMinValue = this.maxValue - this.dragging.difference; - newMinOffset = this.valueToOffset(newMinValue); - } - else { - newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist); - newMinValue = this.roundStep(newMinValue); - newMinOffset = this.valueToOffset(newMinValue); - newMaxValue = newMinValue + this.dragging.difference; - newMaxOffset = this.valueToOffset(newMaxValue); - } + return event.originalEvent === undefined ? + event.touches[0].clientX : event.originalEvent.touches[0].clientX; + }, - this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset); - }, + /** + * 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 offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale; + return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH; + }, - /** - * Set the new value and offset for the entire bar - * - * @param {number} newMinValue the new minimum value - * @param {number} newMaxValue the new maximum value - * @param {number} newMinOffset the new minimum offset - * @param {number} newMaxOffset the new maximum offset - */ - positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) { - this.scope.rzSliderModel = newMinValue; - this.scope.rzSliderHigh = newMaxValue; - this.updateHandles('rzSliderModel', newMinOffset); - this.updateHandles('rzSliderHigh', newMaxOffset); - this.scope.$apply(); - this.callOnChange(); - }, + /** + * Bind mouse and touch events to slider handles + * + * @returns {undefined} + */ + bindEvents: function() { + if (this.options.readOnly || this.options.disabled) return; + var barTracking, barStart, barMove; + + if (this.options.draggableRange) { + barTracking = 'rzSliderDrag'; + barStart = this.onDragStart; + barMove = this.onDragMove; + } else { + barTracking = 'rzSliderModel'; + barStart = this.onStart; + barMove = this.onMove; + } - /** - * Set the new value and offset to the current tracking handle - * - * @param {number} newValue new model value - * @param {number} newOffset new offset value - */ - positionTrackingHandle: function(newValue, newOffset) { - if (this.range) { - /* This is to check if we need to switch the min and max handles*/ - if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh) { - this.scope[this.tracking] = this.scope.rzSliderHigh; - this.updateHandles(this.tracking, this.maxH.rzsl); - this.tracking = 'rzSliderHigh'; - this.minH.removeClass('rz-active'); - this.maxH.addClass('rz-active'); - /* We need to apply here because we are not sure that we will enter the next block */ - this.scope.$apply(); - this.callOnChange(); - } - else if (this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) { - this.scope[this.tracking] = this.scope.rzSliderModel; - this.updateHandles(this.tracking, this.minH.rzsl); - this.tracking = 'rzSliderModel'; - this.maxH.removeClass('rz-active'); - this.minH.addClass('rz-active'); - /* We need to apply here because we are not sure that we will enter the next block */ - this.scope.$apply(); - this.callOnChange(); - } + this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); + if (this.range) { + this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); + } + this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); + this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); + this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); + this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); + this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); + this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks)); + + this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel')); + if (this.range) { + this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')); + } + this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); + this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); + this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); + this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); + this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); + this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks)); + }, + + /** + * 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(); + + if (this.tracking !== '') { + return; + } + + // 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 ? 'rzSliderModel' : 'rzSliderHigh'; + } + + pointer.addClass('rz-active'); + + 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.one(eventNames.endEvent, ehEnd); + this.callOnStart(); + }, + + /** + * onMove event handler + * + * @param {jqLite} pointer + * @param {Event} event The event + * @returns {undefined} + */ + onMove: function(pointer, event) { + var eventX = this.getEventX(event), + sliderLO, newOffset, newValue; + + sliderLO = this.sliderElem.rzsl; + newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale; + + if (newOffset <= 0) { + if (pointer.rzsl === 0) + return; + newValue = this.minValue; + newOffset = 0; + } else if (newOffset >= this.maxLeft) { + if (pointer.rzsl === this.maxLeft) + return; + newValue = this.maxValue; + newOffset = this.maxLeft; + } else { + newValue = this.offsetToValue(newOffset); + newValue = this.roundStep(newValue); + newOffset = this.valueToOffset(newValue); + } + this.positionTrackingHandle(newValue, newOffset); + }, + + /** + * 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 offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth; + this.dragging = { + active: true, + value: this.offsetToValue(offset), + difference: this.scope.rzSliderHigh - this.scope.rzSliderModel, + offset: offset, + lowDist: offset - this.minH.rzsl, + highDist: this.maxH.rzsl - offset + }; + this.minH.addClass('rz-active'); + this.maxH.addClass('rz-active'); + + this.onStart(pointer, ref, event); + }, + + /** + * onDragMove event handler + * + * Handles dragging of the middle bar. + * + * @param {jqLite} pointer + * @param {Event} event The event + * @returns {undefined} + */ + onDragMove: function(pointer, event) { + var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth, + newMinOffset, newMaxOffset, + newMinValue, newMaxValue; + + if (newOffset <= this.dragging.lowDist) { + if (pointer.rzsl === this.dragging.lowDist) { + return; + } + newMinValue = this.minValue; + newMinOffset = 0; + newMaxValue = this.minValue + this.dragging.difference; + newMaxOffset = this.valueToOffset(newMaxValue); + } else if (newOffset >= this.maxLeft - this.dragging.highDist) { + if (pointer.rzsl === this.dragging.highDist) { + return; } + newMaxValue = this.maxValue; + newMaxOffset = this.maxLeft; + newMinValue = this.maxValue - this.dragging.difference; + newMinOffset = this.valueToOffset(newMinValue); + } else { + newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist); + newMinValue = this.roundStep(newMinValue); + newMinOffset = this.valueToOffset(newMinValue); + newMaxValue = newMinValue + this.dragging.difference; + newMaxOffset = this.valueToOffset(newMaxValue); + } + + this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset); + }, + + /** + * Set the new value and offset for the entire bar + * + * @param {number} newMinValue the new minimum value + * @param {number} newMaxValue the new maximum value + * @param {number} newMinOffset the new minimum offset + * @param {number} newMaxOffset the new maximum offset + */ + positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) { + this.scope.rzSliderModel = newMinValue; + this.scope.rzSliderHigh = newMaxValue; + this.updateHandles('rzSliderModel', newMinOffset); + this.updateHandles('rzSliderHigh', newMaxOffset); + this.scope.$apply(); + this.callOnChange(); + }, - if (this.scope[this.tracking] !== newValue) { - this.scope[this.tracking] = newValue; - this.updateHandles(this.tracking, newOffset); + /** + * Set the new value and offset to the current tracking handle + * + * @param {number} newValue new model value + * @param {number} newOffset new offset value + */ + positionTrackingHandle: function(newValue, newOffset) { + if (this.range) { + /* This is to check if we need to switch the min and max handles*/ + if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh) { + this.scope[this.tracking] = this.scope.rzSliderHigh; + this.updateHandles(this.tracking, this.maxH.rzsl); + this.tracking = 'rzSliderHigh'; + this.minH.removeClass('rz-active'); + this.maxH.addClass('rz-active'); + /* We need to apply here because we are not sure that we will enter the next block */ + this.scope.$apply(); + this.callOnChange(); + } else if (this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) { + this.scope[this.tracking] = this.scope.rzSliderModel; + this.updateHandles(this.tracking, this.minH.rzsl); + this.tracking = 'rzSliderModel'; + this.maxH.removeClass('rz-active'); + this.minH.addClass('rz-active'); + /* We need to apply here because we are not sure that we will enter the next block */ this.scope.$apply(); this.callOnChange(); } - }, - - /** - * onEnd event handler - * - * @param {Event} event The event - * @param {Function} ehMove The the bound move event handler - * @returns {undefined} - */ - onEnd: function(ehMove, event) { - var moveEventName = this.getEventNames(event).moveEvent; + } - this.minH.removeClass('rz-active'); - this.maxH.removeClass('rz-active'); + if (this.scope[this.tracking] !== newValue) { + this.scope[this.tracking] = newValue; + this.updateHandles(this.tracking, newOffset); + this.scope.$apply(); + this.callOnChange(); + } + }, - $document.off(moveEventName, ehMove); + /** + * onEnd event handler + * + * @param {Event} event The event + * @param {Function} ehMove The the bound move event handler + * @returns {undefined} + */ + onEnd: function(ehMove, event) { + var moveEventName = this.getEventNames(event).moveEvent; - this.scope.$emit('slideEnded'); - this.tracking = ''; + this.minH.removeClass('rz-active'); + this.maxH.removeClass('rz-active'); - this.dragging.active = false; - this.callOnEnd(); - }, + $document.off(moveEventName, ehMove); - /** - * 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: ''}; + this.scope.$emit('slideEnded'); + this.tracking = ''; - if (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) { - eventNames.moveEvent = 'touchmove'; - eventNames.endEvent = 'touchend'; - } - else { - eventNames.moveEvent = 'mousemove'; - eventNames.endEvent = 'mouseup'; - } + this.dragging.active = false; + this.callOnEnd(); + }, - return eventNames; + /** + * 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 (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) { + eventNames.moveEvent = 'touchmove'; + eventNames.endEvent = 'touchend'; + } else { + eventNames.moveEvent = 'mousemove'; + eventNames.endEvent = 'mouseup'; } - }; - return Slider; - }) + return eventNames; + } + }; - .directive('rzslider', function(RzSlider) { - 'use strict'; + return Slider; + }) - return { - restrict: 'E', - 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'; - }, + .directive('rzslider', function(RzSlider) { + 'use strict'; - link: function(scope, elem) { - return new RzSlider(scope, elem); - } - }; - }); + return { + restrict: 'E', + scope: { + rzSliderModel: '=?', + rzSliderHigh: '=?', + rzSliderOptions: '=?', + rzSliderTplUrl: '@' + }, -// IDE assist + /** + * 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) { + return new RzSlider(scope, elem); + } + }; + }); + + // IDE assist /** * @name ngScope From 51cd1bfb6c0706e121f44b6b717d277ef39c7de6 Mon Sep 17 00:00:00 2001 From: Valentin Hervieu Date: Thu, 12 Nov 2015 07:25:35 +0100 Subject: [PATCH 15/15] Fix when floor is negative and ceil is 0 As reported in #174 --- dist/rzslider.js | 2 +- dist/rzslider.min.css | 2 +- dist/rzslider.min.js | 4 ++-- src/rzslider.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/rzslider.js b/dist/rzslider.js index 655f223..2845dc6 100644 --- a/dist/rzslider.js +++ b/dist/rzslider.js @@ -585,7 +585,7 @@ this.minValue = this.roundStep(+this.options.floor); - if (this.options.ceil) + if (this.options.ceil != null) this.maxValue = this.roundStep(+this.options.ceil); else this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel; diff --git a/dist/rzslider.min.css b/dist/rzslider.min.css index 34da7a9..703424c 100644 --- a/dist/rzslider.min.css +++ b/dist/rzslider.min.css @@ -1,2 +1,2 @@ -/*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ +/*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-12 */ rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:35px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}rzslider[disabled]{cursor:not-allowed}rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}rzslider span{position:absolute;display:inline-block;white-space:nowrap}rzslider .rz-base{width:100%;height:100%;padding:0}rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}rzslider .rz-bar-wrapper.rz-draggable{cursor:move}rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}rzslider .rz-pointer:hover:after{background-color:#fff}rzslider .rz-pointer.rz-active:after{background-color:#451aff}rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}rzslider .rz-bubble.rz-selection{top:16px}rzslider .rz-bubble.rz-limit{color:#55637d}rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:flex;width:100%;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;justify-content:space-between}rzslider .rz-ticks .tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}rzslider .rz-ticks .tick.selected{background:#0db9f0}rzslider .rz-ticks .tick .tick-value{position:absolute;top:-30px;transform:translate(-50%,0)} \ No newline at end of file diff --git a/dist/rzslider.min.js b/dist/rzslider.min.js index 3ff878e..0366c7c 100644 --- a/dist/rzslider.min.js +++ b/dist/rzslider.min.js @@ -1,2 +1,2 @@ -/*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-11 */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,ticksValuesTooltip:null,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","$compile","RzSliderOptions","rzThrottle",function(b,c,d,e,f,g){var h=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return h.prototype={init:function(){var c,e,f=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",f),a.element(d).on("resize",f),this.initHasRun=!0,c=g(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.options.interval),e=g(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.options.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",f)})},applyOptions:function(){this.options=f.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),f=this.isTickSelected(d)?"selected":"";if(a+='
                                            • ',this.options.showTicksValues){var g="";this.options.ticksValuesTooltip&&(g='uib-tooltip="'+this.options.ticksValuesTooltip(d)+'"'),a+="'+this.getDisplayValue(d)+""}a+="
                                            • "}this.ticks.html(a),this.options.ticksValuesTooltip&&e(this.ticks.contents())(this.scope)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.minValue+this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},h}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                                ')}]),b}); \ No newline at end of file +/*! angularjs-slider - v2.0.0 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu , https://github.com/rzajac/angularjs-slider.git - 2015-11-12 */ +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("angular")):b(a.angular)}(this,function(a){"use strict";var b=a.module("rzModule",[]).factory("RzSliderOptions",function(){var b={floor:0,ceil:null,step:1,precision:0,id:null,translate:null,stepsArray:null,draggableRange:!1,showSelectionBar:!1,hideLimitLabels:!1,readOnly:!1,disabled:!1,interval:350,showTicks:!1,showTicksValues:!1,ticksValuesTooltip:null,scale:1,onStart:null,onChange:null,onEnd:null},c={},d={};return d.options=function(b){a.extend(c,b)},d.getOptions=function(d){return a.extend({},b,c,d)},d}).value("rzThrottle",function(a,b,c){var d,e,f,g=Date.now||function(){return(new Date).getTime()},h=null,i=0;c=c||{};var j=function(){i=c.leading===!1?0:g(),h=null,f=a.apply(d,e),d=e=null};return function(){var k=g();i||c.leading!==!1||(i=k);var l=b-(k-i);return d=this,e=arguments,0>=l?(clearTimeout(h),h=null,i=k,f=a.apply(d,e),d=e=null):h||c.trailing===!1||(h=setTimeout(j,l)),f}}).factory("RzSlider",["$timeout","$document","$window","$compile","RzSliderOptions","rzThrottle",function(b,c,d,e,f,g){var h=function(a,b){this.scope=a,this.sliderElem=b,this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.dragging={active:!1,value:0,difference:0,offset:0,lowDist:0,highDist:0},this.handleHalfWidth=0,this.maxLeft=0,this.precision=0,this.step=0,this.tracking="",this.minValue=0,this.maxValue=0,this.valueRange=0,this.initHasRun=!1,this.fullBar=null,this.selBar=null,this.minH=null,this.maxH=null,this.flrLab=null,this.ceilLab=null,this.minLab=null,this.maxLab=null,this.cmbLab=null,this.ticks=null,this.init()};return h.prototype={init:function(){var c,e,f=a.bind(this,this.calcViewDimensions),h=this;this.applyOptions(),this.initElemHandles(),this.manageElementsStyle(),this.addAccessibility(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions(),this.setMinAndMax(),b(function(){h.updateCeilLab(),h.updateFloorLab(),h.initHandles(),h.bindEvents()}),this.scope.$on("reCalcViewDimensions",f),a.element(d).on("resize",f),this.initHasRun=!0,c=g(function(){h.setMinAndMax(),h.updateLowHandle(h.valueToOffset(h.scope.rzSliderModel)),h.updateSelectionBar(),h.updateTicksScale(),h.range&&h.updateCmbLabel()},h.options.interval),e=g(function(){h.setMinAndMax(),h.updateHighHandle(h.valueToOffset(h.scope.rzSliderHigh)),h.updateSelectionBar(),h.updateTicksScale(),h.updateCmbLabel()},h.options.interval),this.scope.$on("rzSliderForceRender",function(){h.resetLabelsValue(),c(),h.range&&e(),h.resetSlider()}),this.scope.$watch("rzSliderModel",function(a,b){a!==b&&c()}),this.scope.$watch("rzSliderHigh",function(a,b){a!==b&&(null!=a&&e(),(h.range&&null==a||!h.range&&null!=a)&&(h.applyOptions(),h.resetSlider()))}),this.scope.$watch("rzSliderOptions",function(a,b){a!==b&&(h.applyOptions(),h.resetSlider())},!0),this.scope.$on("$destroy",function(){h.unbindEvents(),a.element(d).off("resize",f)})},applyOptions:function(){this.options=f.getOptions(this.scope.rzSliderOptions),this.options.step<=0&&(this.options.step=1),this.range=void 0!==this.scope.rzSliderModel&&void 0!==this.scope.rzSliderHigh,this.options.draggableRange=this.range&&this.options.draggableRange,this.options.showTicks=this.options.showTicks||this.options.showTicksValues,this.options.stepsArray?(this.options.floor=0,this.options.ceil=this.options.stepsArray.length-1,this.options.step=1,this.customTrFn=function(a){return this.options.stepsArray[a]}):this.options.translate?this.customTrFn=this.options.translate:this.customTrFn=function(a){return String(a)}},resetSlider:function(){this.manageElementsStyle(),this.setMinAndMax(),this.updateCeilLab(),this.updateFloorLab(),this.unbindEvents(),this.manageEventsBindings(),this.setDisabledState(),this.calcViewDimensions()},initElemHandles:function(){a.forEach(this.sliderElem.children(),function(b,c){var d=a.element(b);switch(c){case 0:this.fullBar=d;break;case 1:this.selBar=d;break;case 2:this.minH=d;break;case 3:this.maxH=d;break;case 4:this.flrLab=d;break;case 5:this.ceilLab=d;break;case 6:this.minLab=d;break;case 7:this.maxLab=d;break;case 8:this.cmbLab=d;break;case 9:this.ticks=d}},this),this.selBar.rzsl=0,this.minH.rzsl=0,this.maxH.rzsl=0,this.flrLab.rzsl=0,this.ceilLab.rzsl=0,this.minLab.rzsl=0,this.maxLab.rzsl=0,this.cmbLab.rzsl=0},manageElementsStyle:function(){this.range?this.maxH.css("display",null):this.maxH.css("display","none"),this.alwaysHide(this.flrLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.ceilLab,this.options.showTicksValues||this.options.hideLimitLabels),this.alwaysHide(this.minLab,this.options.showTicksValues),this.alwaysHide(this.maxLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.cmbLab,this.options.showTicksValues||!this.range),this.alwaysHide(this.selBar,!this.range&&!this.options.showSelectionBar),this.options.showTicks||this.ticks.html(""),this.options.draggableRange?this.selBar.addClass("rz-draggable"):this.selBar.removeClass("rz-draggable")},alwaysHide:function(a,b){a.rzAlwaysHide=b,b?this.hideEl(a):this.showEl(a)},manageEventsBindings:function(){this.options.disabled||this.options.readOnly?this.unbindEvents():this.options.disabled&&this.options.readOnly||this.bindEvents()},setDisabledState:function(){this.options.disabled?this.sliderElem.attr("disabled","disabled"):this.sliderElem.attr("disabled",null)},resetLabelsValue:function(){this.minLab.rzsv=void 0,this.maxLab.rzsv=void 0},initHandles:function(){this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel)),this.range&&this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh)),this.updateSelectionBar(),this.range&&this.updateCmbLabel(),this.updateTicksScale()},translateFn:function(a,b,c){c=void 0===c?!0:c;var d=String(c?this.customTrFn(a,this.options.id):a),e=!1;(void 0===b.rzsv||b.rzsv.length!==d.length||b.rzsv.length>0&&0===b.rzsw)&&(e=!0,b.rzsv=d),b.text(d),e&&this.getWidth(b)},setMinAndMax:function(){this.step=+this.options.step,this.precision=+this.options.precision,this.scope.rzSliderModel=this.roundStep(this.scope.rzSliderModel),this.range&&(this.scope.rzSliderHigh=this.roundStep(this.scope.rzSliderHigh)),this.minValue=this.roundStep(+this.options.floor),null!=this.options.ceil?this.maxValue=this.roundStep(+this.options.ceil):this.maxValue=this.options.ceil=this.range?this.scope.rzSliderHigh:this.scope.rzSliderModel,this.valueRange=this.maxValue-this.minValue},addAccessibility:function(){this.sliderElem.attr("role","slider")},calcViewDimensions:function(){var a=this.getWidth(this.minH);this.handleHalfWidth=a/2,this.barWidth=this.getWidth(this.fullBar),this.maxLeft=this.barWidth-a,this.getWidth(this.sliderElem),this.sliderElem.rzsl=this.sliderElem[0].getBoundingClientRect().left,this.initHasRun&&(this.updateFloorLab(),this.updateCeilLab(),this.initHandles())},updateTicksScale:function(){if(this.options.showTicks&&this.step){for(var a="",b=Math.round((this.maxValue-this.minValue)/this.step)+1,c=0;b>c;c++){var d=this.roundStep(this.minValue+c*this.step),f=this.isTickSelected(d)?"selected":"";if(a+='
                                              • ',this.options.showTicksValues){var g="";this.options.ticksValuesTooltip&&(g='uib-tooltip="'+this.options.ticksValuesTooltip(d)+'"'),a+="'+this.getDisplayValue(d)+""}a+="
                                              • "}this.ticks.html(a),this.options.ticksValuesTooltip&&e(this.ticks.contents())(this.scope)}},isTickSelected:function(a){return!this.range&&this.options.showSelectionBar&&a<=this.scope.rzSliderModel?!0:this.range&&a>=this.scope.rzSliderModel&&a<=this.scope.rzSliderHigh?!0:!1},updateCeilLab:function(){this.translateFn(this.maxValue,this.ceilLab),this.setLeft(this.ceilLab,this.barWidth-this.ceilLab.rzsw),this.getWidth(this.ceilLab)},updateFloorLab:function(){this.translateFn(this.minValue,this.flrLab),this.getWidth(this.flrLab)},callOnStart:function(){if(this.options.onStart){var a=this;b(function(){a.options.onStart()})}},callOnChange:function(){if(this.options.onChange){var a=this;b(function(){a.options.onChange()})}},callOnEnd:function(){if(this.options.onEnd){var a=this;b(function(){a.options.onEnd()})}},updateHandles:function(a,b){return"rzSliderModel"===a?(this.updateLowHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):"rzSliderHigh"===a?(this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void(this.range&&this.updateCmbLabel())):(this.updateLowHandle(b),this.updateHighHandle(b),this.updateSelectionBar(),this.updateTicksScale(),void this.updateCmbLabel())},updateLowHandle:function(a){this.setLeft(this.minH,a),this.translateFn(this.scope.rzSliderModel,this.minLab),this.setLeft(this.minLab,a-this.minLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},updateHighHandle:function(a){this.setLeft(this.maxH,a),this.translateFn(this.scope.rzSliderHigh,this.maxLab),this.setLeft(this.maxLab,a-this.maxLab.rzsw/2+this.handleHalfWidth),this.shFloorCeil()},shFloorCeil:function(){var a=!1,b=!1;this.minLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+5?(a=!0,this.hideEl(this.flrLab)):(a=!1,this.showEl(this.flrLab)),this.minLab.rzsl+this.minLab.rzsw>=this.ceilLab.rzsl-this.handleHalfWidth-10?(b=!0,this.hideEl(this.ceilLab)):(b=!1,this.showEl(this.ceilLab)),this.range&&(this.maxLab.rzsl+this.maxLab.rzsw>=this.ceilLab.rzsl-10?this.hideEl(this.ceilLab):b||this.showEl(this.ceilLab),this.maxLab.rzsl<=this.flrLab.rzsl+this.flrLab.rzsw+this.handleHalfWidth?this.hideEl(this.flrLab):a||this.showEl(this.flrLab))},updateSelectionBar:function(){this.setWidth(this.selBar,Math.abs(this.maxH.rzsl-this.minH.rzsl)+this.handleHalfWidth),this.setLeft(this.selBar,this.range?this.minH.rzsl+this.handleHalfWidth:0)},updateCmbLabel:function(){var a,b;this.minLab.rzsl+this.minLab.rzsw+10>=this.maxLab.rzsl?(a=this.getDisplayValue(this.scope.rzSliderModel),b=this.getDisplayValue(this.scope.rzSliderHigh),this.translateFn(a+" - "+b,this.cmbLab,!1),this.setLeft(this.cmbLab,this.selBar.rzsl+this.selBar.rzsw/2-this.cmbLab.rzsw/2),this.hideEl(this.minLab),this.hideEl(this.maxLab),this.showEl(this.cmbLab)):(this.showEl(this.maxLab),this.showEl(this.minLab),this.hideEl(this.cmbLab))},getDisplayValue:function(a){return this.customTrFn(a,this.options.id)},roundStep:function(a){var b=this.step,c=+((a-this.minValue)%b).toFixed(3),d=c>b/2?a+b-c:a-c;return d=d.toFixed(this.precision),+d},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.rzAlwaysHide?a:a.css({opacity:1})},setLeft:function(a,b){return a.rzsl=b,a.css({left:b+"px"}),b},getWidth:function(a){var b=a[0].getBoundingClientRect();return a.rzsw=(b.right-b.left)*this.options.scale,a.rzsw},setWidth:function(a,b){return a.rzsw=b,a.css({width:b+"px"}),b},valueToOffset:function(a){return(this.sanitizeOffsetValue(a)-this.minValue)*this.maxLeft/this.valueRange||0},sanitizeOffsetValue:function(a){return Math.min(Math.max(a,this.minValue),this.maxValue)},offsetToValue:function(a){return a/this.maxLeft*this.valueRange+this.minValue},getEventX:function(a){return"clientX"in a?a.clientX:void 0===a.originalEvent?a.touches[0].clientX:a.originalEvent.touches[0].clientX},getNearestHandle:function(a){if(!this.range)return this.minH;var b=(this.getEventX(a)-this.sliderElem.rzsl-this.handleHalfWidth)*this.options.scale;return Math.abs(b-this.minH.rzsl)=d){if(0===a.rzsl)return;e=this.minValue,d=0}else if(d>=this.maxLeft){if(a.rzsl===this.maxLeft)return;e=this.maxValue,d=this.maxLeft}else e=this.offsetToValue(d),e=this.roundStep(e),d=this.valueToOffset(e);this.positionTrackingHandle(e,d)},onDragStart:function(a,b,c){var d=this.getEventX(c)-this.sliderElem.rzsl-this.handleHalfWidth;this.dragging={active:!0,value:this.offsetToValue(d),difference:this.scope.rzSliderHigh-this.scope.rzSliderModel,offset:d,lowDist:d-this.minH.rzsl,highDist:this.maxH.rzsl-d},this.minH.addClass("rz-active"),this.maxH.addClass("rz-active"),this.onStart(a,b,c)},onDragMove:function(a,b){var c,d,e,f,g=this.getEventX(b)-this.sliderElem.rzsl-this.handleHalfWidth;if(g<=this.dragging.lowDist){if(a.rzsl===this.dragging.lowDist)return;e=this.minValue,c=0,f=this.minValue+this.dragging.difference,d=this.valueToOffset(f)}else if(g>=this.maxLeft-this.dragging.highDist){if(a.rzsl===this.dragging.highDist)return;f=this.maxValue,d=this.maxLeft,e=this.maxValue-this.dragging.difference,c=this.valueToOffset(e)}else e=this.offsetToValue(g-this.dragging.lowDist),e=this.roundStep(e),c=this.valueToOffset(e),f=e+this.dragging.difference,d=this.valueToOffset(f);this.positionTrackingBar(e,f,c,d)},positionTrackingBar:function(a,b,c,d){this.scope.rzSliderModel=a,this.scope.rzSliderHigh=b,this.updateHandles("rzSliderModel",c),this.updateHandles("rzSliderHigh",d),this.scope.$apply(),this.callOnChange()},positionTrackingHandle:function(a,b){this.range&&("rzSliderModel"===this.tracking&&a>=this.scope.rzSliderHigh?(this.scope[this.tracking]=this.scope.rzSliderHigh,this.updateHandles(this.tracking,this.maxH.rzsl),this.tracking="rzSliderHigh",this.minH.removeClass("rz-active"),this.maxH.addClass("rz-active"),this.scope.$apply(),this.callOnChange()):"rzSliderHigh"===this.tracking&&a<=this.scope.rzSliderModel&&(this.scope[this.tracking]=this.scope.rzSliderModel,this.updateHandles(this.tracking,this.minH.rzsl),this.tracking="rzSliderModel",this.maxH.removeClass("rz-active"),this.minH.addClass("rz-active"),this.scope.$apply(),this.callOnChange())),this.scope[this.tracking]!==a&&(this.scope[this.tracking]=a,this.updateHandles(this.tracking,b),this.scope.$apply(),this.callOnChange())},onEnd:function(a,b){var d=this.getEventNames(b).moveEvent;this.minH.removeClass("rz-active"),this.maxH.removeClass("rz-active"),c.off(d,a),this.scope.$emit("slideEnded"),this.tracking="",this.dragging.active=!1,this.callOnEnd()},getEventNames:function(a){var b={moveEvent:"",endEvent:""};return a.touches||void 0!==a.originalEvent&&a.originalEvent.touches?(b.moveEvent="touchmove",b.endEvent="touchend"):(b.moveEvent="mousemove",b.endEvent="mouseup"),b}},h}]).directive("rzslider",["RzSlider",function(a){return{restrict:"E",scope:{rzSliderModel:"=?",rzSliderHigh:"=?",rzSliderOptions:"=?",rzSliderTplUrl:"@"},templateUrl:function(a,b){return b.rzSliderTplUrl||"rzSliderTpl.html"},link:function(b,c){return new a(b,c)}}}]);return b.run(["$templateCache",function(a){a.put("rzSliderTpl.html",'
                                                  ')}]),b}); \ No newline at end of file diff --git a/src/rzslider.js b/src/rzslider.js index 56af511..b5e9fc0 100644 --- a/src/rzslider.js +++ b/src/rzslider.js @@ -585,7 +585,7 @@ this.minValue = this.roundStep(+this.options.floor); - if (this.options.ceil) + if (this.options.ceil != null) this.maxValue = this.roundStep(+this.options.ceil); else this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel;