3 * Pure JavaScript plotting plugin using jQuery
8 * Copyright (c) 2009-2013 Chris Leonello
9 * jqPlot is currently available for use in all personal or commercial projects
10 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
11 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
12 * choose the license that best suits your project and use it accordingly.
14 * Although not required, the author would appreciate an email letting him
15 * know of any substantial use of jqPlot. You can reach the author at:
16 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
18 * If you are feeling kind and generous, consider supporting the project by
19 * making a donation at: http://www.jqplot.com/donate.php .
21 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
25 * http://hexmen.com/blog/2007/03/printf-sprintf/
26 * http://hexmen.com/js/sprintf.js
27 * The author (Ash Searle) has placed this code in the public domain:
28 * "This code is unrestricted: you are free to use it however you like."
33 // class: $.jqplot.CanvasOverlay
34 $.jqplot.CanvasOverlay = function(opts){
35 var options = opts || {};
37 show: $.jqplot.config.enablePlugins,
42 this.objectNames = [];
44 this.markerRenderer = new $.jqplot.MarkerRenderer({style:'line'});
45 this.markerRenderer.init();
46 this.highlightObjectIndex = null;
47 if (options.objects) {
48 var objs = options.objects,
50 for (var i=0; i<objs.length; i++) {
57 case 'horizontalLine':
58 this.addHorizontalLine(obj[n]);
60 case 'dashedHorizontalLine':
61 this.addDashedHorizontalLine(obj[n]);
64 this.addVerticalLine(obj[n]);
66 case 'dashedVerticalLine':
67 this.addDashedVerticalLine(obj[n]);
70 this.addRectangle(obj[n]);
78 $.extend(true, this.options, options);
81 // called with scope of a plot object
82 $.jqplot.CanvasOverlay.postPlotInit = function (target, data, opts) {
83 var options = opts || {};
84 // add a canvasOverlay attribute to the plot
85 this.plugins.canvasOverlay = new $.jqplot.CanvasOverlay(options.canvasOverlay);
92 this.gridStart = null;
94 this.tooltipWidthFactor = 0;
97 // Optional name for the overlay object.
98 // Can be later used to retrieve the object by name.
101 // true to show (draw), false to not draw.
104 // Width of the line.
107 // Type of ending placed on the line ['round', 'butt', 'square']
113 // whether or not to draw a shadow on the line
116 // Shadow angle in degrees
118 // prop: shadowOffset
119 // Shadow offset from line in pixels
122 // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
125 // Alpha channel transparency of shadow. 0 = transparent.
128 // X axis to use for positioning/scaling the line.
131 // Y axis to use for positioning/scaling the line.
134 // Show a tooltip with data point values.
136 // prop: showTooltipPrecision
137 // Controls how close to line cursor must be to show tooltip.
138 // Higher number = closer to line, lower number = farther from line.
139 // 1.0 = cursor must be over line.
140 showTooltipPrecision: 0.6,
141 // prop: tooltipLocation
142 // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
143 tooltipLocation: 'nw',
145 // true = fade in/out tooltip, flase = show/hide tooltip
147 // prop: tooltipFadeSpeed
148 // 'slow', 'def', 'fast', or number of milliseconds.
149 tooltipFadeSpeed: "fast",
150 // prop: tooltipOffset
151 // Pixel offset of tooltip from the highlight.
153 // prop: tooltipFormatString
154 // Format string passed the x and y values of the cursor on the line.
155 // e.g., 'Dogs: %.2f, Cats: %d'.
156 tooltipFormatString: '%d, %d'
161 function Rectangle(options) {
163 this.type = 'rectangle';
166 // x value for the start of the line, null to scale to axis min.
169 // x value for the end of the line, null to scale to axis max.
172 // offset ends of the line inside the grid. Number
173 xOffset: '6px', // number or string. Number interpreted as units, string as pixels.
179 yOffset: '6px', // number or string. Number interpreted as units, string as pixels.
183 $.extend(true, this.options, opts, options);
185 if (this.options.showTooltipPrecision < 0.01) {
186 this.options.showTooltipPrecision = 0.01;
190 Rectangle.prototype = new LineBase();
191 Rectangle.prototype.constructor = Rectangle;
198 function Line(options) {
203 // [x, y] coordinates for the start of the line.
206 // [x, y] coordinates for the end of the line.
209 $.extend(true, this.options, opts, options);
211 if (this.options.showTooltipPrecision < 0.01) {
212 this.options.showTooltipPrecision = 0.01;
216 Line.prototype = new LineBase();
217 Line.prototype.constructor = Line;
221 * Class: HorizontalLine
222 * A straight horizontal line.
224 function HorizontalLine(options) {
226 this.type = 'horizontalLine';
229 // y value to position the line
232 // x value for the start of the line, null to scale to axis min.
235 // x value for the end of the line, null to scale to axis max.
238 // offset ends of the line inside the grid. Number
239 xOffset: '6px', // number or string. Number interpreted as units, string as pixels.
243 $.extend(true, this.options, opts, options);
245 if (this.options.showTooltipPrecision < 0.01) {
246 this.options.showTooltipPrecision = 0.01;
250 HorizontalLine.prototype = new LineBase();
251 HorizontalLine.prototype.constructor = HorizontalLine;
255 * Class: DashedHorizontalLine
256 * A straight dashed horizontal line.
258 function DashedHorizontalLine(options) {
260 this.type = 'dashedHorizontalLine';
265 xOffset: '6px', // number or string. Number interpreted as units, string as pixels.
269 // Array of line, space settings in pixels.
270 // Default is 8 pixel of line, 8 pixel of space.
271 // Note, limit to a 2 element array b/c of bug with higher order arrays.
274 $.extend(true, this.options, opts, options);
276 if (this.options.showTooltipPrecision < 0.01) {
277 this.options.showTooltipPrecision = 0.01;
281 DashedHorizontalLine.prototype = new LineBase();
282 DashedHorizontalLine.prototype.constructor = DashedHorizontalLine;
286 * Class: VerticalLine
287 * A straight vertical line.
289 function VerticalLine(options) {
291 this.type = 'verticalLine';
296 yOffset: '6px', // number or string. Number interpreted as units, string as pixels.
300 $.extend(true, this.options, opts, options);
302 if (this.options.showTooltipPrecision < 0.01) {
303 this.options.showTooltipPrecision = 0.01;
307 VerticalLine.prototype = new LineBase();
308 VerticalLine.prototype.constructor = VerticalLine;
312 * Class: DashedVerticalLine
313 * A straight dashed vertical line.
315 function DashedVerticalLine(options) {
317 this.type = 'dashedVerticalLine';
324 yOffset: '6px', // number or string. Number interpreted as units, string as pixels.
328 // Array of line, space settings in pixels.
329 // Default is 8 pixel of line, 8 pixel of space.
330 // Note, limit to a 2 element array b/c of bug with higher order arrays.
333 $.extend(true, this.options, opts, options);
335 if (this.options.showTooltipPrecision < 0.01) {
336 this.options.showTooltipPrecision = 0.01;
340 DashedVerticalLine.prototype = new LineBase();
341 DashedVerticalLine.prototype.constructor = DashedVerticalLine;
343 $.jqplot.CanvasOverlay.prototype.addLine = function(opts) {
344 var line = new Line(opts);
345 line.uid = objCounter++;
346 this.objects.push(line);
347 this.objectNames.push(line.options.name);
350 $.jqplot.CanvasOverlay.prototype.addHorizontalLine = function(opts) {
351 var line = new HorizontalLine(opts);
352 line.uid = objCounter++;
353 this.objects.push(line);
354 this.objectNames.push(line.options.name);
357 $.jqplot.CanvasOverlay.prototype.addDashedHorizontalLine = function(opts) {
358 var line = new DashedHorizontalLine(opts);
359 line.uid = objCounter++;
360 this.objects.push(line);
361 this.objectNames.push(line.options.name);
364 $.jqplot.CanvasOverlay.prototype.addVerticalLine = function(opts) {
365 var line = new VerticalLine(opts);
366 line.uid = objCounter++;
367 this.objects.push(line);
368 this.objectNames.push(line.options.name);
371 $.jqplot.CanvasOverlay.prototype.addDashedVerticalLine = function(opts) {
372 var line = new DashedVerticalLine(opts);
373 line.uid = objCounter++;
374 this.objects.push(line);
375 this.objectNames.push(line.options.name);
378 $.jqplot.CanvasOverlay.prototype.addRectangle = function(opts) {
379 var line = new Rectangle(opts);
380 line.uid = objCounter++;
381 this.objects.push(line);
382 this.objectNames.push(line.options.name);
385 $.jqplot.CanvasOverlay.prototype.removeObject = function(idx) {
386 // check if integer, remove by index
387 if ($.type(idx) == 'number') {
388 this.objects.splice(idx, 1);
389 this.objectNames.splice(idx, 1);
391 // if string, remove by name
393 var id = $.inArray(idx, this.objectNames);
395 this.objects.splice(id, 1);
396 this.objectNames.splice(id, 1);
401 $.jqplot.CanvasOverlay.prototype.getObject = function(idx) {
402 // check if integer, remove by index
403 if ($.type(idx) == 'number') {
404 return this.objects[idx];
406 // if string, remove by name
408 var id = $.inArray(idx, this.objectNames);
410 return this.objects[id];
415 // Set get as alias for getObject.
416 $.jqplot.CanvasOverlay.prototype.get = $.jqplot.CanvasOverlay.prototype.getObject;
418 $.jqplot.CanvasOverlay.prototype.clear = function(plot) {
419 this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight());
422 $.jqplot.CanvasOverlay.prototype.draw = function(plot) {
425 mr = this.markerRenderer,
428 if (this.options.show) {
429 this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight());
430 for (var k=0; k<objs.length; k++) {
432 var opts = $.extend(true, {}, obj.options);
433 if (obj.options.show) {
434 // style and shadow properties should be set before
435 // every draw of marker renderer.
436 mr.shadow = obj.options.shadow;
437 obj.tooltipWidthFactor = obj.options.lineWidth / obj.options.showTooltipPrecision;
440 // style and shadow properties should be set before
441 // every draw of marker renderer.
443 opts.closePath = false;
444 start = [plot.axes[obj.options.xaxis].series_u2p(obj.options.start[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.start[1])];
445 stop = [plot.axes[obj.options.xaxis].series_u2p(obj.options.stop[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.stop[1])];
446 obj.gridStart = start;
448 mr.draw(start, stop, this.canvas._ctx, opts);
450 case 'horizontalLine':
452 // style and shadow properties should be set before
453 // every draw of marker renderer.
454 if (obj.options.y != null) {
456 opts.closePath = false;
457 var xaxis = plot.axes[obj.options.xaxis],
460 y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y),
461 xminoff = obj.options.xminOffset || obj.options.xOffset,
462 xmaxoff = obj.options.xmaxOffset || obj.options.xOffset;
463 if (obj.options.xmin != null) {
464 xstart = xaxis.series_u2p(obj.options.xmin);
466 else if (xminoff != null) {
467 if ($.type(xminoff) == "number") {
468 xstart = xaxis.series_u2p(xaxis.min + xminoff);
470 else if ($.type(xminoff) == "string") {
471 xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff);
474 if (obj.options.xmax != null) {
475 xstop = xaxis.series_u2p(obj.options.xmax);
477 else if (xmaxoff != null) {
478 if ($.type(xmaxoff) == "number") {
479 xstop = xaxis.series_u2p(xaxis.max - xmaxoff);
481 else if ($.type(xmaxoff) == "string") {
482 xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff);
485 if (xstop != null && xstart != null) {
486 obj.gridStart = [xstart, y];
487 obj.gridStop = [xstop, y];
488 mr.draw([xstart, y], [xstop, y], this.canvas._ctx, opts);
493 case 'dashedHorizontalLine':
495 var dashPat = obj.options.dashPattern;
497 for (var i=0; i<dashPat.length; i++) {
498 dashPatLen += dashPat[i];
501 // style and shadow properties should be set before
502 // every draw of marker renderer.
503 if (obj.options.y != null) {
505 opts.closePath = false;
506 var xaxis = plot.axes[obj.options.xaxis],
509 y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y),
510 xminoff = obj.options.xminOffset || obj.options.xOffset,
511 xmaxoff = obj.options.xmaxOffset || obj.options.xOffset;
512 if (obj.options.xmin != null) {
513 xstart = xaxis.series_u2p(obj.options.xmin);
515 else if (xminoff != null) {
516 if ($.type(xminoff) == "number") {
517 xstart = xaxis.series_u2p(xaxis.min + xminoff);
519 else if ($.type(xminoff) == "string") {
520 xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff);
523 if (obj.options.xmax != null) {
524 xstop = xaxis.series_u2p(obj.options.xmax);
526 else if (xmaxoff != null) {
527 if ($.type(xmaxoff) == "number") {
528 xstop = xaxis.series_u2p(xaxis.max - xmaxoff);
530 else if ($.type(xmaxoff) == "string") {
531 xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff);
534 if (xstop != null && xstart != null) {
535 obj.gridStart = [xstart, y];
536 obj.gridStop = [xstop, y];
537 var numDash = Math.ceil((xstop - xstart)/dashPatLen);
539 for (var i=0; i<numDash; i++) {
540 for (var j=0; j<dashPat.length; j+=2) {
542 mr.draw([b, y], [e, y], this.canvas._ctx, opts);
544 if (j < dashPat.length-1) {
555 // style and shadow properties should be set before
556 // every draw of marker renderer.
557 if (obj.options.x != null) {
559 opts.closePath = false;
560 var yaxis = plot.axes[obj.options.yaxis],
563 x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x),
564 yminoff = obj.options.yminOffset || obj.options.yOffset,
565 ymaxoff = obj.options.ymaxOffset || obj.options.yOffset;
566 if (obj.options.ymin != null) {
567 ystart = yaxis.series_u2p(obj.options.ymin);
569 else if (yminoff != null) {
570 if ($.type(yminoff) == "number") {
571 ystart = yaxis.series_u2p(yaxis.min - yminoff);
573 else if ($.type(yminoff) == "string") {
574 ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff);
577 if (obj.options.ymax != null) {
578 ystop = yaxis.series_u2p(obj.options.ymax);
580 else if (ymaxoff != null) {
581 if ($.type(ymaxoff) == "number") {
582 ystop = yaxis.series_u2p(yaxis.max + ymaxoff);
584 else if ($.type(ymaxoff) == "string") {
585 ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff);
588 if (ystop != null && ystart != null) {
589 obj.gridStart = [x, ystart];
590 obj.gridStop = [x, ystop];
591 mr.draw([x, ystart], [x, ystop], this.canvas._ctx, opts);
596 case 'dashedVerticalLine':
598 var dashPat = obj.options.dashPattern;
600 for (var i=0; i<dashPat.length; i++) {
601 dashPatLen += dashPat[i];
604 // style and shadow properties should be set before
605 // every draw of marker renderer.
606 if (obj.options.x != null) {
608 opts.closePath = false;
609 var yaxis = plot.axes[obj.options.yaxis],
612 x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x),
613 yminoff = obj.options.yminOffset || obj.options.yOffset,
614 ymaxoff = obj.options.ymaxOffset || obj.options.yOffset;
615 if (obj.options.ymin != null) {
616 ystart = yaxis.series_u2p(obj.options.ymin);
618 else if (yminoff != null) {
619 if ($.type(yminoff) == "number") {
620 ystart = yaxis.series_u2p(yaxis.min - yminoff);
622 else if ($.type(yminoff) == "string") {
623 ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff);
626 if (obj.options.ymax != null) {
627 ystop = yaxis.series_u2p(obj.options.ymax);
629 else if (ymaxoff != null) {
630 if ($.type(ymaxoff) == "number") {
631 ystop = yaxis.series_u2p(yaxis.max + ymaxoff);
633 else if ($.type(ymaxoff) == "string") {
634 ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff);
639 if (ystop != null && ystart != null) {
640 obj.gridStart = [x, ystart];
641 obj.gridStop = [x, ystop];
642 var numDash = Math.ceil((ystart - ystop)/dashPatLen);
643 var firstDashAdjust = ((numDash * dashPatLen) - (ystart - ystop))/2.0;
644 var b=ystart, e, bs, es;
645 for (var i=0; i<numDash; i++) {
646 for (var j=0; j<dashPat.length; j+=2) {
656 // es += firstDashAdjust;
658 mr.draw([x, b], [x, e], this.canvas._ctx, opts);
660 if (j < dashPat.length-1) {
670 // style and shadow properties should be set before
671 // every draw of marker renderer.
673 opts.closePath = true;
675 var xaxis = plot.axes[obj.options.xaxis],
678 y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y),
679 xminoff = obj.options.xminOffset || obj.options.xOffset,
680 xmaxoff = obj.options.xmaxOffset || obj.options.xOffset;
681 if (obj.options.xmin != null) {
682 xstart = xaxis.series_u2p(obj.options.xmin);
684 else if (xminoff != null) {
685 if ($.type(xminoff) == "number") {
686 xstart = xaxis.series_u2p(xaxis.min + xminoff);
688 else if ($.type(xminoff) == "string") {
689 xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff);
692 if (obj.options.xmax != null) {
693 xstop = xaxis.series_u2p(obj.options.xmax);
695 else if (xmaxoff != null) {
696 if ($.type(xmaxoff) == "number") {
697 xstop = xaxis.series_u2p(xaxis.max - xmaxoff);
699 else if ($.type(xmaxoff) == "string") {
700 xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff);
704 var yaxis = plot.axes[obj.options.yaxis],
707 x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x),
708 yminoff = obj.options.yminOffset || obj.options.yOffset,
709 ymaxoff = obj.options.ymaxOffset || obj.options.yOffset;
710 if (obj.options.ymin != null) {
711 ystart = yaxis.series_u2p(obj.options.ymin);
713 else if (yminoff != null) {
714 if ($.type(yminoff) == "number") {
715 ystart = yaxis.series_u2p(yaxis.min - yminoff);
717 else if ($.type(yminoff) == "string") {
718 ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff);
721 if (obj.options.ymax != null) {
722 ystop = yaxis.series_u2p(obj.options.ymax);
724 else if (ymaxoff != null) {
725 if ($.type(ymaxoff) == "number") {
726 ystop = yaxis.series_u2p(yaxis.max + ymaxoff);
728 else if ($.type(ymaxoff) == "string") {
729 ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff);
734 if (xstop != null && xstart != null && ystop != null && ystart != null) {
735 obj.gridStart = [xstart, ystart];
736 obj.gridStop = [xstop, ystop];
738 this.canvas._ctx.fillStyle = obj.options.color;
739 this.canvas._ctx.fillRect(xstart, ystart, xstop - xstart, ystop - ystart);
751 // called within context of plot
752 // create a canvas which we can draw on.
753 // insert it before the eventCanvas, so eventCanvas will still capture events.
754 $.jqplot.CanvasOverlay.postPlotDraw = function() {
755 var co = this.plugins.canvasOverlay;
756 // Memory Leaks patch
757 if (co && co.highlightCanvas) {
758 co.highlightCanvas.resetCanvas();
759 co.highlightCanvas = null;
761 co.canvas = new $.jqplot.GenericCanvas();
763 this.eventCanvas._elem.before(co.canvas.createElement(this._gridPadding, 'jqplot-overlayCanvas-canvas', this._plotDimensions, this));
764 co.canvas.setContext();
769 var elem = document.createElement('div');
770 co._tooltipElem = $(elem);
772 co._tooltipElem.addClass('jqplot-canvasOverlay-tooltip');
773 co._tooltipElem.css({position:'absolute', display:'none'});
775 this.eventCanvas._elem.before(co._tooltipElem);
776 this.eventCanvas._elem.bind('mouseleave', { elem: co._tooltipElem }, function (ev) { ev.data.elem.hide(); });
782 function showTooltip(plot, obj, gridpos, datapos) {
783 var co = plot.plugins.canvasOverlay;
784 var elem = co._tooltipElem;
786 var opts = obj.options, x, y;
788 elem.html($.jqplot.sprintf(opts.tooltipFormatString, datapos[0], datapos[1]));
790 switch (opts.tooltipLocation) {
792 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset;
793 y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true);
796 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true)/2;
797 y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true);
800 x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset;
801 y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true);
804 x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset;
805 y = gridpos[1] + plot._gridPadding.top - elem.outerHeight(true)/2;
808 x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset;
809 y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset;
812 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true)/2;
813 y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset;
816 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset;
817 y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset;
820 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset;
821 y = gridpos[1] + plot._gridPadding.top - elem.outerHeight(true)/2;
823 default: // same as 'nw'
824 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset;
825 y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true);
831 if (opts.fadeTooltip) {
832 // Fix for stacked up animations. Thnanks Trevor!
833 elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed);
842 function isNearLine(point, lstart, lstop, width) {
843 // r is point to test, p and q are end points.
846 var px = Math.round(lstop[0]);
847 var py = Math.round(lstop[1]);
848 var qx = Math.round(lstart[0]);
849 var qy = Math.round(lstart[1]);
851 var l = Math.sqrt(Math.pow(px-qx, 2) + Math.pow(py-qy, 2));
853 // scale error term by length of line.
855 var res = Math.abs((qx-px) * (ry-py) - (qy-py) * (rx-px));
856 var ret = (res < eps) ? true : false;
860 function isNearRectangle(point, lstart, lstop, width) {
861 // r is point to test, p and q are end points.
864 var px = Math.round(lstop[0]);
865 var py = Math.round(lstop[1]);
866 var qx = Math.round(lstart[0]);
867 var qy = Math.round(lstart[1]);
870 if (px > qx) { temp = px; px = qx; qx = temp; }
871 if (py > qy) { temp = py; py = qy; qy = temp; }
873 var ret = (rx >= px && rx <= qx && ry >= py && ry <= qy);
879 function handleMove(ev, gridpos, datapos, neighbor, plot) {
880 var co = plot.plugins.canvasOverlay;
881 var objs = co.objects;
883 var obj, haveHighlight=false;
885 for (var i=0; i<l; i++) {
887 if (obj.options.showTooltip) {
889 if (obj.type === 'rectangle') {
890 n = isNearRectangle([gridpos.x, gridpos.y], obj.gridStart, obj.gridStop, obj.tooltipWidthFactor);
892 n = isNearLine([gridpos.x, gridpos.y], obj.gridStart, obj.gridStop, obj.tooltipWidthFactor);
894 datapos = [plot.axes[obj.options.xaxis].series_p2u(gridpos.x), plot.axes[obj.options.yaxis].series_p2u(gridpos.y)];
897 // near line, no highlighting
898 // near line, highliting on this line
899 // near line, highlighting another line
900 // not near any line, highlighting
901 // not near any line, no highlighting
903 // near line, not currently highlighting
904 if (n && co.highlightObjectIndex == null) {
907 showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos);
910 case 'horizontalLine':
911 case 'dashedHorizontalLine':
912 showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]);
916 case 'dashedVerticalLine':
917 showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
921 showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
927 co.highlightObjectIndex = i;
928 haveHighlight = true;
932 // near line, highlighting another line.
933 else if (n && co.highlightObjectIndex !== i) {
935 elem = co._tooltipElem;
936 if (obj.fadeTooltip) {
937 elem.fadeOut(obj.tooltipFadeSpeed);
943 // turn on right tooltip.
946 showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos);
949 case 'horizontalLine':
950 case 'dashedHorizontalLine':
951 showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]);
955 case 'dashedVerticalLine':
956 showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
960 showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
967 co.highlightObjectIndex = i;
968 haveHighlight = true;
972 // near line, already highlighting this line, update
976 showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos);
979 case 'horizontalLine':
980 case 'dashedHorizontalLine':
981 showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]);
985 case 'dashedVerticalLine':
986 showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
990 showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
997 haveHighlight = true;
1003 // check if we are highlighting and not near a line, turn it off.
1004 if (!haveHighlight && co.highlightObjectIndex !== null) {
1005 elem = co._tooltipElem;
1006 obj = co.getObject(co.highlightObjectIndex);
1007 if (obj.fadeTooltip) {
1008 elem.fadeOut(obj.tooltipFadeSpeed);
1013 co.highlightObjectIndex = null;
1017 $.jqplot.postInitHooks.push($.jqplot.CanvasOverlay.postPlotInit);
1018 $.jqplot.postDrawHooks.push($.jqplot.CanvasOverlay.postPlotDraw);
1019 $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);