(function($) {
  $.fn.carousel = function (options) {          
    var opts = $.extend({}, $.fn.carousel.defaults, options);
    return this.each(function() {
      var $viewport = $(this);
      var $viewport_offset = $viewport[0].offsetTop
      var $list = $viewport.children("ul:only-child");
      var $viewport_height = $viewport.height();
      var $list_height = $list.height();
      var $last = 0;
      var $speed_coefficient = 0;
      var $zero_zone = 100;
      var $length = $viewport_height - $viewport_offset;
      var $max = $length - $list_height;

      $list.css({top: '0px'});

      var last = function(coeff) {
        var top = parseInt($list.css('top'));
        return (coeff < 0 ? top : $max - top) / $max;
      }

      speed = function(coeff) {
        var acot = Math.PI/2 - Math.atan(coeff > 0 ? 2*(4*coeff/($length - $zero_zone) - 1) : -2*(4*coeff/($length - $zero_zone) + 1));
        var speed = acot/(Math.PI + 1) + 0.3;
        return speed * $last * Math.abs($max) * 5;
      }

      var speed_coefficient = function(pos) {
        var coeff = (pos - $viewport_offset) - Math.round(($length) / 2);
        return Math.abs(coeff) < $zero_zone/2 ? 0 : (coeff < 0 ? coeff + $zero_zone/2 : coeff - $zero_zone/2);
      }
      
      var animate = function(coeff) {
        var top = parseInt($list.css('top'));
        if ((coeff < 0 && top != 0) || (coeff > 0 && top != $max)) {
          $list.stop().animate({top: coeff < 0 ? 0 : $max}, speed(coeff));
        }
      }

      $viewport.mousemove(function(e) {
        if ($max >= 0) { return }
        var coeff = speed_coefficient(e.clientY);
        if (coeff == 0) {
          $list.stop()
        }
        if (($speed_coefficient >= 0 && coeff < 0) || ($speed_coefficient <= 0 && coeff > 0)) {
          $last = last(coeff);
          animate(coeff);
        }
        if (($speed_coefficient >= 0 && coeff > 0) || ($speed_coefficient <= 0 && coeff < 0)) {
          $list.speed(speed(coeff));
        }
        $speed_coefficient = coeff
      });

      $viewport.mouseenter(function(e) {
        if ($max >= 0) { return }
        $speed_coefficient = speed_coefficient(e.clientY);
        $last = last($speed_coefficient);
        animate($speed_coefficient);
      });

      $viewport.mouseleave(function(e) {
        $list.stop()
      });
    });
  };
  $.fn.carousel.defaults = {
  };
})(jQuery);
