function AutoScroll(mapSettings) {
   var _self = this;
   var _scrolltrack;
   var _blocks;
   var _itemselectors;
   var _blockSize;
   var _currentBlockIndex;
   var _timer = 0;
   var _onscrollended = false;

   //default settings
   var settings = {
      rootelement: null,
      animationtime: 500,
      waittime: 4000,
      direction: 'vertical',
      pos: '',
      dim: ''
   }

   ////////////

   _self.next = function () {
      var _css = new Object();
      var _index = ((_currentBlockIndex+1) % _blocks.length);

      _css[settings.pos] = -(_index * _blockSize);

      _scrolltrack.animate(
         _css,
         {
            duration: settings.animationtime,
            complete: function () {
               //fire callback
               if (settings.onscrollend) {
                  settings.onscrollend(
                     _blocks.eq((_index) % _blocks.length),
                     ((_currentBlockIndex + _blocks.length) % _blocks.length)
                  );
               }
               _itemselectors.eq(_currentBlockIndex % _blocks.length).removeClass('active');
               _itemselectors.eq(_index).addClass('active');

               _currentBlockIndex++;
            }
         }
      );
   }

   _self.prev = function () {
      var _css = new Object();
      var _index = (_currentBlockIndex % _blocks.length);
      var jBlock = _blocks.eq(_index);

      //put last block at start to wrap the scrolling.
      _css[settings.pos] = parseInt(jBlock.css(settings.pos)) - (_blocks.length * _blockSize);
      jBlock.css(_css);

      _css[settings.pos] = parseInt(_scrolltrack.css(settings.pos)) + _blockSize;
      _scrolltrack.animate(
         _css,
         {
            duration: settings.animationtime,
            complete: function () {
               //fire callback
               if (settings.onscrollend) {
                  settings.onscrollend(
                     jBlock,
                     (_currentBlockIndex + _blocks.length) % _blocks.length
                  );
               }
               _currentBlockIndex--;
            }
         }
      );
   }
   _self.manualNext = function () {
      clearInterval( _timer );
      _self.next();

      return false;
   }
   _self.manualPrev = function () {
      clearInterval( _timer );
      _self.prev();

      return false;
   }
   _self.goTo = function (n) {
      //console.log('GOTO:', n);
      var _css = new Object();
      var _index = (_currentBlockIndex % _blocks.length);
      //console.log('current index', _index);

      clearInterval(_timer);

      _css[settings.pos] = -(n * _blockSize);

      _scrolltrack.animate(
         _css,
         {
            duration: settings.animationtime,
            complete: function () {
               //console.log('GOTO', n ,'COMPLETED');
               //console.log('curblockindex', _currentBlockIndex);
               //console.log('deactivate button:', _currentBlockIndex % _blocks.length);

               //fire callback
               if (settings.onscrollend) {
                  settings.onscrollend(_blocks.eq(n), n);
               }

               _itemselectors.eq(_currentBlockIndex % _blocks.length).removeClass('active');
               _itemselectors.eq(n).addClass('active');

               _currentBlockIndex = n;
            }
         }
      );
      return;
   }

   ////////////

   //override default settings
   if (mapSettings) {
      for (var key in mapSettings) {
         settings[key] = mapSettings[key];
      }
   }

   if (!settings.rootelement) {
      console.log('No root element specified. Cannot create autoscroller.');
      return;
   }

   switch (settings.direction) {
      case 'vertical':
         settings.pos = 'top';
         settings.dim = 'Height';
      break;
      case 'horizontal':
      default:
         settings.pos = 'left';
         settings.dim = 'Width';
   }

   _scrolltrack = jQuery('.scroll_track', settings.rootelement);
   _blocks = jQuery('.scroll_item', settings.rootelement);
   _itemselectors = jQuery('.scroll_itemselector a', settings.rootelement);
   _blockSize = _blocks.get(0)['offset'+ settings.dim];
   _currentBlockIndex = 0;

   // Distribute blocks
   _blocks.each(function (i) {
      var _css = new Object();
      _css[settings.pos] = i * _blockSize;
      jQuery(this).css(_css);
   });

   //guard against too few items. Then, autofit the container to the items and hide the nav.
   var fewBlocks = (settings.rootelement[settings.dim.toLowerCase()]() > (_blocks.length * _blockSize))
   if (fewBlocks) {
     settings.rootelement.height(_blocks.length * _blockSize);
     jQuery('.sidebar_pager', settings.rootelement.parent()).hide();

     return;
   }

   //set manual scroll events
   jQuery('.scroll_pager_prev', settings.rootelement).click(_self.manualPrev);
   jQuery('.scroll_pager_next', settings.rootelement).click(_self.manualNext);

   _itemselectors.each(function (x) {
      $(this).click(function () {
         _self.goTo(x);

         return false;
      });
   });

   //initialise
   //fire callback
   if (settings.oninitialise) settings.oninitialise(_blocks.eq(_currentBlockIndex), _currentBlockIndex);

   _timer = setInterval(
      _self.next,
      settings.waittime
   );
}
