(function() { "use strict"; angular.module('shared') .directive('rangedSlider', function() { return { restrict: 'E', scope: { ngModel: '=', onChanged: '&', onSlide: '&', min: '<', max: '<', initMin: '<', initMax: '<', step: '<' }, replace: true, template: '
', require: 'ngModel', link: function (scope,element,attrs) { var settingInitVal = false; scope.$watch('ngModel', function(newVal, oldVal) { if (newVal !== undefined){ settingInitVal = true; element.slider("value", parseInt(newVal,10)); settingInitVal = false; } }); function onSlide( event, ui ) { if((ui.values[0]) >= ui.values[1]) { return false; } if ('onSlide' in attrs) { scope.onSlide({ leftValue: ui.values[0], rightValue: ui.values[1] }); } scope.ngModel = ui.value; scope.$apply(); values = ui.values; } function onChanged(event, ui) { if (!settingInitVal) { scope.onChanged({ leftValue: ui.values[0], rightValue: ui.values[1] }); } } element.bind("slide", onSlide); if ('onChanged' in attrs){ //(not executed during dragging the slider) element.bind( "slidechange", onChanged); } // Adding touch support below. var min = parseInt(scope.min, 10); var max = parseInt(scope.max, 10); var step = parseInt(scope.step, 10) || 1; var numTicks = Math.ceil((max - min) / step); var values = []; values[0] = scope.initMin; values[1] = scope.initMax; element.slider({ min: min, max: max, value: scope.ngModel, step: step, range: true, values: values }); var handles = element.children("a"); function getHandleIndex(handle) { return handle.index() - 1; //denotes the index of handle's value in values array } function getNearestTickFromPositionAsDecimal(position) { var sliderLeftBoundary = element.offset(); var sliderWidth = element.width(); position -= sliderLeftBoundary.left; position /= sliderWidth; //position is unrounded to the nearest tick at this point position *= numTicks; position = Math.round(position); //convert position into a slider value, to determine percentage position *= step; position += min; return getValueAsPercentageOfSlider(position); } function getValueAsPercentageOfSlider(value) { value -= min; value /= step * numTicks; value = Math.min(value, 1); return Math.max(value, 0); } function isAscending(array) { var lastValue = min - 1; var isAscending = true; for (var i = 0; i < array.length; ++i) { if (array[i] <= lastValue) { isAscending = false; } lastValue = array[i]; } return isAscending; } function updateSliderView(handle, values) { var handlePositionInSlider = getValueAsPercentageOfSlider(values[getHandleIndex(handle)]); var leftBoundaryOfSelectedRange = getValueAsPercentageOfSlider(values[0]); var rightBoundaryOfSelectedRange = getValueAsPercentageOfSlider(values[1]); var selectedRangeWidth = rightBoundaryOfSelectedRange - leftBoundaryOfSelectedRange; element.children("div").css({ left: leftBoundaryOfSelectedRange * 100 + '%', width: selectedRangeWidth * 100 + '%' }); handle.css({ left: handlePositionInSlider * 100 + '%' }); } var handleIndexMostRecentlyTouched; handles.bind("touchstart", function(event) { event.preventDefault(); var handle = jQuery(event.target); handleIndexMostRecentlyTouched = getHandleIndex(handle); handle.bind("touchmove", touchMove); handle.bind("touchend", touchEnd); }); function touchMove(event) { var handle = jQuery(event.target); var handlePosition = getNearestTickFromPositionAsDecimal(event.originalEvent.touches[0].pageX); var handleIndex = getHandleIndex(handle); var testValues = values.slice(); if (testValues[handleIndex] !== handlePosition * numTicks * step + min) { testValues[handleIndex] = handlePosition * numTicks * step + min; if (isAscending(testValues)) { values = testValues; updateSliderView(handle, values); onSlide(event, {values: values}); } } } function touchEnd(event) { var handle = jQuery(event.target); handle.unbind("touchmove"); handle.unbind("touchend"); if (isAscending(values)) { scope.$apply(function() { element.slider("values", handleIndexMostRecentlyTouched, values[handleIndexMostRecentlyTouched]); }); } } } } }); })();