UNPKG

angularjs-slider

Version:

AngularJS slider directive with no external dependencies. Mobile friendly!.

1,579 lines (1,377 loc) 286 kB
/* * angular-ui-bootstrap * http://angular-ui.github.io/bootstrap/ * Version: 0.14.3 - 2015-10-23 * License: MIT */ angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]); angular.module("ui.bootstrap.tpls", ["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-popup.html","template/tooltip/tooltip-popup.html","template/tooltip/tooltip-template-popup.html","template/popover/popover-html.html","template/popover/popover-template.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]); angular.module('ui.bootstrap.collapse', []) .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) { var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; return { link: function(scope, element, attrs) { function expand() { element.removeClass('collapse') .addClass('collapsing') .attr('aria-expanded', true) .attr('aria-hidden', false); if ($animateCss) { $animateCss(element, { addClass: 'in', easing: 'ease', to: { height: element[0].scrollHeight + 'px' } }).start().finally(expandDone); } else { $animate.addClass(element, 'in', { to: { height: element[0].scrollHeight + 'px' } }).then(expandDone); } } function expandDone() { element.removeClass('collapsing') .addClass('collapse') .css({height: 'auto'}); } function collapse() { if (!element.hasClass('collapse') && !element.hasClass('in')) { return collapseDone(); } element // IMPORTANT: The height must be set before adding "collapsing" class. // Otherwise, the browser attempts to animate from height 0 (in // collapsing class) to the given height here. .css({height: element[0].scrollHeight + 'px'}) // initially all panel collapse have the collapse class, this removal // prevents the animation from jumping to collapsed state .removeClass('collapse') .addClass('collapsing') .attr('aria-expanded', false) .attr('aria-hidden', true); if ($animateCss) { $animateCss(element, { removeClass: 'in', to: {height: '0'} }).start().finally(collapseDone); } else { $animate.removeClass(element, 'in', { to: {height: '0'} }).then(collapseDone); } } function collapseDone() { element.css({height: '0'}); // Required so that collapse works when animation is disabled element.removeClass('collapsing') .addClass('collapse'); } scope.$watch(attrs.uibCollapse, function(shouldCollapse) { if (shouldCollapse) { collapse(); } else { expand(); } }); } }; }]); /* Deprecated collapse below */ angular.module('ui.bootstrap.collapse') .value('$collapseSuppressWarning', false) .directive('collapse', ['$animate', '$injector', '$log', '$collapseSuppressWarning', function($animate, $injector, $log, $collapseSuppressWarning) { var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; return { link: function(scope, element, attrs) { if (!$collapseSuppressWarning) { $log.warn('collapse is now deprecated. Use uib-collapse instead.'); } function expand() { element.removeClass('collapse') .addClass('collapsing') .attr('aria-expanded', true) .attr('aria-hidden', false); if ($animateCss) { $animateCss(element, { easing: 'ease', to: { height: element[0].scrollHeight + 'px' } }).start().done(expandDone); } else { $animate.animate(element, {}, { height: element[0].scrollHeight + 'px' }).then(expandDone); } } function expandDone() { element.removeClass('collapsing') .addClass('collapse in') .css({height: 'auto'}); } function collapse() { if (!element.hasClass('collapse') && !element.hasClass('in')) { return collapseDone(); } element // IMPORTANT: The height must be set before adding "collapsing" class. // Otherwise, the browser attempts to animate from height 0 (in // collapsing class) to the given height here. .css({height: element[0].scrollHeight + 'px'}) // initially all panel collapse have the collapse class, this removal // prevents the animation from jumping to collapsed state .removeClass('collapse in') .addClass('collapsing') .attr('aria-expanded', false) .attr('aria-hidden', true); if ($animateCss) { $animateCss(element, { to: {height: '0'} }).start().done(collapseDone); } else { $animate.animate(element, {}, { height: '0' }).then(collapseDone); } } function collapseDone() { element.css({height: '0'}); // Required so that collapse works when animation is disabled element.removeClass('collapsing') .addClass('collapse'); } scope.$watch(attrs.collapse, function(shouldCollapse) { if (shouldCollapse) { collapse(); } else { expand(); } }); } }; }]); angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse']) .constant('uibAccordionConfig', { closeOthers: true }) .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) { // This array keeps track of the accordion groups this.groups = []; // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to this.closeOthers = function(openGroup) { var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers; if (closeOthers) { angular.forEach(this.groups, function(group) { if (group !== openGroup) { group.isOpen = false; } }); } }; // This is called from the accordion-group directive to add itself to the accordion this.addGroup = function(groupScope) { var that = this; this.groups.push(groupScope); groupScope.$on('$destroy', function(event) { that.removeGroup(groupScope); }); }; // This is called from the accordion-group directive when to remove itself this.removeGroup = function(group) { var index = this.groups.indexOf(group); if (index !== -1) { this.groups.splice(index, 1); } }; }]) // The accordion directive simply sets up the directive controller // and adds an accordion CSS class to itself element. .directive('uibAccordion', function() { return { controller: 'UibAccordionController', controllerAs: 'accordion', transclude: true, templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/accordion/accordion.html'; } }; }) // The accordion-group directive indicates a block of html that will expand and collapse in an accordion .directive('uibAccordionGroup', function() { return { require: '^uibAccordion', // We need this directive to be inside an accordion transclude: true, // It transcludes the contents of the directive into the template replace: true, // The element containing the directive will be replaced with the template templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/accordion/accordion-group.html'; }, scope: { heading: '@', // Interpolate the heading attribute onto this scope isOpen: '=?', isDisabled: '=?' }, controller: function() { this.setHeading = function(element) { this.heading = element; }; }, link: function(scope, element, attrs, accordionCtrl) { accordionCtrl.addGroup(scope); scope.openClass = attrs.openClass || 'panel-open'; scope.panelClass = attrs.panelClass; scope.$watch('isOpen', function(value) { element.toggleClass(scope.openClass, !!value); if (value) { accordionCtrl.closeOthers(scope); } }); scope.toggleOpen = function($event) { if (!scope.isDisabled) { if (!$event || $event.which === 32) { scope.isOpen = !scope.isOpen; } } }; } }; }) // Use accordion-heading below an accordion-group to provide a heading containing HTML .directive('uibAccordionHeading', function() { return { transclude: true, // Grab the contents to be used as the heading template: '', // In effect remove this element! replace: true, require: '^uibAccordionGroup', link: function(scope, element, attrs, accordionGroupCtrl, transclude) { // Pass the heading to the accordion-group controller // so that it can be transcluded into the right place in the template // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); } }; }) // Use in the accordion-group template to indicate where you want the heading to be transcluded // You must provide the property on the accordion-group controller that will hold the transcluded element .directive('uibAccordionTransclude', function() { return { require: ['?^uibAccordionGroup', '?^accordionGroup'], link: function(scope, element, attrs, controller) { controller = controller[0] ? controller[0] : controller[1]; // Delete after we remove deprecation scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) { if (heading) { element.find('span').html(''); element.find('span').append(heading); } }); } }; }); /* Deprecated accordion below */ angular.module('ui.bootstrap.accordion') .value('$accordionSuppressWarning', false) .controller('AccordionController', ['$scope', '$attrs', '$controller', '$log', '$accordionSuppressWarning', function($scope, $attrs, $controller, $log, $accordionSuppressWarning) { if (!$accordionSuppressWarning) { $log.warn('AccordionController is now deprecated. Use UibAccordionController instead.'); } angular.extend(this, $controller('UibAccordionController', { $scope: $scope, $attrs: $attrs })); }]) .directive('accordion', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { return { restrict: 'EA', controller: 'AccordionController', controllerAs: 'accordion', transclude: true, replace: false, templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/accordion/accordion.html'; }, link: function() { if (!$accordionSuppressWarning) { $log.warn('accordion is now deprecated. Use uib-accordion instead.'); } } }; }]) .directive('accordionGroup', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { return { require: '^accordion', // We need this directive to be inside an accordion restrict: 'EA', transclude: true, // It transcludes the contents of the directive into the template replace: true, // The element containing the directive will be replaced with the template templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/accordion/accordion-group.html'; }, scope: { heading: '@', // Interpolate the heading attribute onto this scope isOpen: '=?', isDisabled: '=?' }, controller: function() { this.setHeading = function(element) { this.heading = element; }; }, link: function(scope, element, attrs, accordionCtrl) { if (!$accordionSuppressWarning) { $log.warn('accordion-group is now deprecated. Use uib-accordion-group instead.'); } accordionCtrl.addGroup(scope); scope.openClass = attrs.openClass || 'panel-open'; scope.panelClass = attrs.panelClass; scope.$watch('isOpen', function(value) { element.toggleClass(scope.openClass, !!value); if (value) { accordionCtrl.closeOthers(scope); } }); scope.toggleOpen = function($event) { if (!scope.isDisabled) { if (!$event || $event.which === 32) { scope.isOpen = !scope.isOpen; } } }; } }; }]) .directive('accordionHeading', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { return { restrict: 'EA', transclude: true, // Grab the contents to be used as the heading template: '', // In effect remove this element! replace: true, require: '^accordionGroup', link: function(scope, element, attr, accordionGroupCtrl, transclude) { if (!$accordionSuppressWarning) { $log.warn('accordion-heading is now deprecated. Use uib-accordion-heading instead.'); } // Pass the heading to the accordion-group controller // so that it can be transcluded into the right place in the template // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); } }; }]) .directive('accordionTransclude', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { return { require: '^accordionGroup', link: function(scope, element, attr, controller) { if (!$accordionSuppressWarning) { $log.warn('accordion-transclude is now deprecated. Use uib-accordion-transclude instead.'); } scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) { if (heading) { element.find('span').html(''); element.find('span').append(heading); } }); } }; }]); angular.module('ui.bootstrap.alert', []) .controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) { $scope.closeable = !!$attrs.close; var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ? $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null; if (dismissOnTimeout) { $timeout(function() { $scope.close(); }, parseInt(dismissOnTimeout, 10)); } }]) .directive('uibAlert', function() { return { controller: 'UibAlertController', controllerAs: 'alert', templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/alert/alert.html'; }, transclude: true, replace: true, scope: { type: '@', close: '&' } }; }); /* Deprecated alert below */ angular.module('ui.bootstrap.alert') .value('$alertSuppressWarning', false) .controller('AlertController', ['$scope', '$attrs', '$controller', '$log', '$alertSuppressWarning', function($scope, $attrs, $controller, $log, $alertSuppressWarning) { if (!$alertSuppressWarning) { $log.warn('AlertController is now deprecated. Use UibAlertController instead.'); } angular.extend(this, $controller('UibAlertController', { $scope: $scope, $attrs: $attrs })); }]) .directive('alert', ['$log', '$alertSuppressWarning', function($log, $alertSuppressWarning) { return { controller: 'AlertController', controllerAs: 'alert', templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/alert/alert.html'; }, transclude: true, replace: true, scope: { type: '@', close: '&' }, link: function() { if (!$alertSuppressWarning) { $log.warn('alert is now deprecated. Use uib-alert instead.'); } } }; }]); angular.module('ui.bootstrap.buttons', []) .constant('uibButtonConfig', { activeClass: 'active', toggleEvent: 'click' }) .controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) { this.activeClass = buttonConfig.activeClass || 'active'; this.toggleEvent = buttonConfig.toggleEvent || 'click'; }]) .directive('uibBtnRadio', function() { return { require: ['uibBtnRadio', 'ngModel'], controller: 'UibButtonsController', controllerAs: 'buttons', link: function(scope, element, attrs, ctrls) { var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; element.find('input').css({display: 'none'}); //model -> UI ngModelCtrl.$render = function() { element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio))); }; //ui->model element.on(buttonsCtrl.toggleEvent, function() { if (attrs.disabled) { return; } var isActive = element.hasClass(buttonsCtrl.activeClass); if (!isActive || angular.isDefined(attrs.uncheckable)) { scope.$apply(function() { ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio)); ngModelCtrl.$render(); }); } }); } }; }) .directive('uibBtnCheckbox', function() { return { require: ['uibBtnCheckbox', 'ngModel'], controller: 'UibButtonsController', controllerAs: 'button', link: function(scope, element, attrs, ctrls) { var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; element.find('input').css({display: 'none'}); function getTrueValue() { return getCheckboxValue(attrs.btnCheckboxTrue, true); } function getFalseValue() { return getCheckboxValue(attrs.btnCheckboxFalse, false); } function getCheckboxValue(attribute, defaultValue) { return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue; } //model -> UI ngModelCtrl.$render = function() { element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); }; //ui->model element.on(buttonsCtrl.toggleEvent, function() { if (attrs.disabled) { return; } scope.$apply(function() { ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); ngModelCtrl.$render(); }); }); } }; }); /* Deprecated buttons below */ angular.module('ui.bootstrap.buttons') .value('$buttonsSuppressWarning', false) .controller('ButtonsController', ['$controller', '$log', '$buttonsSuppressWarning', function($controller, $log, $buttonsSuppressWarning) { if (!$buttonsSuppressWarning) { $log.warn('ButtonsController is now deprecated. Use UibButtonsController instead.'); } angular.extend(this, $controller('UibButtonsController')); }]) .directive('btnRadio', ['$log', '$buttonsSuppressWarning', function($log, $buttonsSuppressWarning) { return { require: ['btnRadio', 'ngModel'], controller: 'ButtonsController', controllerAs: 'buttons', link: function(scope, element, attrs, ctrls) { if (!$buttonsSuppressWarning) { $log.warn('btn-radio is now deprecated. Use uib-btn-radio instead.'); } var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; element.find('input').css({display: 'none'}); //model -> UI ngModelCtrl.$render = function() { element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio))); }; //ui->model element.bind(buttonsCtrl.toggleEvent, function() { if (attrs.disabled) { return; } var isActive = element.hasClass(buttonsCtrl.activeClass); if (!isActive || angular.isDefined(attrs.uncheckable)) { scope.$apply(function() { ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.btnRadio)); ngModelCtrl.$render(); }); } }); } }; }]) .directive('btnCheckbox', ['$document', '$log', '$buttonsSuppressWarning', function($document, $log, $buttonsSuppressWarning) { return { require: ['btnCheckbox', 'ngModel'], controller: 'ButtonsController', controllerAs: 'button', link: function(scope, element, attrs, ctrls) { if (!$buttonsSuppressWarning) { $log.warn('btn-checkbox is now deprecated. Use uib-btn-checkbox instead.'); } var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; element.find('input').css({display: 'none'}); function getTrueValue() { return getCheckboxValue(attrs.btnCheckboxTrue, true); } function getFalseValue() { return getCheckboxValue(attrs.btnCheckboxFalse, false); } function getCheckboxValue(attributeValue, defaultValue) { var val = scope.$eval(attributeValue); return angular.isDefined(val) ? val : defaultValue; } //model -> UI ngModelCtrl.$render = function() { element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); }; //ui->model element.bind(buttonsCtrl.toggleEvent, function() { if (attrs.disabled) { return; } scope.$apply(function() { ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); ngModelCtrl.$render(); }); }); //accessibility element.on('keypress', function(e) { if (attrs.disabled || e.which !== 32 || $document[0].activeElement !== element[0]) { return; } scope.$apply(function() { ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); ngModelCtrl.$render(); }); }); } }; }]); /** * @ngdoc overview * @name ui.bootstrap.carousel * * @description * AngularJS version of an image carousel. * */ angular.module('ui.bootstrap.carousel', []) .controller('UibCarouselController', ['$scope', '$element', '$interval', '$animate', function($scope, $element, $interval, $animate) { var self = this, slides = self.slides = $scope.slides = [], NEW_ANIMATE = angular.version.minor >= 4, NO_TRANSITION = 'uib-noTransition', SLIDE_DIRECTION = 'uib-slideDirection', currentIndex = -1, currentInterval, isPlaying; self.currentSlide = null; var destroyed = false; /* direction: "prev" or "next" */ self.select = $scope.select = function(nextSlide, direction) { var nextIndex = $scope.indexOfSlide(nextSlide); //Decide direction if it's not given if (direction === undefined) { direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; } //Prevent this user-triggered transition from occurring if there is already one in progress if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) { goNext(nextSlide, nextIndex, direction); } }; function goNext(slide, index, direction) { // Scope has been destroyed, stop here. if (destroyed) { return; } angular.extend(slide, {direction: direction, active: true}); angular.extend(self.currentSlide || {}, {direction: direction, active: false}); if ($animate.enabled() && !$scope.noTransition && !$scope.$currentTransition && slide.$element && self.slides.length > 1) { slide.$element.data(SLIDE_DIRECTION, slide.direction); if (self.currentSlide && self.currentSlide.$element) { self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction); } $scope.$currentTransition = true; if (NEW_ANIMATE) { $animate.on('addClass', slide.$element, function(element, phase) { if (phase === 'close') { $scope.$currentTransition = null; $animate.off('addClass', element); } }); } else { slide.$element.one('$animate:close', function closeFn() { $scope.$currentTransition = null; }); } } self.currentSlide = slide; currentIndex = index; //every time you change slides, reset the timer restartTimer(); } $scope.$on('$destroy', function() { destroyed = true; }); function getSlideByIndex(index) { if (angular.isUndefined(slides[index].index)) { return slides[index]; } var i, len = slides.length; for (i = 0; i < slides.length; ++i) { if (slides[i].index == index) { return slides[i]; } } } self.getCurrentIndex = function() { if (self.currentSlide && angular.isDefined(self.currentSlide.index)) { return +self.currentSlide.index; } return currentIndex; }; /* Allow outside people to call indexOf on slides array */ $scope.indexOfSlide = function(slide) { return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide); }; $scope.next = function() { var newIndex = (self.getCurrentIndex() + 1) % slides.length; if (newIndex === 0 && $scope.noWrap()) { $scope.pause(); return; } return self.select(getSlideByIndex(newIndex), 'next'); }; $scope.prev = function() { var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; if ($scope.noWrap() && newIndex === slides.length - 1) { $scope.pause(); return; } return self.select(getSlideByIndex(newIndex), 'prev'); }; $scope.isActive = function(slide) { return self.currentSlide === slide; }; $scope.$watch('interval', restartTimer); $scope.$watchCollection('slides', resetTransition); $scope.$on('$destroy', resetTimer); function restartTimer() { resetTimer(); var interval = +$scope.interval; if (!isNaN(interval) && interval > 0) { currentInterval = $interval(timerFn, interval); } } function resetTimer() { if (currentInterval) { $interval.cancel(currentInterval); currentInterval = null; } } function timerFn() { var interval = +$scope.interval; if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) { $scope.next(); } else { $scope.pause(); } } function resetTransition(slides) { if (!slides.length) { $scope.$currentTransition = null; } } $scope.play = function() { if (!isPlaying) { isPlaying = true; restartTimer(); } }; $scope.pause = function() { if (!$scope.noPause) { isPlaying = false; resetTimer(); } }; self.addSlide = function(slide, element) { slide.$element = element; slides.push(slide); //if this is the first slide or the slide is set to active, select it if (slides.length === 1 || slide.active) { self.select(slides[slides.length - 1]); if (slides.length === 1) { $scope.play(); } } else { slide.active = false; } }; self.removeSlide = function(slide) { if (angular.isDefined(slide.index)) { slides.sort(function(a, b) { return +a.index > +b.index; }); } //get the index of the slide inside the carousel var index = slides.indexOf(slide); slides.splice(index, 1); if (slides.length > 0 && slide.active) { if (index >= slides.length) { self.select(slides[index - 1]); } else { self.select(slides[index]); } } else if (currentIndex > index) { currentIndex--; } //clean the currentSlide when no more slide if (slides.length === 0) { self.currentSlide = null; } }; $scope.$watch('noTransition', function(noTransition) { $element.data(NO_TRANSITION, noTransition); }); }]) /** * @ngdoc directive * @name ui.bootstrap.carousel.directive:carousel * @restrict EA * * @description * Carousel is the outer container for a set of image 'slides' to showcase. * * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide. * @param {boolean=} noTransition Whether to disable transitions on the carousel. * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover). * * @example <example module="ui.bootstrap"> <file name="index.html"> <uib-carousel> <uib-slide> <img src="http://placekitten.com/150/150" style="margin:auto;"> <div class="carousel-caption"> <p>Beautiful!</p> </div> </uib-slide> <uib-slide> <img src="http://placekitten.com/100/150" style="margin:auto;"> <div class="carousel-caption"> <p>D'aww!</p> </div> </uib-slide> </uib-carousel> </file> <file name="demo.css"> .carousel-indicators { top: auto; bottom: 15px; } </file> </example> */ .directive('uibCarousel', [function() { return { transclude: true, replace: true, controller: 'UibCarouselController', controllerAs: 'carousel', require: 'carousel', templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/carousel/carousel.html'; }, scope: { interval: '=', noTransition: '=', noPause: '=', noWrap: '&' } }; }]) /** * @ngdoc directive * @name ui.bootstrap.carousel.directive:slide * @restrict EA * * @description * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element. * * @param {boolean=} active Model binding, whether or not this slide is currently active. * @param {number=} index The index of the slide. The slides will be sorted by this parameter. * * @example <example module="ui.bootstrap"> <file name="index.html"> <div ng-controller="CarouselDemoCtrl"> <uib-carousel> <uib-slide ng-repeat="slide in slides" active="slide.active" index="$index"> <img ng-src="{{slide.image}}" style="margin:auto;"> <div class="carousel-caption"> <h4>Slide {{$index}}</h4> <p>{{slide.text}}</p> </div> </uib-slide> </uib-carousel> Interval, in milliseconds: <input type="number" ng-model="myInterval"> <br />Enter a negative number to stop the interval. </div> </file> <file name="script.js"> function CarouselDemoCtrl($scope) { $scope.myInterval = 5000; } </file> <file name="demo.css"> .carousel-indicators { top: auto; bottom: 15px; } </file> </example> */ .directive('uibSlide', function() { return { require: '^uibCarousel', restrict: 'EA', transclude: true, replace: true, templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/carousel/slide.html'; }, scope: { active: '=?', actual: '=?', index: '=?' }, link: function (scope, element, attrs, carouselCtrl) { carouselCtrl.addSlide(scope, element); //when the scope is destroyed then remove the slide from the current slides array scope.$on('$destroy', function() { carouselCtrl.removeSlide(scope); }); scope.$watch('active', function(active) { if (active) { carouselCtrl.select(scope); } }); } }; }) .animation('.item', [ '$injector', '$animate', function ($injector, $animate) { var NO_TRANSITION = 'uib-noTransition', SLIDE_DIRECTION = 'uib-slideDirection', $animateCss = null; if ($injector.has('$animateCss')) { $animateCss = $injector.get('$animateCss'); } function removeClass(element, className, callback) { element.removeClass(className); if (callback) { callback(); } } return { beforeAddClass: function(element, className, done) { // Due to transclusion, noTransition property is on parent's scope if (className == 'active' && element.parent() && element.parent().parent() && !element.parent().parent().data(NO_TRANSITION)) { var stopped = false; var direction = element.data(SLIDE_DIRECTION); var directionClass = direction == 'next' ? 'left' : 'right'; var removeClassFn = removeClass.bind(this, element, directionClass + ' ' + direction, done); element.addClass(direction); if ($animateCss) { $animateCss(element, {addClass: directionClass}) .start() .done(removeClassFn); } else { $animate.addClass(element, directionClass).then(function () { if (!stopped) { removeClassFn(); } done(); }); } return function () { stopped = true; }; } done(); }, beforeRemoveClass: function (element, className, done) { // Due to transclusion, noTransition property is on parent's scope if (className === 'active' && element.parent() && element.parent().parent() && !element.parent().parent().data(NO_TRANSITION)) { var stopped = false; var direction = element.data(SLIDE_DIRECTION); var directionClass = direction == 'next' ? 'left' : 'right'; var removeClassFn = removeClass.bind(this, element, directionClass, done); if ($animateCss) { $animateCss(element, {addClass: directionClass}) .start() .done(removeClassFn); } else { $animate.addClass(element, directionClass).then(function() { if (!stopped) { removeClassFn(); } done(); }); } return function() { stopped = true; }; } done(); } }; }]); /* deprecated carousel below */ angular.module('ui.bootstrap.carousel') .value('$carouselSuppressWarning', false) .controller('CarouselController', ['$scope', '$element', '$controller', '$log', '$carouselSuppressWarning', function($scope, $element, $controller, $log, $carouselSuppressWarning) { if (!$carouselSuppressWarning) { $log.warn('CarouselController is now deprecated. Use UibCarouselController instead.'); } angular.extend(this, $controller('UibCarouselController', { $scope: $scope, $element: $element })); }]) .directive('carousel', ['$log', '$carouselSuppressWarning', function($log, $carouselSuppressWarning) { return { transclude: true, replace: true, controller: 'CarouselController', controllerAs: 'carousel', require: 'carousel', templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/carousel/carousel.html'; }, scope: { interval: '=', noTransition: '=', noPause: '=', noWrap: '&' }, link: function() { if (!$carouselSuppressWarning) { $log.warn('carousel is now deprecated. Use uib-carousel instead.'); } } }; }]) .directive('slide', ['$log', '$carouselSuppressWarning', function($log, $carouselSuppressWarning) { return { require: '^carousel', transclude: true, replace: true, templateUrl: function(element, attrs) { return attrs.templateUrl || 'template/carousel/slide.html'; }, scope: { active: '=?', actual: '=?', index: '=?' }, link: function (scope, element, attrs, carouselCtrl) { if (!$carouselSuppressWarning) { $log.warn('slide is now deprecated. Use uib-slide instead.'); } carouselCtrl.addSlide(scope, element); //when the scope is destroyed then remove the slide from the current slides array scope.$on('$destroy', function() { carouselCtrl.removeSlide(scope); }); scope.$watch('active', function(active) { if (active) { carouselCtrl.select(scope); } }); } }; }]); angular.module('ui.bootstrap.dateparser', []) .service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) { // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; var localeId; var formatCodeToRegex; this.init = function() { localeId = $locale.id; this.parsers = {}; formatCodeToRegex = { 'yyyy': { regex: '\\d{4}', apply: function(value) { this.year = +value; } }, 'yy': { regex: '\\d{2}', apply: function(value) { this.year = +value + 2000; } }, 'y': { regex: '\\d{1,4}', apply: function(value) { this.year = +value; } }, 'MMMM': { regex: $locale.DATETIME_FORMATS.MONTH.join('|'), apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); } }, 'MMM': { regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); } }, 'MM': { regex: '0[1-9]|1[0-2]', apply: function(value) { this.month = value - 1; } }, 'M': { regex: '[1-9]|1[0-2]', apply: function(value) { this.month = value - 1; } }, 'dd': { regex: '[0-2][0-9]{1}|3[0-1]{1}', apply: function(value) { this.date = +value; } }, 'd': { regex: '[1-2]?[0-9]{1}|3[0-1]{1}', apply: function(value) { this.date = +value; } }, 'EEEE': { regex: $locale.DATETIME_FORMATS.DAY.join('|') }, 'EEE': { regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|') }, 'HH': { regex: '(?:0|1)[0-9]|2[0-3]', apply: function(value) { this.hours = +value; } }, 'hh': { regex: '0[0-9]|1[0-2]', apply: function(value) { this.hours = +value; } }, 'H': { regex: '1?[0-9]|2[0-3]', apply: function(value) { this.hours = +value; } }, 'h': { regex: '[0-9]|1[0-2]', apply: function(value) { this.hours = +value; } }, 'mm': { regex: '[0-5][0-9]', apply: function(value) { this.minutes = +value; } }, 'm': { regex: '[0-9]|[1-5][0-9]', apply: function(value) { this.minutes = +value; } }, 'sss': { regex: '[0-9][0-9][0-9]', apply: function(value) { this.milliseconds = +value; } }, 'ss': { regex: '[0-5][0-9]', apply: function(value) { this.seconds = +value; } }, 's': { regex: '[0-9]|[1-5][0-9]', apply: function(value) { this.seconds = +value; } }, 'a': { regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), apply: function(value) { if (this.hours === 12) { this.hours = 0; } if (value === 'PM') { this.hours += 12; } } } }; }; this.init(); function createParser(format) { var map = [], regex = format.split(''); angular.forEach(formatCodeToRegex, function(data, code) { var index = format.indexOf(code); if (index > -1) { format = format.split(''); regex[index] = '(' + data.regex + ')'; format[index] = '$'; // Custom symbol to define consumed part of format for (var i = index + 1, n = index + code.length; i < n; i++) { regex[i] = ''; format[i] = '$'; } format = format.join(''); map.push({ index: index, apply: data.apply }); } }); return { regex: new RegExp('^' + regex.join('') + '$'), map: orderByFilter(map, 'index') }; } this.parse = function(input, format, baseDate) { if (!angular.isString(input) || !format) { return input; } format = $locale.DATETIME_FORMATS[format] || format; format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&'); if ($locale.id !== localeId) { this.init(); } if (!this.parsers[format]) { this.parsers[format] = createParser(format); } var parser = this.parsers[format], regex = parser.regex, map = parser.map, results = input.match(regex); if (results && results.length) { var fields, dt; if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { fields = { year: baseDate.getFullYear(), month: baseDate.getMonth(), date: baseDate.getDate(), hours: baseDate.getHours(), minutes: baseDate.getMinutes(), seconds: baseDate.getSeconds(), milliseconds: baseDate.getMilliseconds() }; } else { if (baseDate) { $log.warn('dateparser:', 'baseDate is not a valid date'); } fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }; } for (var i = 1, n = results.length; i < n; i++) { var mapper = map[i-1]; if (mapper.apply) { mapper.apply.call(fields, results[i]); } } if (isValid(fields.year, fields.month, fields.date)) { if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { dt = new Date(baseDate); dt.setFullYear(fields.year, fields.month, fields.date, fields.hours, fields.minutes, fields.seconds, fields.milliseconds || 0); } else { dt = new Date(fields.year, fields.month, fields.date, fields.hours, fields.minutes, fields.seconds, fields.milliseconds || 0); } } return dt; } }; // Check if date is valid for specific month (and year for February). // Month: 0 = Jan, 1 = Feb, etc function isValid(year, month, date) { if (date < 1) { return false; } if (month === 1 && date > 28) { return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); } if (month === 3 || month === 5 || month === 8 || month === 10) { return date < 31; } return true; } }]); /* Deprecated dateparser below */ angular.module('ui.bootstrap.dateparser') .value('$dateParserSuppressWarning', false) .service('dateParser', ['$log', '$dateParserSuppressWarning', 'uibDateParser', function($log, $dateParserSuppressWarning, uibDateParser) { if (!$dateParserSuppressWarning) { $log.warn('dateParser is now deprecated. Use uibDateParser instead.'); } angular.extend(this, uibDateParser); }]); angular.module('ui.bootstrap.position', []) /** * A set of utility methods that can be use to retrieve position of DOM elements. * It is meant to be used where we need to absolute-position DOM elements in * relation to other, existing elements (this is the case for tooltips, popovers, * typeahead suggestions etc.). */ .factory('$uibPosition', ['$document', '$window', function($document, $window) { function getStyle(el, cssprop) { if (el.currentStyle) { //IE return el.currentStyle[cssprop]; } else if ($window.getComputedStyle) { return $window.getComputedStyle(el)[cssprop]; } // finally try and get inline style return el.style[cssprop]; } /** * Checks if a given element is statically positioned * @param element - raw DOM element */ function isStaticPositioned(element) { return (getStyle(element, 'position') || 'static' ) === 'static'; } /** * returns the closest, non-statically positioned parentOffset of a given element * @param element */ var parentOffsetEl = function(element) { var docDomEl = $document[0]; var offsetParent = element.offsetParent || docDomEl; while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) { offsetParent = offsetParent.offsetParent; } return offsetParent || docDomEl; }; return { /** * Provides read-only equivalent of jQuery's position function: * http://api.jquery.com/position/ */ position: function(element) { var elBCR = this.offset(element); var offsetParentBCR = { top: 0, left: 0 }; var offsetParentEl = parentOffsetEl(element[0]); if (offsetParentEl != $document[0]) { offsetParentBCR = this.offset(angular.element(offsetParentEl)); offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop; offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; } var boundingClientRect = element[0].getBoundingClientRect(); return { width: boundingClientRect.width || element.prop('offsetWidth'), height: boundingClientRect.height || element.prop('offsetHeight'), top: elBCR.top - offsetParentBCR.top, left: elBCR.left - offsetParentBCR.left }; }, /** * Provides read-only equivalent of jQuery's offset function: * http://api.jquery.com/offset/ */ offset: function(element) { var boundingClientRect = element[0].getBoundingClientRect(); return { width: boundingClientRect.width || element.prop('offsetWidth'), height: boundingClientRect.height || element.prop('offsetHeight'), top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop), left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft) }; }, /** * Provides coordinates for the targetEl in relation to hostEl */ positionElements: function(hostEl, targetEl, positionStr, appendToBody) { var positionStrParts = positionStr.split('-'); var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center'; var hostElPos, targetElWidth, targetElHeight, targetElPos; hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl); targetElWidth = targetEl.prop('offsetWidth'); targetElHeight = targetEl.prop('offsetHeight'); var shiftWidth = { center: function() { return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2; }, left: function() { return hostElPos.left; }, right: function() { return hostElPos.left + hostElPos.width; } }; var shiftHeight = { center: function() { return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2; }, top: function() { return hostElPos.top; }, bottom: function() { return hostElPos.top + hostElPos.height; } }; switch (pos0) { case 'right': targetElPos = { top: shiftHeight[pos1](), left: shiftWidth[pos0]() }; break; case 'left': targetElPos = { top: shiftHeight[pos1](), left: hostElPos.left - targetElWidth }; break; case 'bottom': targetElPos = { top: shiftHeight[pos0](), left: shiftWidth[pos1]() }; break; default: targetElPos = { top: hostElPos.top - targetElHeight, left: shiftWidth[pos1]() }; break; } return targetElPos; } }; }]); /* Deprecated position below */ angular.module('ui.bootstrap.position') .value('$positionSuppressWarning', false) .service('$position', ['$log', '$positionSuppressWarning', '$uibPosition', function($log, $positionSuppressWarning, $uibPosition) { if (!$positionSuppressWarning) { $log.warn('$position is now deprecated. Use