diff --git a/js/carousel.js b/js/carousel.js
index 282d618fe20e8eef9840b8aa866183657a06feb9..99a077c10a7d55da6d02ed310eb7eec78ce1ce84 100644
--- a/js/carousel.js
+++ b/js/carousel.js
@@ -70,8 +70,11 @@
   }
 
   Carousel.prototype.getItemForDirection = function (direction, active) {
-    var delta = direction == 'prev' ? -1 : 1
     var activeIndex = this.getItemIndex(active)
+    var willWrap = (direction == 'prev' && activeIndex === 0)
+                || (direction == 'next' && activeIndex == (this.$items.length - 1))
+    if (willWrap && !this.options.wrap) return active
+    var delta = direction == 'prev' ? -1 : 1
     var itemIndex = (activeIndex + delta) % this.$items.length
     return this.$items.eq(itemIndex)
   }
@@ -116,14 +119,8 @@
     var $next     = next || this.getItemForDirection(type, $active)
     var isCycling = this.interval
     var direction = type == 'next' ? 'left' : 'right'
-    var fallback  = type == 'next' ? 'first' : 'last'
     var that      = this
 
-    if (!$next.length) {
-      if (!this.options.wrap) return
-      $next = this.$element.find('.item')[fallback]()
-    }
-
     if ($next.hasClass('active')) return (this.sliding = false)
 
     var relatedTarget = $next[0]
diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js
index 5998abe6af8365a1cbbd4f63585d4d022f671d64..008b7208255227f0d05ee70930a23680b05484aa 100644
--- a/js/tests/unit/carousel.js
+++ b/js/tests/unit/carousel.js
@@ -541,4 +541,157 @@ $(function () {
       strictEqual(type in $._data($template[0], 'events'), !isMobile, 'does' + (isMobile ? ' not' : '') + ' listen for ' + type + ' events')
     })
   })
+
+  test('should wrap around from end to start when wrap option is true', function () {
+    var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">'
+        + '<ol class="carousel-indicators">'
+        + '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
+        + '<li data-target="#carousel-example-generic" data-slide-to="1"/>'
+        + '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+        + '</ol>'
+        + '<div class="carousel-inner">'
+        + '<div class="item active" id="one">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '<div class="item" id="two">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '<div class="item" id="three">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '</div>'
+        + '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>'
+        + '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>'
+        + '</div>'
+    var $carousel = $(carouselHTML)
+    var getActiveId = function () { return $carousel.find('.item.active').attr('id') }
+
+    stop()
+
+    $carousel
+      .one('slid.bs.carousel', function () {
+        strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
+        $carousel
+          .one('slid.bs.carousel', function () {
+            strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
+            $carousel
+              .one('slid.bs.carousel', function () {
+                strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide')
+                start()
+              })
+              .bootstrapCarousel('next')
+          })
+          .bootstrapCarousel('next')
+      })
+      .bootstrapCarousel('next')
+  })
+
+  test('should wrap around from start to end when wrap option is true', function () {
+    var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">'
+        + '<ol class="carousel-indicators">'
+        + '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
+        + '<li data-target="#carousel-example-generic" data-slide-to="1"/>'
+        + '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+        + '</ol>'
+        + '<div class="carousel-inner">'
+        + '<div class="item active" id="one">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '<div class="item" id="two">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '<div class="item" id="three">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '</div>'
+        + '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>'
+        + '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>'
+        + '</div>'
+    var $carousel = $(carouselHTML)
+
+    stop()
+
+    $carousel
+      .on('slid.bs.carousel', function () {
+        strictEqual($carousel.find('.item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide')
+        start()
+      })
+      .bootstrapCarousel('prev')
+  })
+
+  test('should stay at the end when the next method is called and wrap is false', function () {
+    var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">'
+        + '<ol class="carousel-indicators">'
+        + '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
+        + '<li data-target="#carousel-example-generic" data-slide-to="1"/>'
+        + '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+        + '</ol>'
+        + '<div class="carousel-inner">'
+        + '<div class="item active" id="one">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '<div class="item" id="two">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '<div class="item" id="three">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '</div>'
+        + '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>'
+        + '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>'
+        + '</div>'
+    var $carousel = $(carouselHTML)
+    var getActiveId = function () { return $carousel.find('.item.active').attr('id') }
+
+    stop()
+
+    $carousel
+      .one('slid.bs.carousel', function () {
+        strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
+        $carousel
+          .one('slid.bs.carousel', function () {
+            strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
+            $carousel
+              .one('slid.bs.carousel', function () {
+                ok(false, 'carousel slid when it should not have slid')
+              })
+              .bootstrapCarousel('next')
+            strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide')
+            start()
+          })
+          .bootstrapCarousel('next')
+      })
+      .bootstrapCarousel('next')
+  })
+
+  test('should stay at the start when the prev method is called and wrap is false', function () {
+    var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">'
+        + '<ol class="carousel-indicators">'
+        + '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
+        + '<li data-target="#carousel-example-generic" data-slide-to="1"/>'
+        + '<li data-target="#carousel-example-generic" data-slide-to="2"/>'
+        + '</ol>'
+        + '<div class="carousel-inner">'
+        + '<div class="item active" id="one">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '<div class="item" id="two">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '<div class="item" id="three">'
+        + '<div class="carousel-caption"/>'
+        + '</div>'
+        + '</div>'
+        + '<a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"/>'
+        + '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>'
+        + '</div>'
+    var $carousel = $(carouselHTML)
+
+    $carousel
+      .on('slid.bs.carousel', function () {
+        ok(false, 'carousel slid when it should not have slid')
+      })
+      .bootstrapCarousel('prev')
+    strictEqual($carousel.find('.item.active').attr('id'), 'one', 'carousel did not wrap around and stayed on 1st slide')
+  })
 })