var ScrollablePanel = new Class({
    // Options
    interval: 100, // ms
    distance: 10, // pixels
    // Internals
    panel: null,
    timer: null,
    content: null,
    content_inner: null,
    content_height: null,
    scroll_max: null,
    curr_pos: 0,
    initialize: function(panel) {
        var obj_ref = this; // ease scope issues
        this.panel = $(panel);
        // Find the theoretical height of the content
        this.panel.setStyle('overflow', 'scroll');
        var full_height = this.panel.scrollHeight;
        this.panel.setStyles({overflow: 'hidden', padding: '0px'});
        // Stop now if content will fit without scrolling
        if(full_height <= this.panel.clientHeight) return;
        // Store and clear our content        
        var content = this.panel.innerHTML;
        this.panel.innerHTML = '';
        // Make and add control elements
        this.panel.setStyle('position', 'relative');
        var controls = new Element('div', {'class': 'sp_controls'});
        controls.appendChild(new Element('a', {
            'href': '#', 'class' : 'sp_up', 'onclick': 'return false;'
        }));
        controls.appendChild(new Element('a', {
            'href': '#', 'class': 'sp_dn', 'onclick': 'return false;'
        }));
        controls.getElements('a').each(function(a) {
            a.addEvent('mouseover', function(e) {
                obj_ref.start($(this).hasClass('sp_up') ? 'up' : 'dn');
            });
            a.addEvent('mouseout', function(e) {
                obj_ref.stop();
            });
        });
        this.panel.appendChild(controls);
        // Make and add content container
        this.content = new Element('div', {
            'class': 'sp_content_outer'
        }).setStyles({
            overflow: 'hidden', position: 'relative'
        });
        this.content_inner = new Element('div', {
            'class': 'sp_content_inner', 'html': content
        });
        this.content.appendChild(new Element('div', {
            'class': 'sp_fade'
        }).setStyles({
            position:'absolute', bottom:'0px', width:'100%'
        }));
        this.content.appendChild(this.content_inner);
        this.panel.appendChild(this.content);
        // Set height of content outer panel
        var avail_height = this.panel.clientHeight - this.outer_height(this.content) + this.content.clientHeight;
        this.content.setStyle('height', avail_height + 'px');
        // Store height of content inner panel
        this.content_height = this.outer_height(this.content_inner);
        // Store max scroll offset to allow
        this.scroll_max = this.content_height - this.content.clientHeight + this.distance;
    },
    start: function(dir) {
        var obj_ref = this;
        if(dir == 'dn' && this.curr_pos > this.scroll_max) return;
        this.curr_pos += this.distance * (dir == 'dn' ? +1 : -1);
        if(this.curr_pos < 0) this.curr_pos = 0;
        this.content_inner.setStyle('margin-top', '-' + this.curr_pos + 'px');
        this.timer = setTimeout(function() { obj_ref.start(dir); }, this.interval);
    },
    stop: function() {
        clearTimeout(this.timer);
    },
    // Get a value for all vertical space required for an element
    outer_height: function(el) {
        var h = $(el).getSize().y;
        $A(['margin-top', 'margin-bottom', 'padding-top', 'padding-bottom', 'border-top-width', 'border-bottom-width']).each(function(k){
            h += parseInt(el.getStyle(k));
        });
        return h;
    }
});

window.addEvent('domready', function() {
    $$('.scrollable').each(function(el) {
        new ScrollablePanel(el);
    });
});
