(function (factory) { if (typeof define === 'function' && define.amd) { define(['angular', 'bootstrap-slider'], factory); } else if (typeof module === 'object' && module.exports) { module.exports = factory(require('angular'), require('bootstrap-slider')); } else if (window) { factory(window.angular, window.Slider); } })(function (angular, Slider) { angular.module('ui.bootstrap-slider', []) .directive('slider', ['$parse', '$timeout', '$rootScope', function ($parse, $timeout, $rootScope) { return { restrict: 'AE', replace: true, template: '
', require: 'ngModel', scope: { max: "=", min: "=", step: "=", value: "=", ngModel: '=', ngDisabled: '=', range: '=', sliderid: '=', ticks: '=', ticksLabels: '=', ticksSnapBounds: '=', ticksPositions: '=', scale: '=', focus: '=', formatter: '&', onStartSlide: '&', onStopSlide: '&', onSlide: '&' }, link: function ($scope, element, attrs, ngModelCtrl, $compile) { var ngModelDeregisterFn, ngDisabledDeregisterFn; var slider = initSlider(); function initSlider() { var options = {}; function setOption(key, value, defaultValue) { options[key] = value || defaultValue; } function setFloatOption(key, value, defaultValue) { options[key] = value || value === 0 ? parseFloat(value) : defaultValue; } function setBooleanOption(key, value, defaultValue) { options[key] = value ? value + '' === 'true' : defaultValue; } function getArrayOrValue(value) { return (angular.isString(value) && value.indexOf("[") === 0) ? angular.fromJson(value) : value; } setOption('id', $scope.sliderid); setOption('orientation', attrs.orientation, 'horizontal'); setOption('selection', attrs.selection, 'before'); setOption('handle', attrs.handle, 'round'); setOption('tooltip', attrs.sliderTooltip || attrs.tooltip, 'show'); setOption('tooltip_position', attrs.sliderTooltipPosition, 'top'); setOption('tooltipseparator', attrs.tooltipseparator, ':'); setOption('ticks', $scope.ticks); setOption('ticks_labels', $scope.ticksLabels); setOption('ticks_snap_bounds', $scope.ticksSnapBounds); setOption('ticks_positions', $scope.ticksPositions); setOption('scale', $scope.scale, 'linear'); setOption('focus', $scope.focus); setFloatOption('min', $scope.min, 0); setFloatOption('max', $scope.max, 10); setFloatOption('step', $scope.step, 1); var strNbr = options.step + ''; var dotPos = strNbr.search(/[^.,]*$/); var decimals = strNbr.substring(dotPos); setFloatOption('precision', attrs.precision, decimals.length); setBooleanOption('tooltip_split', attrs.tooltipsplit, false); setBooleanOption('enabled', attrs.enabled, true); setBooleanOption('naturalarrowkeys', attrs.naturalarrowkeys, false); setBooleanOption('reversed', attrs.reversed, false); setBooleanOption('range', $scope.range, false); if (options.range) { if (angular.isArray($scope.value)) { options.value = $scope.value; } else if (angular.isString($scope.value)) { options.value = getArrayOrValue($scope.value); if (!angular.isArray(options.value)) { var value = parseFloat($scope.value); if (isNaN(value)) value = 5; if (value < $scope.min) { value = $scope.min; options.value = [value, options.max]; } else if (value > $scope.max) { value = $scope.max; options.value = [options.min, value]; } else { options.value = [options.min, options.max]; } } } else { options.value = [options.min, options.max]; // This is needed, because of value defined at $.fn.slider.defaults - default value 5 prevents creating range slider } $scope.ngModel = options.value; // needed, otherwise turns value into [null, ##] } else { setFloatOption('value', $scope.value, 5); } if (attrs.formatter) { options.formatter = function (value) { return $scope.formatter({ value: value }); } } // check if slider jQuery plugin exists if ('$' in window && $.fn.slider) { // adding methods to jQuery slider plugin prototype $.fn.slider.constructor.prototype.disable = function () { this.picker.off(); }; $.fn.slider.constructor.prototype.enable = function () { this.picker.on(); }; } // destroy previous slider to reset all options if (element[0].__slider) element[0].__slider.destroy(); var slider = new Slider(element[0].getElementsByClassName('slider-input')[0], options); element[0].__slider = slider; // everything that needs slider element var updateEvent = getArrayOrValue(attrs.updateevent); if (angular.isString(updateEvent)) { // if only single event name in string updateEvent = [updateEvent]; } else { // default to slide event updateEvent = ['slide']; } angular.forEach(updateEvent, function (sliderEvent) { slider.on(sliderEvent, function (ev) { ngModelCtrl.$setViewValue(ev); }); }); slider.on('change', function (ev) { ngModelCtrl.$setViewValue(ev.newValue); }); // Event listeners var sliderEvents = { slideStart: 'onStartSlide', slide: 'onSlide', slideStop: 'onStopSlide' }; angular.forEach(sliderEvents, function (sliderEventAttr, sliderEvent) { var fn = $parse(attrs[sliderEventAttr]); slider.on(sliderEvent, function (ev) { if ($scope[sliderEventAttr]) { $scope.$apply(function () { fn($scope.$parent, { $event: ev, value: ev }); }); } }); }); // deregister ngDisabled watcher to prevent memory leaks if (angular.isFunction(ngDisabledDeregisterFn)) { ngDisabledDeregisterFn(); ngDisabledDeregisterFn = null; } ngDisabledDeregisterFn = $scope.$watch('ngDisabled', function (value) { if (value) { slider.disable(); } else { slider.enable(); } }); // deregister ngModel watcher to prevent memory leaks if (angular.isFunction(ngModelDeregisterFn)) ngModelDeregisterFn(); ngModelDeregisterFn = $scope.$watch('ngModel', function (value) { if ($scope.range) { slider.setValue(value); } else { slider.setValue(parseFloat(value)); } slider.relayout(); }, true); return slider; } var watchers = ['min', 'max', 'step', 'range', 'scale', 'ticksLabels']; angular.forEach(watchers, function (prop) { $scope.$watch(prop, function () { slider = initSlider(); }); }); var globalEvents = ['relayout', 'refresh', 'resize']; angular.forEach(globalEvents, function (event) { if (angular.isFunction(slider[event])) { $scope.$on('slider:' + event, function () { slider[event](); }); } }); } }; }]) ; });