jQuery.fn.pannable = function() {
    /* First we need to see what the scrollbar width is on this browser. */
    document.body.style.overflow = 'hidden';
    var scrollbarwidth = document.body.clientWidth;
    document.body.style.overflow = 'scroll';
    scrollbarwidth -= document.body.clientWidth;
    if (!scrollbarwidth) {
        scrollbarwidth = document.body.offsetWidth - document.body.clientWidth;
    }
    document.body.style.overflow = '';

    /* Add an extra px of buffer on the scrollbar width. */
    scrollbarwidth += 1;

    /* Given a pannable element and an event, return true if the event is
     * over the bottom scrollbar of the element. */
    function on_scrollbar(el, e) {
        return (($(el).height() - (e.pageY - $(el).offset().top)) < scrollbarwidth);
    }

    /* When called on an HTML block element which has scrollbars, allow the
     * user to scroll around the block element by clicking and dragging on the
     * content. */
    return this.each(function() {
        movecount = 1;
        jQuery(this)
            .mousedown(function(e) {
                if(on_scrollbar(this, e)) {
                    /* We're on the scrollbar. */
                    return false;
                }
                mousestarty = e.pageY;
                mousestartx = e.pageX;
                container = $(this);
                scrollstartx = container.attr("scrollLeft");
                scrollstarty = container.attr("scrollTop");

                /* This is to keep stupid IE from trying to drag the image. */
                var nodrag = function(e) {
                    return false;
                }
                container.bind('drag', nodrag);

                var movefunc = function(e) {
                    /* When we move the mouse, compare with start position and
                     * adjust scroll offsets accordingly. */
                    container.attr("scrollLeft", scrollstartx - (e.pageX - mousestartx));
                    container.attr("scrollTop", scrollstarty - (e.pageY - mousestarty));
                };
                
                var upfunc = function(e) {
                    container.unbind('drag', nodrag);
                    $(document)
                        .unbind("mousemove", movefunc)
                        .unbind("mouseup", upfunc);
                };

                $(document)
                    .bind("mousemove", movefunc)
                    .bind("mouseup", upfunc);

                return false;
            })
            .mousemove(function(e) {
                if(!on_scrollbar(this, e)) {
                    $(this).css('cursor', 'move');
                } else {
                    $(this).css('cursor', 'auto');
                }
            });
    });
};

