diff --git a/includes/jcarousel_style_plugin.inc b/includes/jcarousel_style_plugin.inc
index 53dcbc9..44b73f7 100644
--- a/includes/jcarousel_style_plugin.inc
+++ b/includes/jcarousel_style_plugin.inc
@@ -23,6 +23,7 @@ class jcarousel_style_plugin extends views_plugin_style {
     $options['start'] = array('default' => '1');
     $options['easing'] = array('default' => NULL);
     $options['vertical'] = array('default' => FALSE);
+    $options['navigation'] = array('default' => '');
     return $options;
   }
 
@@ -88,6 +89,17 @@ class jcarousel_style_plugin extends views_plugin_style {
       '#field_suffix' => ' ' . t('seconds'),
       '#description' => t('Specifies how many seconds to periodically auto-scroll the content. If set to 0 (default) then autoscrolling is turned off.'),
     );
+    $form['navigation'] = array(
+      '#type' => 'select',
+      '#title' => t('Enable navigation'),
+      '#options' => array(
+        '' => t('None'),
+        'before' => t('Before'),
+        'after' => t('After'),
+      ),
+      '#default_value' => $this->options['navigation'],
+      '#description' => t('Enable a clickable navigation list to jump straight to a given page.'),
+    );
 
     $form['advanced'] = array(
       '#type' => 'fieldset',
diff --git a/jcarousel.module b/jcarousel.module
index a01fd98..0164dbf 100644
--- a/jcarousel.module
+++ b/jcarousel.module
@@ -308,7 +308,7 @@ function jcarousel_skins() {
 
 /**
  * Creates a jCarousel element on the page.
- * 
+ *
  * @param $items
  *   The items to appear in the carousel.
  * @param $options
diff --git a/js/jcarousel.js b/js/jcarousel.js
index 00296b0..06604e8 100644
--- a/js/jcarousel.js
+++ b/js/jcarousel.js
@@ -7,14 +7,13 @@
 
 Drupal.behaviors.jcarousel = {};
 Drupal.behaviors.jcarousel.attach = function(context, settings) {
-  var settings = settings || Drupal.settings;
-  for (var key in settings.jcarousel.carousels) {
-    var options = settings.jcarousel.carousels[key];
+  settings = settings || Drupal.settings;
+  jQuery.each(settings.jcarousel.carousels, function(key, options) {
     var $carousel = $(options.selector + ':not(.jcarousel-processed)', context);
 
     // If this carousel has already been processed or doesn't exist, move on.
     if (!$carousel.length) {
-      continue;
+      return;
     }
 
     // Callbacks need to be converted from a string to an actual function.
@@ -38,7 +37,50 @@ Drupal.behaviors.jcarousel.attach = function(context, settings) {
     if (options.auto && options.autoPause && !options.initCallback) {
       options.initCallback = function(carousel, state) {
         Drupal.jcarousel.autoPauseCallback(carousel, state);
-      }
+      };
+    }
+
+    if (options.navigation) {
+      options.setupCallback = function(carousel) {
+        $(carousel.list)[options.navigation](function() {
+          // This only works for a positive starting point.  Also, .first is 1-based
+          // while .last is a count, so we need to reset the .first number to be
+          // 0-based to make the math work.
+          var pageSize = carousel.last - (carousel.first - 1);
+          var navigation = $('<ul class="jcarousel-navigation"></ul>');
+          var numPages = Math.ceil($('li', carousel.list).length / pageSize);
+
+          for (var i = 0; i < numPages; i++) {
+            var pagerItem = $(Drupal.theme('jCarouselButton', 'navigation', i));
+            var listItem = $('<li></li>').attr('jCarousel-index', i).append(pagerItem);
+            navigation.append(listItem);
+
+            // Make the first page active by default.
+            if (i === 0) {
+              listItem.addClass('active');
+            }
+
+            // Scroll to the correct page when a pager is clicked.
+            pagerItem.bind('click', function() {
+              // Need to scroll to the first item in that page, which means we
+              // need to add one more to the value to get the item to scroll to
+              // in order to get to that particular page.  Weird but true.
+              carousel.scroll($(this).parent().attr('jCarousel-index') * pageSize + 1);
+              return false;
+            });
+          }
+
+          return navigation;
+        });
+      };
+
+      options.itemVisibleInCallback = {
+          onAfterAnimation: function (carousel, item, idx, state) {
+            var i = carousel.index(idx) - 1;
+            var listItems = $('.jcarousel-navigation li', $(item).parents('.jcarousel-container')).removeClass('active');
+            $(listItems[i]).addClass('active');
+          }
+      };
     }
 
     if (!options.hasOwnProperty('buttonNextHTML') && !options.hasOwnProperty('buttonPrevHTML')) {
@@ -48,7 +90,7 @@ Drupal.behaviors.jcarousel.attach = function(context, settings) {
 
     // Initialize the jcarousel.
     $carousel.addClass('jcarousel-processed').jcarousel(options);
-  }
+  });
 };
 
 Drupal.jcarousel = {};
@@ -72,7 +114,7 @@ Drupal.jcarousel.ajaxLoadCallback = function(jcarousel, state) {
   });
 
   // Copied from ajax_view.js:
-  var viewData = { 'js': 1, 'first': jcarousel.first - 1, 'last': jcarousel.last };
+  var viewData = {'js': 1, 'first': jcarousel.first - 1, 'last': jcarousel.last};
   // Construct an object using the settings defaults and then overriding
   // with data specific to the link.
   $.extend(
@@ -85,9 +127,11 @@ Drupal.jcarousel.ajaxLoadCallback = function(jcarousel, state) {
     type: 'GET',
     data: viewData,
     success: function(response) {
-      Drupal.jcarousel.ajaxResponseCallback(jcarousel, target, response)
+      Drupal.jcarousel.ajaxResponseCallback(jcarousel, target, response);
+    },
+    error: function(xhr) {
+      Drupal.jcarousel.ajaxErrorCallback(xhr, ajaxPath);
     },
-    error: function(xhr) { Drupal.jcarousel.ajaxErrorCallback(xhr, ajaxPath); },
     dataType: 'json'
   });
 
@@ -117,7 +161,7 @@ Drupal.jcarousel.ajaxResponseCallback = function(jcarousel, target, response) {
   }
 
   var $view = $(target);
-  var jcarousel = $view.find('ul.jcarousel').data('jcarousel');
+  jcarousel = $view.find('ul.jcarousel').data('jcarousel');
 
   // Add items to the jCarousel.
   $('ul.jcarousel li', response.display).each(function(i) {
@@ -162,9 +206,9 @@ Drupal.jcarousel.ajaxErrorCallback = function (xhr, path) {
   alert(Drupal.t("An error occurred at @path.\n\nError Description: @error", {'@path': path, '@error': error_text}));
 };
 
-Drupal.theme.prototype.jCarouselButton = function(type) {
+Drupal.theme.prototype.jCarouselButton = function(type, page) {
   // Use links for buttons for accessibility.
-  return '<a href="javascript:void()"></a>';
+  return '<a href="javascript:void()">' + (page === 0 || page ? ++page : '') + '</a>';
 };
 
 })(jQuery);
diff --git a/skins/default/jcarousel-default.css b/skins/default/jcarousel-default.css
index 9166f5f..3bd03ef 100644
--- a/skins/default/jcarousel-default.css
+++ b/skins/default/jcarousel-default.css
@@ -152,3 +152,7 @@
   background-position: -96px -96px;
 }
 
+.jcarousel-skin-default .jcarousel-pager li {
+  display: inline;
+  margin-right: 2px;
+}
diff --git a/skins/tango/jcarousel-tango.css b/skins/tango/jcarousel-tango.css
index ab4c3d6..437ed44 100644
--- a/skins/tango/jcarousel-tango.css
+++ b/skins/tango/jcarousel-tango.css
@@ -155,3 +155,7 @@
   background-position: -96px -96px;
 }
 
+.jcarousel-skin-tango .jcarousel-pager li {
+  display: inline;
+  margin-right: 2px;
+}
