    MouseHandler = createClass({

        init: function (el, options) {

            var $el = $(el);

            this.$el = $el;

            this.options = options;

            this.currentPageX = 0;

            this.currentPageY = 0;

            this.el = el;

            this.splist = [];

            this.tooltip = null;

            this.over = false;

            this.displayTooltips = !options.get('disableTooltips');

            this.highlightEnabled = !options.get('disableHighlight');

        },



        registerSparkline: function (sp) {

            this.splist.push(sp);

            if (this.over) {

                this.updateDisplay();

            }

        },



        registerCanvas: function (canvas) {

            var $canvas = $(canvas.canvas);

            this.canvas = canvas;

            this.$canvas = $canvas;

            $canvas.mouseenter($.proxy(this.mouseenter, this));

            $canvas.mouseleave($.proxy(this.mouseleave, this));

            $canvas.click($.proxy(this.mouseclick, this));

        },



        reset: function (removeTooltip) {

            this.splist = [];

            if (this.tooltip && removeTooltip) {

                this.tooltip.remove();

                this.tooltip = undefined;

            }

        },



        mouseclick: function (e) {

            var clickEvent = $.Event('sparklineClick');

            clickEvent.originalEvent = e;

            clickEvent.sparklines = this.splist;

            this.$el.trigger(clickEvent);

        },



        mouseenter: function (e) {

            $(document.body).unbind('mousemove.jqs');

            $(document.body).bind('mousemove.jqs', $.proxy(this.mousemove, this));

            this.over = true;

            this.currentPageX = e.pageX;

            this.currentPageY = e.pageY;

            this.currentEl = e.target;

            if (!this.tooltip && this.displayTooltips) {

                this.tooltip = new Tooltip(this.options);

                this.tooltip.updatePosition(e.pageX, e.pageY);

            }

            this.updateDisplay();

        },



        mouseleave: function () {

            $(document.body).unbind('mousemove.jqs');

            var splist = this.splist,

                 spcount = splist.length,

                 needsRefresh = false,

                 sp, i;

            this.over = false;

            this.currentEl = null;



            if (this.tooltip) {

                this.tooltip.remove();

                this.tooltip = null;

            }



            for (i = 0; i < spcount; i++) {

                sp = splist[i];

                if (sp.clearRegionHighlight()) {

                    needsRefresh = true;

                }

            }



            if (needsRefresh) {

                this.canvas.render();

            }

        },



        mousemove: function (e) {

            this.currentPageX = e.pageX;

            this.currentPageY = e.pageY;

            this.currentEl = e.target;

            if (this.tooltip) {

                this.tooltip.updatePosition(e.pageX, e.pageY);

            }

            this.updateDisplay();

        },



        updateDisplay: function () {

            var splist = this.splist,

                 spcount = splist.length,

                 needsRefresh = false,

                 offset = this.$canvas.offset(),

                 localX = this.currentPageX - offset.left,

                 localY = this.currentPageY - offset.top,

                 tooltiphtml, sp, i, result, changeEvent;

            if (!this.over) {

                return;

            }

            for (i = 0; i < spcount; i++) {

                sp = splist[i];

                result = sp.setRegionHighlight(this.currentEl, localX, localY);

                if (result) {

                    needsRefresh = true;

                }

            }

            if (needsRefresh) {

                changeEvent = $.Event('sparklineRegionChange');

                changeEvent.sparklines = this.splist;

                this.$el.trigger(changeEvent);

                if (this.tooltip) {

                    tooltiphtml = '';

                    for (i = 0; i < spcount; i++) {

                        sp = splist[i];

                        tooltiphtml += sp.getCurrentRegionTooltip();

                    }

                    this.tooltip.setContent(tooltiphtml);

                }

                if (!this.disableHighlight) {

                    this.canvas.render();

                }

            }

            if (result === null) {

                this.mouseleave();

            }

        }

    });





    Tooltip = createClass({

        sizeStyle: 'position: static !important;' +

            'display: block !important;' +

            'visibility: hidden !important;' +

            'float: left !important;',



        init: function (options) {

            var tooltipClassname = options.get('tooltipClassname', 'jqstooltip'),

                sizetipStyle = this.sizeStyle,

                offset;

            this.container = options.get('tooltipContainer') || document.body;

            this.tooltipOffsetX = options.get('tooltipOffsetX', 10);

            this.tooltipOffsetY = options.get('tooltipOffsetY', 12);

            // remove any previous lingering tooltip

            $('#jqssizetip').remove();

            $('#jqstooltip').remove();

            this.sizetip = $('<div/>', {

                id: 'jqssizetip',

                style: sizetipStyle,

                'class': tooltipClassname

            });

            this.tooltip = $('<div/>', {

                id: 'jqstooltip',

                'class': tooltipClassname

            }).appendTo(this.container);

            // account for the container's location

            offset = this.tooltip.offset();

            this.offsetLeft = offset.left;

            this.offsetTop = offset.top;

            this.hidden = true;

            $(window).unbind('resize.jqs scroll.jqs');

            $(window).bind('resize.jqs scroll.jqs', $.proxy(this.updateWindowDims, this));

            this.updateWindowDims();

        },



        updateWindowDims: function () {

            this.scrollTop = $(window).scrollTop();

            this.scrollLeft = $(window).scrollLeft();

            this.scrollRight = this.scrollLeft + $(window).width();

            this.updatePosition();

        },



        getSize: function (content) {

            this.sizetip.html(content).appendTo(this.container);

            this.width = this.sizetip.width() + 1;

            this.height = this.sizetip.height();

            this.sizetip.remove();

        },



        setContent: function (content) {

            if (!content) {

                this.tooltip.css('visibility', 'hidden');

                this.hidden = true;

                return;

            }

            this.getSize(content);

            this.tooltip.html(content)

                .css({

                    'width': this.width,

                    'height': this.height,

                    'visibility': 'visible'

                });

            if (this.hidden) {

                this.hidden = false;

                this.updatePosition();

            }

        },



        updatePosition: function (x, y) {

            if (x === undefined) {

                if (this.mousex === undefined) {

                    return;

                }

                x = this.mousex - this.offsetLeft;

                y = this.mousey - this.offsetTop;



            } else {

                this.mousex = x = x - this.offsetLeft;

                this.mousey = y = y - this.offsetTop;

            }

            if (!this.height || !this.width || this.hidden) {

                return;

            }



            y -= this.height + this.tooltipOffsetY;

            x += this.tooltipOffsetX;



            if (y < this.scrollTop) {

                y = this.scrollTop;

            }

            if (x < this.scrollLeft) {

                x = this.scrollLeft;

            } else if (x + this.width > this.scrollRight) {

                x = this.scrollRight - this.width;

            }



            this.tooltip.css({

                'left': x,

                'top': y

            });

        },



        remove: function () {

            this.tooltip.remove();

            this.sizetip.remove();

            this.sizetip = this.tooltip = undefined;

            $(window).unbind('resize.jqs scroll.jqs');

        }

    });



