var Scroller = Class.create({

  initialize: function(element) {
    this.element = $(element).wrap();
    this.wrapper = this.element.parentNode;
    this.has_focus = false;
    this.old_scroll_height = 0;
    this._build();
    this._register_events();
  },

  // Call anytime you think the content size has changed
  update: function() {
    //if(!this.needs_scrollbar()) this.scrollbar.hide();
    if( this.old_scroll_height == this.element.scrollHeight ) return;
    this.scrollbar.show();

    var handle_height = this.element.clientHeight *
      (this.handle_container.getHeight()) / this.element.scrollHeight;
    if(handle_height < 10) handle_height = 10;
    this.handle.setStyle({height: handle_height+'px'});

    if( this.slider ) this.slider.dispose();    
    this.slider = new Control.Slider(this.handle, this.handle_container, {
      axis: 'vertical',
      onSlide: this.set_position.bind(this),
      onChange: this.set_position.bind(this)
    });
    this.up.stopObserving('click');
    this.down.stopObserving('click');
    this.up.observe('click', (function() {this.setValue(this.value-0.1);}).bind(this.slider));
    this.down.observe('click', (function() {this.setValue(this.value+0.1);}).bind(this.slider));

    if(this.needs_scrollbar())
      this.scrollbar.show();
    else
      this.scrollbar.hide();
    this.slider.setValue(this.element.scrollTop / (this.old_scroll_height-this.element.clientHeight));
    this.old_scroll_height = this.element.scrollHeight;
  },

  // value is the % of the content height to scroll
  set_position: function(value) {
    var position = (value*(this.element.scrollHeight-this.element.clientHeight)).toFixed();
    this.element.scrollTop = position;
  },

  // Does the area need a scrollbar
  needs_scrollbar: function() {
    if(this.element.getStyle('overflow') == 'scroll') return true;

    return this.element.scrollHeight > this.element.clientHeight;
  },

  // Add the scrollbar markup
  _build: function() {
    this.wrapper.makePositioned();
    this.element.setStyle({overflow: 'hidden'});
    this.wrapper.setStyle({
      float: this.element.getStyle('float'),
      marginTop: this.element.getStyle('margin-top'),
      marginRight: this.element.getStyle('margin-right'),
      marginBottom: this.element.getStyle('margin-bottom'),
      marginLeft: this.element.getStyle('margin-left'),
      width: this.element.getWidth()+'px',
      height: this.element.getHeight()+'px'
    });
    this.element.setStyle({margin: '0em'});

    this.scrollbar = new Element('div');
    this.scrollbar.addClassName('scroll-track');
    this.scrollbar.setStyle({
      position: 'absolute',
      top: '0em',
      right: '0em',
      height: this.element.getHeight()+'px',
      zIndex: 1000,
      visibility: 'hidden'
    });
    this.wrapper.insert(this.scrollbar);

    this.up = new Element('div');
    this.up.addClassName('scroll-up');
    this.up.setStyle({
      position: 'absolute',
      top: '0em',
      right: '0em'
    });
    this.scrollbar.insert(this.up);

    this.down = new Element('div');
    this.down.addClassName('scroll-down');
    this.down.setStyle({
      position: 'absolute',
      bottom: '0em',
      right: '0em'
    });
    this.scrollbar.insert(this.down);

    // We need a handle container to slide along so it doesnt
    // overlap the up and down buttons.
    this.handle_container = new Element('div');
    this.handle_container.addClassName('handle-container');
    this.handle_container.setStyle({
      top: this.up.getHeight()+'px',
      right: '0em',
      position: 'absolute',
      height: (parseInt(this.scrollbar.getStyle('height')) -
        this.up.getHeight() - this.down.getHeight()) + 'px'
    });
    this.scrollbar.insert(this.handle_container);

    this.handle = new Element('div');
    this.handle.addClassName('scroll-handle');
    this.handle.setStyle({
      right: '0em',
      top: '0em',
      position: 'absolute'
    });
    this.handle_container.insert(this.handle);

    this.scrollbar.setStyle({
      display: 'none',
      visibility: 'visible',
      width: ([
        this.scrollbar.getWidth(),
        this.up.getWidth(),
        this.down.getWidth(),
        this.handle.getWidth(),
      ].max() + 'px')
    });
  },

  _register_events: function() {
    // Handle keyboard events
    document.observe('keypress', (function(event) {
      if(!this.has_focus) return false;
      var amount = 0;
      switch (event.keyCode) {
        case Event.KEY_DOWN:
          amount = 0.1;
          break;
        case Event.KEY_UP:
          amount = -0.1;
          break;
        case Event.KEY_PAGEDOWN:
          amount = this.element.clientHeight / this.element.scrollHeight;
          break;
        case Event.KEY_PAGEUP:
          amount = -this.element.clientHeight / this.element.scrollHeight;
      }
      this.slider.setValue(this.slider.value + amount);
    }).bind(this));

    // Handle scroll wheel events
    var wheel = (function(event) {
      this.slider.setValue(this.slider.value - Event.wheel(event)/10);
    }).bind(this);
    this.wrapper.observe("mousewheel", wheel);
    this.wrapper.observe("DOMMouseScroll", wheel); // Firefox

    // Make sure we always know where the pointer is
    document.observe("mouseover", (function(event) {
      Position.cumulativeOffset(this.wrapper);
      this.has_focus = Position.within(this.wrapper,
        event.pointerX(), event.pointerY());
    }).bind(this));

    document.body.focus();

    setInterval((function() {this.update()}).bind(this), 250);
  }
});

// Borrowed from http://www.ogonek.net/mousewheel/demo.html
Object.extend(Event, {
	wheel:function (event){
		var delta = 0;
		if (!event) event = window.event;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120;
			if (window.opera) delta = -delta;
		} else if (event.detail) { delta = -event.detail/3;	}
		return Math.round(delta); //Safari Round
	}
});
Event.DOMEvents.push('mousewheel', 'DOMMouseScroll');

// Automatically styleize all divs with class "stylized-scroller"
Event.observe(document, 'contentloaded', function() {
  $$('.stylized-scroller').each(function(e) {new Scroller(e)});
});

