diff --git a/src/carousel/carousel.js b/src/carousel/carousel.js index 14dd3a6bca..012e3da979 100644 --- a/src/carousel/carousel.js +++ b/src/carousel/carousel.js @@ -13,6 +13,11 @@ angular.module('ui.bootstrap.carousel', []) currentIndex = -1, currentInterval, isPlaying; self.currentSlide = null; + + // setting wrap as false if undefined + $scope.wrapValue = angular.isUndefined($scope.wrap)?true:$scope.wrap; + $scope.nextActive = true; + $scope.prevActive = true; var destroyed = false; /* direction: "prev" or "next" */ @@ -24,7 +29,9 @@ angular.module('ui.bootstrap.carousel', []) } if (nextSlide && nextSlide !== self.currentSlide) { goNext(); + self.updateCarouselControls(); } + function goNext() { // Scope has been destroyed, stop here. if (destroyed) { return; } @@ -53,7 +60,7 @@ angular.module('ui.bootstrap.carousel', []) return slides[index]; } var i, len = slides.length; - for (i = 0; i < slides.length; ++i) { + for (i = 0; i < len; ++i) { if (slides[i].index == index) { return slides[i]; } @@ -67,26 +74,81 @@ angular.module('ui.bootstrap.carousel', []) return currentIndex; }; + self.updateNextActive = function(){ + // if wrap and there are slides -> true + // if wrap false and there are next slides -> true + // otherwise -> false + if (slides.length !== 0) + { + if ($scope.wrapValue) + { + $scope.nextActive = true; + return; + } + else if (self.getCurrentIndex() + 1 < slides.length) + { + $scope.nextActive = true; + return; + } + } + + $scope.nextActive = false; + }; + + self.updatePrevActive = function(){ + // if wrap and there are slides -> true + // if wrap false and there are previous slides -> true + // otherwise -> false + if (slides.length !== 0) + { + if ($scope.wrapValue) + { + $scope.prevActive = true; + return; + } + else if (self.getCurrentIndex() - 1 >= 0) + { + $scope.prevActive = true; + return; + } + } + + $scope.prevActive = false; + }; + + self.updateCarouselControls = function (){ + self.updatePrevActive(); + self.updateNextActive(); + }; + /* Allow outside people to call indexOf on slides array */ self.indexOfSlide = function(slide) { return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide); }; $scope.next = function() { - var newIndex = (self.getCurrentIndex() + 1) % slides.length; + // if wrap is not active stops the next action + if ($scope.nextActive) + { + var newIndex = (self.getCurrentIndex() + 1) % slides.length; - //Prevent this user-triggered transition from occurring if there is already one in progress - if (!$scope.$currentTransition) { - return self.select(getSlideByIndex(newIndex), 'next'); + //Prevent this user-triggered transition from occurring if there is already one in progress + if (!$scope.$currentTransition) { + return self.select(getSlideByIndex(newIndex), 'next'); + } } }; $scope.prev = function() { - var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; + // if wrap is not active stops the next action + if ($scope.prevActive) + { + var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; - //Prevent this user-triggered transition from occurring if there is already one in progress - if (!$scope.$currentTransition) { - return self.select(getSlideByIndex(newIndex), 'prev'); + //Prevent this user-triggered transition from occurring if there is already one in progress + if (!$scope.$currentTransition) { + return self.select(getSlideByIndex(newIndex), 'prev'); + } } }; @@ -94,6 +156,13 @@ angular.module('ui.bootstrap.carousel', []) return self.currentSlide === slide; }; + $scope.$watch('wrap', updateWrapStatus); + + function updateWrapStatus() { + $scope.wrapValue = angular.isUndefined($scope.wrap)?true:$scope.wrap; + self.updateCarouselControls(); + } + $scope.$watch('interval', restartTimer); $scope.$on('$destroy', resetTimer); @@ -146,6 +215,7 @@ angular.module('ui.bootstrap.carousel', []) } else { slide.active = false; } + self.updateCarouselControls(); }; self.removeSlide = function(slide) { @@ -166,6 +236,7 @@ angular.module('ui.bootstrap.carousel', []) } else if (currentIndex > index) { currentIndex--; } + self.updateCarouselControls(); }; }]) @@ -181,6 +252,7 @@ angular.module('ui.bootstrap.carousel', []) * @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). + * @param {boolean=} wrap Whether the carousel should cycle continuously or have hard stops * * @example @@ -219,7 +291,8 @@ angular.module('ui.bootstrap.carousel', []) scope: { interval: '=', noTransition: '=', - noPause: '=' + noPause: '=', + wrap: '=' } }; }]) diff --git a/src/carousel/docs/README.md b/src/carousel/docs/README.md index 2c7d173adc..1a07573f6d 100644 --- a/src/carousel/docs/README.md +++ b/src/carousel/docs/README.md @@ -3,3 +3,5 @@ Carousel creates a carousel similar to bootstrap's image carousel. The carousel also offers support for touchscreen devices in the form of swiping. To enable swiping, load the `ngTouch` module as a dependency. Use a `` element with `` elements inside it. It will automatically cycle through the slides at a given rate, and a current-index variable will be kept in sync with the currently visible slide. + +Use the attribute `wrap` set to `false` (the default value is true) to had hard stops to the carousel. diff --git a/src/carousel/docs/demo.html b/src/carousel/docs/demo.html index 3f7d13b25b..4c6bc8abdb 100644 --- a/src/carousel/docs/demo.html +++ b/src/carousel/docs/demo.html @@ -1,6 +1,6 @@
- + +
+
+
diff --git a/src/carousel/docs/demo.js b/src/carousel/docs/demo.js index 7aed52da0b..db081f0639 100644 --- a/src/carousel/docs/demo.js +++ b/src/carousel/docs/demo.js @@ -1,5 +1,6 @@ angular.module('ui.bootstrap.demo').controller('CarouselDemoCtrl', function ($scope) { $scope.myInterval = 5000; + $scope.myWrap = true; var slides = $scope.slides = []; $scope.addSlide = function() { var newWidth = 600 + slides.length + 1; diff --git a/src/carousel/test/carousel.spec.js b/src/carousel/test/carousel.spec.js index 86e5a0735a..fe907debea 100644 --- a/src/carousel/test/carousel.spec.js +++ b/src/carousel/test/carousel.spec.js @@ -254,6 +254,68 @@ describe('carousel', function() { testSlideActive(1); }); + it('should stop navigation when wrap is false', function () { + scope.slides = [ + {active:false,content:'one'}, + {active:false,content:'two'}, + {active:false,content:'three'} + ]; + elm = $compile( + '' + + '' + + '{{slide.content}}' + + '' + + '' + )(scope); + scope.$apply(); + + var navPrev = elm.find('a.left'); + var navNext = elm.find('a.right'); + + testSlideActive(0); + navPrev.click(); + testSlideActive(0); + navNext.click(); + testSlideActive(1); + navNext.click(); + testSlideActive(2); + navNext.click(); + testSlideActive(2); + }); + + it('should hide navigation when wrap is false', function () { + scope.slides = [ + {active:false,content:'one'}, + {active:false,content:'two'}, + {active:false,content:'three'} + ]; + elm = $compile( + '' + + '' + + '{{slide.content}}' + + '' + + '' + )(scope); + scope.$apply(); + + var navPrev = elm.find('a.left'); + var navNext = elm.find('a.right'); + + testSlideActive(0); + expect(navPrev.hasClass('ng-hide')).toBe(true); + expect(navNext.hasClass('ng-hide')).toBe(false); + navNext.click(); + expect(navPrev.hasClass('ng-hide')).toBe(false); + expect(navNext.hasClass('ng-hide')).toBe(false); + testSlideActive(1); + expect(navPrev.hasClass('ng-hide')).toBe(false); + expect(navNext.hasClass('ng-hide')).toBe(false); + navNext.click(); + testSlideActive(2); + expect(navPrev.hasClass('ng-hide')).toBe(false); + expect(navNext.hasClass('ng-hide')).toBe(true); + }); + it('issue 1414 - should not continue running timers after scope is destroyed', function() { testSlideActive(0); $interval.flush(scope.interval); diff --git a/template/carousel/carousel.html b/template/carousel/carousel.html index 3b26a25814..eac72cda9a 100644 --- a/template/carousel/carousel.html +++ b/template/carousel/carousel.html @@ -3,6 +3,6 @@
  • - - + +