(function($){
  $.tiny=$.tiny||{};
  
  $.tiny.scrollbar={
    options:{
      axis:'y',
      wheel:40,
      scroll:true,
      size:'auto',
      sizethumb:'auto'
    }
  };
  
  $.fn.tinyscrollbar=function(options){
    var options=$.extend({},$.tiny.scrollbar.options,options);
    this.each(function(){
      $(this).data('tsb',new Scrollbar($(this),options));
    });
    return this;
  };

  $.fn.tinyscrollbar_update=function(sScroll){
    return $(this).data('tsb').update(sScroll);
  };

  function Scrollbar(root,options){
    var oSelf=this;
    var oWrapper=root;
    var oViewport={
      obj:$('.viewport',root)
    };
    
    var oContent={
      obj:$('.overview',root)
    };
    
    var oScrollbar={
      obj:$('.scrollbar',root)
    };
    
    var oTrack={
      obj:$('.track',oScrollbar.obj)
    };
    
    var oThumb={
      obj:$('.thumb',oScrollbar.obj)
    };
    
    var sAxis=options.axis=='x',sDirection=sAxis?'left':'top',sSize=sAxis?'Width':'Height';
    var iScroll,iPosition={
      start:0,
      now:0
    },iMouse={};
  
    function initialize(){
      oSelf.update();
      setEvents();
      return oSelf;
    }
    this.update=function(sScroll){
      oViewport[options.axis]=oViewport.obj[0]['offset'+sSize];
      oContent[options.axis]=oContent.obj[0]['scroll'+sSize];
      oContent.ratio=oViewport[options.axis]/oContent[options.axis];
      oScrollbar.obj.toggleClass('disable',oContent.ratio>=1);
      oTrack[options.axis]=options.size=='auto'?oViewport[options.axis]:options.size;
      oThumb[options.axis]=Math.min(oTrack[options.axis],Math.max(0,(options.sizethumb=='auto'?(oTrack[options.axis]*oContent.ratio):options.sizethumb)));
      oScrollbar.ratio=options.sizethumb=='auto'?(oContent[options.axis]/oTrack[options.axis]):(oContent[options.axis]-oViewport[options.axis])/(oTrack[options.axis]-oThumb[options.axis]);
      iScroll=(sScroll=='relative'&&oContent.ratio<=1)?Math.min((oContent[options.axis]-oViewport[options.axis]),Math.max(0,iScroll)):0;
      iScroll=(sScroll=='bottom'&&oContent.ratio<=1)?(oContent[options.axis]-oViewport[options.axis]):isNaN(parseInt(sScroll))?iScroll:parseInt(sScroll);
      setSize();
    };
  
    function setSize(){
      oThumb.obj.css(sDirection,iScroll/oScrollbar.ratio);
      oContent.obj.css(sDirection,-iScroll);
      iMouse['start']=oThumb.obj.offset()[sDirection];
      var sCssSize=sSize.toLowerCase();
      oScrollbar.obj.css(sCssSize,oTrack[options.axis]);
      oTrack.obj.css(sCssSize,oTrack[options.axis]);
      oThumb.obj.css(sCssSize,oThumb[options.axis]);
    };
  
    function setEvents(){
      oThumb.obj.bind('mousedown',start);
      oThumb.obj[0].ontouchstart=function(oEvent){
        oEvent.preventDefault();
        oThumb.obj.unbind('mousedown');
        start(oEvent.touches[0]);
        return false;
      };
    
      oTrack.obj.bind('mouseup',drag);
      if(options.scroll&&this.addEventListener){
        oWrapper[0].addEventListener('DOMMouseScroll',wheel,false);
        oWrapper[0].addEventListener('mousewheel',wheel,false);
      }
      else if(options.scroll){
        oWrapper[0].onmousewheel=wheel;
      }
    };
  
    function start(oEvent){
      iMouse.start=sAxis?oEvent.pageX:oEvent.pageY;
      var oThumbDir=parseInt(oThumb.obj.css(sDirection));
      iPosition.start=oThumbDir=='auto'?0:oThumbDir;
      $(document).bind('mousemove',drag);
      document.ontouchmove=function(oEvent){
        $(document).unbind('mousemove');
        drag(oEvent.touches[0]);
      };
  
      $(document).bind('mouseup',end);
      oThumb.obj.bind('mouseup',end);
      oThumb.obj[0].ontouchend=document.ontouchend=function(oEvent){
        $(document).unbind('mouseup');
        oThumb.obj.unbind('mouseup');
        end(oEvent.touches[0]);
      };
  
      return false;
    };

    function wheel(oEvent){
      if(!(oContent.ratio>=1)){
        oEvent=$.event.fix(oEvent||window.event);
        var iDelta=oEvent.wheelDelta?oEvent.wheelDelta/120:-oEvent.detail/3;
        iScroll-=iDelta*options.wheel;
        iScroll=Math.min((oContent[options.axis]-oViewport[options.axis]),Math.max(0,iScroll));
        oThumb.obj.css(sDirection,iScroll/oScrollbar.ratio);
        oContent.obj.css(sDirection,-iScroll);
        oEvent.preventDefault();
      };

    };

    function end(oEvent){
      $(document).unbind('mousemove',drag);
      $(document).unbind('mouseup',end);
      oThumb.obj.unbind('mouseup',end);
      document.ontouchmove=oThumb.obj[0].ontouchend=document.ontouchend=null;
      return false;
    };

    function drag(oEvent){
      if(!(oContent.ratio>=1)){
        iPosition.now=Math.min((oTrack[options.axis]-oThumb[options.axis]),Math.max(0,(iPosition.start+((sAxis?oEvent.pageX:oEvent.pageY)-iMouse.start))));
        iScroll=iPosition.now*oScrollbar.ratio;
        oContent.obj.css(sDirection,-iScroll);
        oThumb.obj.css(sDirection,iPosition.now);;
      }
      return false;
    };

    return initialize();
  };

})(jQuery);
