jQuery.fn.extend({
 scrollbar: function(contentMask, scrollContent, options) {
  return this.each(function() {
   new jQuery.Scrollbar(this, contentMask, scrollContent, options);
  });
 }
});

jQuery.Scrollbar = function(element, contentMask, scrollContent, options) {
 if (!options) options = {};

 var settings = {
  step: 6,
  speed: 25,
  hoverWidth: 4,
  expandScroll: true
 };

 var $o_self = $(element);
 var $o_contentMask = $(contentMask);
 var $o_scrollContent = $(scrollContent);
 var h_interval;
 var b_dragged = false;
 var i_mousePositionY = 0;
 var b_hover = false;

 // Aktualizacja ustawień:
 $.extend(settings, options);

 // Wyczyzczenie obiektu scrolla (jeśli wcześniej był używany na tym samym obiekcie)
 $o_self.unScrollbar(element, contentMask, scrollContent);

 // Aktywacja/dezaktywacja scrolla:
 if ($o_scrollContent.height() < $o_contentMask.height()) $o_self.addClass('inactive');
 else $o_self.removeClass('inactive');

 // Inicjacja składowych scrolla:
 var $o_upActivity = $('<div class="upActivity"></div>');
 var $o_up = $('<div class="up"></div>');
 var $o_downActivity = $('<div class="downActivity"></div>');
 var $o_down = $('<div class="down"></div>');
 var $o_trackActivity = $('<div class="trackActivity"></div>');
 var $o_dragActivity = $('<div class="dragActivity"></div>');
 var $o_drag = $('<div class="drag"></div>');

 // Pokazanie elementów składowych:
 $o_upActivity.appendTo($o_self);
 $o_up.appendTo($o_upActivity);
 $o_trackActivity.appendTo($o_self);
 $o_dragActivity.appendTo($o_trackActivity);
 $o_drag.appendTo($o_dragActivity);
 $o_downActivity.appendTo($o_self);
 $o_down.appendTo($o_downActivity);

 // Określenie wysokości ścieżki suwaka:
 $o_trackActivity
  .css({marginTop: 1 + 'px', marginBottom: 1 + 'px'})
  .height(parseInt($o_self.css('height')) - 14 - 2);

 // Wartości wykorzystywane po najechaniu na scrolla:
 var i_scrollbarDefaultWidth = parseInt($o_drag.css('width'));

 // Aktywowanie akcji scrolla:
 if (!$o_self.hasClass('inactive')) {

  // Pokazanie suwaka:
  $o_self.css({opacity: 1.0});

  // Powiększanie scrolla po najechaniu:
  if (settings.expandScroll) {
   $o_self.bind('mouseout', function() { scrollOut() });
   $o_self.add($o_trackActivity).add($o_upActivity).add($o_downActivity).add($o_up).add($o_down).add($o_dragActivity).add($o_drag).bind('mouseover', function() { scrollOver() });
  }

  // Określenie wysokości suwaka:
  $o_drag.height($o_contentMask.height() * parseInt($o_trackActivity.css('height')) / $o_scrollContent.height());

  // Wartości wykorzystywane przez metode 'scroll':
  var i_trackBottom = parseInt($o_trackActivity.css('height')) - parseInt($o_drag.css('height'));
  var i_ratio = $o_scrollContent.height() / parseInt($o_trackActivity.css('height'));

  // Przypisanie zdarzeń elementom scrolla:
  $o_upActivity
   .bind('mousedown', function(e) { up(); return false; })
   .bind('mouseup', function(e) { stopDrag(); stop(); return false; })
   .bind('mouseout', function(e) { scrollOut(); stop(); });

  $o_downActivity
   .bind('mousedown', function(e) { down(); return false; })
   .bind('mouseup', function(e) { stopDrag(); stop(); return false; })
   .bind('mouseout', function(e) { scrollOut(); stop(); });

  $o_dragActivity
   .bind('mousedown', function(e) { i_mousePositionY = e.pageY; b_dragged = true; return false; })
   .bind('dragstart', function(e) { return false; });

  $o_self
   .bind('mousewheel', function(e) { scrollWheel(e); return false; })
   .bind('DOMMouseScroll', function(e) { scrollWheel(e); return false; });

  $(document)
   .bind('mousemove', function(e) { drag(e.pageY); })
   .bind('mouseup', function(e) { stopDrag(); });

  $o_contentMask
   .bind('mousewheel', function(e) { scrollWheel(e); return false; })
   .bind('DOMMouseScroll', function(e) { scrollWheel(e); return false; });

 } else {

  // Wygaszenie suwaka:
  $o_self.css({opacity: 0.3});

  // Określenie wysokości suwaka:
  $o_drag.height(parseInt($o_trackActivity.css('height')));

 }

 // Metody:
 function scrollOver() {
  b_hover = true;
  $o_drag.add($o_up).add($o_down)
   .css({width: settings.hoverWidth + 'px'});
  $o_self.addClass('hover');
 }

 function scrollOut() {
  b_hover = false;
  setTimeout(function() {
   if (b_hover) return;
   $o_drag.add($o_up).add($o_down)
    .css({width: i_scrollbarDefaultWidth + 'px'});
   $o_self.removeClass('hover');
  }, 250);
 }

 function scroll(i_step) {
  // Jeśli prędkość zerowa to nie przesuwa:
  if (i_step == 0) {
   stop();
   return;
  }

  var i_newTop = $o_dragActivity.position().top + i_step;

  // Jeśli w góre:
  if (i_step < 0) {
   if (i_newTop < 0) {
    i_newTop = 0;
    if (!b_dragged) stop();
   }
  } else if (i_step > 0) {
   if (i_newTop > i_trackBottom) {
    i_newTop = i_trackBottom;
    if (!b_dragged) stop();
   }
  }

  $o_dragActivity.css({top: i_newTop + 'px'});
  $o_scrollContent.css({top: -Math.round(i_newTop * i_ratio) + 'px'});
 }

 function stop() {
  clearInterval(h_interval);
 }

 function stopDrag() {
  b_dragged = false;
 }

 function up() {
  h_interval = setInterval(function() { scroll(-settings.step); }, settings.speed);
 }

 function down() {
  h_interval = setInterval(function() { scroll(settings.step); }, settings.speed);
 }

 function drag(i_newPosition) {
  if (b_dragged) {
   i_positionDiff = i_mousePositionY - i_newPosition;
   i_mousePositionY = i_newPosition;
   scroll(-i_positionDiff);
  }
 }

 function scrollWheel(e) {
  var i_step = 0;
  if (e.detail) i_step = (e.detail > 0) ? settings.speed : -settings.speed;
  else if (e.wheelDelta) i_step = (e.wheelDelta > 0) ? -settings.speed : settings.speed;
  scroll(i_step);
 }

};

jQuery.fn.extend({
 unScrollbar: function(contentMask, scrollContent) {
  return this.each(function() {
   new jQuery.UnScrollbar(this, contentMask, scrollContent);
  });
 }
});

jQuery.UnScrollbar = function(element, contentMask, scrollContent) {
 $(element)
  .empty()
  .unbind('mouseover')
  .unbind('mouseout')
  .unbind('mousewheel')
  .unbind('DOMMouseScroll');

 $(document)
  .unbind('mousemove')
  .unbind('mouseup');

 $(contentMask)
  .unbind('mousewheel')
  .unbind('DOMMouseScroll');
};