OSDN Git Service

初回コミット(v2.6.17.1)
[magic3/magic3.git] / scripts / jquery / jqplot1.0 / plugins / jqplot.canvasOverlay.js
1 /**
2  * jqPlot
3  * Pure JavaScript plotting plugin using jQuery
4  *
5  * Version: 1.0.4
6  * Revision: 1120
7  *
8  * Copyright (c) 2009-2012 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. 
13  *
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 .
17  *
18  * If you are feeling kind and generous, consider supporting the project by
19  * making a donation at: http://www.jqplot.com/donate.php .
20  *
21  * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
22  *
23  *     version 2007.04.27
24  *     author 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."
29  * 
30  */
31 (function($) {
32     var objCounter = 0;
33     // class: $.jqplot.CanvasOverlay
34     $.jqplot.CanvasOverlay = function(opts){
35         var options = opts || {};
36         this.options = {
37             show: $.jqplot.config.enablePlugins,
38             deferDraw: false
39         };
40         // prop: objects
41         this.objects = [];
42         this.objectNames = [];
43         this.canvas = null;
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,
49                 obj;
50             for (var i=0; i<objs.length; i++) {
51                 obj = objs[i];
52                 for (var n in obj) {
53                     switch (n) {
54                         case 'line':
55                             this.addLine(obj[n]);
56                             break;
57                         case 'horizontalLine':
58                             this.addHorizontalLine(obj[n]);
59                             break;
60                         case 'dashedHorizontalLine':
61                             this.addDashedHorizontalLine(obj[n]);
62                             break;
63                         case 'verticalLine':
64                             this.addVerticalLine(obj[n]);
65                             break;
66                         case 'dashedVerticalLine':
67                             this.addDashedVerticalLine(obj[n]);
68                             break;
69                         default:
70                             break;
71                     }
72                 }   
73             }
74         }
75         $.extend(true, this.options, options);
76     };
77     
78     // called with scope of a plot object
79     $.jqplot.CanvasOverlay.postPlotInit = function (target, data, opts) {
80         var options = opts || {};
81         // add a canvasOverlay attribute to the plot
82         this.plugins.canvasOverlay = new $.jqplot.CanvasOverlay(options.canvasOverlay);     
83     };
84
85
86     function LineBase() {
87         this.uid = null;
88         this.type = null;
89         this.gridStart = null;
90         this.gridStop = null;
91         this.tooltipWidthFactor = 0;
92         this.options = {           
93             // prop: name
94             // Optional name for the overlay object.
95             // Can be later used to retrieve the object by name.
96             name: null,
97             // prop: show
98             // true to show (draw), false to not draw.
99             show: true,
100             // prop: lineWidth
101             // Width of the line.
102             lineWidth: 2,
103             // prop: lineCap
104             // Type of ending placed on the line ['round', 'butt', 'square']
105             lineCap: 'round',
106             // prop: color
107             // color of the line
108             color: '#666666',
109             // prop: shadow
110             // wether or not to draw a shadow on the line
111             shadow: true,
112             // prop: shadowAngle
113             // Shadow angle in degrees
114             shadowAngle: 45,
115             // prop: shadowOffset
116             // Shadow offset from line in pixels
117             shadowOffset: 1,
118             // prop: shadowDepth
119             // Number of times shadow is stroked, each stroke offset shadowOffset from the last.
120             shadowDepth: 3,
121             // prop: shadowAlpha
122             // Alpha channel transparency of shadow.  0 = transparent.
123             shadowAlpha: '0.07',
124             // prop: xaxis
125             // X axis to use for positioning/scaling the line.
126             xaxis: 'xaxis',
127             // prop: yaxis
128             // Y axis to use for positioning/scaling the line.
129             yaxis: 'yaxis',
130             // prop: showTooltip
131             // Show a tooltip with data point values.
132             showTooltip: false,
133             // prop: showTooltipPrecision
134             // Controls how close to line cursor must be to show tooltip.
135             // Higher number = closer to line, lower number = farther from line.
136             // 1.0 = cursor must be over line.
137             showTooltipPrecision: 0.6,
138             // prop: tooltipLocation
139             // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
140             tooltipLocation: 'nw',
141             // prop: fadeTooltip
142             // true = fade in/out tooltip, flase = show/hide tooltip
143             fadeTooltip: true,
144             // prop: tooltipFadeSpeed
145             // 'slow', 'def', 'fast', or number of milliseconds.
146             tooltipFadeSpeed: "fast",
147             // prop: tooltipOffset
148             // Pixel offset of tooltip from the highlight.
149             tooltipOffset: 4,
150             // prop: tooltipFormatString
151             // Format string passed the x and y values of the cursor on the line.
152             // e.g., 'Dogs: %.2f, Cats: %d'.
153             tooltipFormatString: '%d, %d'
154         };
155     }
156
157     /**
158      * Class: Line
159      * A straight line.
160      */
161     function Line(options) {
162         LineBase.call(this);
163         this.type = 'line';
164         var opts = {
165             // prop: start
166             // [x, y] coordinates for the start of the line.
167             start: [],
168             // prop: stop
169             // [x, y] coordinates for the end of the line.
170             stop: []
171         };
172         $.extend(true, this.options, opts, options);
173
174         if (this.options.showTooltipPrecision < 0.01) {
175             this.options.showTooltipPrecision = 0.01;
176         }
177     }
178
179     Line.prototype = new LineBase();
180     Line.prototype.constructor = Line;
181
182
183     /**
184      * Class: HorizontalLine
185      * A straight horizontal line.
186      */
187     function HorizontalLine(options) {
188         LineBase.call(this);
189         this.type = 'horizontalLine';
190         var opts = {
191             // prop: y
192             // y value to position the line
193             y: null,
194             // prop: xmin
195             // x value for the start of the line, null to scale to axis min.
196             xmin: null,
197             // prop: xmax
198             // x value for the end of the line, null to scale to axis max.
199             xmax: null,
200             // prop xOffset
201             // offset ends of the line inside the grid.  Number 
202             xOffset: '6px', // number or string.  Number interpreted as units, string as pixels.
203             xminOffset: null,
204             xmaxOffset: null
205         };
206         $.extend(true, this.options, opts, options);
207
208         if (this.options.showTooltipPrecision < 0.01) {
209             this.options.showTooltipPrecision = 0.01;
210         }
211     }
212
213     HorizontalLine.prototype = new LineBase();
214     HorizontalLine.prototype.constructor = HorizontalLine;
215     
216
217     /**
218      * Class: DashedHorizontalLine
219      * A straight dashed horizontal line.
220      */
221     function DashedHorizontalLine(options) {
222         LineBase.call(this);
223         this.type = 'dashedHorizontalLine';
224         var opts = {
225             y: null,
226             xmin: null,
227             xmax: null,
228             xOffset: '6px', // number or string.  Number interpreted as units, string as pixels.
229             xminOffset: null,
230             xmaxOffset: null,
231             // prop: dashPattern
232             // Array of line, space settings in pixels.
233             // Default is 8 pixel of line, 8 pixel of space.
234             // Note, limit to a 2 element array b/c of bug with higher order arrays.
235             dashPattern: [8,8]
236         };
237         $.extend(true, this.options, opts, options);
238
239         if (this.options.showTooltipPrecision < 0.01) {
240             this.options.showTooltipPrecision = 0.01;
241         }
242     }
243
244     DashedHorizontalLine.prototype = new LineBase();
245     DashedHorizontalLine.prototype.constructor = DashedHorizontalLine;
246     
247
248     /**
249      * Class: VerticalLine
250      * A straight vertical line.
251      */
252     function VerticalLine(options) {
253         LineBase.call(this);
254         this.type = 'verticalLine';
255         var opts = {
256             x: null,
257             ymin: null,
258             ymax: null,
259             yOffset: '6px', // number or string.  Number interpreted as units, string as pixels.
260             yminOffset: null,
261             ymaxOffset: null
262         };
263         $.extend(true, this.options, opts, options);
264
265         if (this.options.showTooltipPrecision < 0.01) {
266             this.options.showTooltipPrecision = 0.01;
267         }
268     }
269
270     VerticalLine.prototype = new LineBase();
271     VerticalLine.prototype.constructor = VerticalLine;
272     
273
274     /**
275      * Class: DashedVerticalLine
276      * A straight dashed vertical line.
277      */
278     function DashedVerticalLine(options) {
279         LineBase.call(this);
280         this.type = 'dashedVerticalLine';
281         this.start = null;
282         this.stop = null;
283         var opts = {
284             x: null,
285             ymin: null,
286             ymax: null,
287             yOffset: '6px', // number or string.  Number interpreted as units, string as pixels.
288             yminOffset: null,
289             ymaxOffset: null,
290             // prop: dashPattern
291             // Array of line, space settings in pixels.
292             // Default is 8 pixel of line, 8 pixel of space.
293             // Note, limit to a 2 element array b/c of bug with higher order arrays.
294             dashPattern: [8,8]
295         };
296         $.extend(true, this.options, opts, options);
297
298         if (this.options.showTooltipPrecision < 0.01) {
299             this.options.showTooltipPrecision = 0.01;
300         }
301     }
302
303     DashedVerticalLine.prototype = new LineBase();
304     DashedVerticalLine.prototype.constructor = DashedVerticalLine;
305     
306     $.jqplot.CanvasOverlay.prototype.addLine = function(opts) {
307         var line = new Line(opts);
308         line.uid = objCounter++;
309         this.objects.push(line);
310         this.objectNames.push(line.options.name);
311     };
312     
313     $.jqplot.CanvasOverlay.prototype.addHorizontalLine = function(opts) {
314         var line = new HorizontalLine(opts);
315         line.uid = objCounter++;
316         this.objects.push(line);
317         this.objectNames.push(line.options.name);
318     };
319     
320     $.jqplot.CanvasOverlay.prototype.addDashedHorizontalLine = function(opts) {
321         var line = new DashedHorizontalLine(opts);
322         line.uid = objCounter++;
323         this.objects.push(line);
324         this.objectNames.push(line.options.name);
325     };
326     
327     $.jqplot.CanvasOverlay.prototype.addVerticalLine = function(opts) {
328         var line = new VerticalLine(opts);
329         line.uid = objCounter++;
330         this.objects.push(line);
331         this.objectNames.push(line.options.name);
332     };
333     
334     $.jqplot.CanvasOverlay.prototype.addDashedVerticalLine = function(opts) {
335         var line = new DashedVerticalLine(opts);
336         line.uid = objCounter++;
337         this.objects.push(line);
338         this.objectNames.push(line.options.name);
339     };
340     
341     $.jqplot.CanvasOverlay.prototype.removeObject = function(idx) {
342         // check if integer, remove by index
343         if ($.type(idx) == 'number') {
344             this.objects.splice(idx, 1);
345             this.objectNames.splice(idx, 1);
346         }
347         // if string, remove by name
348         else {
349             var id = $.inArray(idx, this.objectNames);
350             if (id != -1) {
351                 this.objects.splice(id, 1);
352                 this.objectNames.splice(id, 1);
353             }
354         }
355     };
356     
357     $.jqplot.CanvasOverlay.prototype.getObject = function(idx) {
358         // check if integer, remove by index
359         if ($.type(idx) == 'number') {
360             return this.objects[idx];
361         }
362         // if string, remove by name
363         else {
364             var id = $.inArray(idx, this.objectNames);
365             if (id != -1) {
366                 return this.objects[id];
367             }
368         }
369     };
370     
371     // Set get as alias for getObject.
372     $.jqplot.CanvasOverlay.prototype.get = $.jqplot.CanvasOverlay.prototype.getObject;
373     
374     $.jqplot.CanvasOverlay.prototype.clear = function(plot) {
375         this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight());
376     };
377     
378     $.jqplot.CanvasOverlay.prototype.draw = function(plot) {
379         var obj, 
380             objs = this.objects,
381             mr = this.markerRenderer,
382             start,
383             stop;
384         if (this.options.show) {
385             this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight());
386             for (var k=0; k<objs.length; k++) {
387                 obj = objs[k];
388                 var opts = $.extend(true, {}, obj.options);
389                 if (obj.options.show) {
390                     // style and shadow properties should be set before
391                     // every draw of marker renderer.
392                     mr.shadow = obj.options.shadow;
393                     obj.tooltipWidthFactor = obj.options.lineWidth / obj.options.showTooltipPrecision;
394                     switch (obj.type) {
395                         case 'line':
396                             // style and shadow properties should be set before
397                             // every draw of marker renderer.
398                             mr.style = 'line';
399                             opts.closePath = false;
400                             start = [plot.axes[obj.options.xaxis].series_u2p(obj.options.start[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.start[1])];
401                             stop = [plot.axes[obj.options.xaxis].series_u2p(obj.options.stop[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.stop[1])];
402                             obj.gridStart = start;
403                             obj.gridStop = stop;
404                             mr.draw(start, stop, this.canvas._ctx, opts);
405                             break;
406                         case 'horizontalLine':
407                             
408                             // style and shadow properties should be set before
409                             // every draw of marker renderer.
410                             if (obj.options.y != null) {
411                                 mr.style = 'line';
412                                 opts.closePath = false;
413                                 var xaxis = plot.axes[obj.options.xaxis],
414                                     xstart,
415                                     xstop,
416                                     y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y),
417                                     xminoff = obj.options.xminOffset || obj.options.xOffset,
418                                     xmaxoff = obj.options.xmaxOffset || obj.options.xOffset;
419                                 if (obj.options.xmin != null) {
420                                     xstart = xaxis.series_u2p(obj.options.xmin);
421                                 }
422                                 else if (xminoff != null) {
423                                     if ($.type(xminoff) == "number") {
424                                         xstart = xaxis.series_u2p(xaxis.min + xminoff);
425                                     }
426                                     else if ($.type(xminoff) == "string") {
427                                         xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff);
428                                     }
429                                 }
430                                 if (obj.options.xmax != null) {
431                                     xstop = xaxis.series_u2p(obj.options.xmax);
432                                 }
433                                 else if (xmaxoff != null) {
434                                     if ($.type(xmaxoff) == "number") {
435                                         xstop = xaxis.series_u2p(xaxis.max - xmaxoff);
436                                     }
437                                     else if ($.type(xmaxoff) == "string") {
438                                         xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff);
439                                     }
440                                 }
441                                 if (xstop != null && xstart != null) {
442                                     obj.gridStart = [xstart, y];
443                                     obj.gridStop = [xstop, y];
444                                     mr.draw([xstart, y], [xstop, y], this.canvas._ctx, opts);
445                                 }
446                             }
447                             break;
448
449                         case 'dashedHorizontalLine':
450                             
451                             var dashPat = obj.options.dashPattern;
452                             var dashPatLen = 0;
453                             for (var i=0; i<dashPat.length; i++) {
454                                 dashPatLen += dashPat[i];
455                             }
456
457                             // style and shadow properties should be set before
458                             // every draw of marker renderer.
459                             if (obj.options.y != null) {
460                                 mr.style = 'line';
461                                 opts.closePath = false;
462                                 var xaxis = plot.axes[obj.options.xaxis],
463                                     xstart,
464                                     xstop,
465                                     y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y),
466                                     xminoff = obj.options.xminOffset || obj.options.xOffset,
467                                     xmaxoff = obj.options.xmaxOffset || obj.options.xOffset;
468                                 if (obj.options.xmin != null) {
469                                     xstart = xaxis.series_u2p(obj.options.xmin);
470                                 }
471                                 else if (xminoff != null) {
472                                     if ($.type(xminoff) == "number") {
473                                         xstart = xaxis.series_u2p(xaxis.min + xminoff);
474                                     }
475                                     else if ($.type(xminoff) == "string") {
476                                         xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff);
477                                     }
478                                 }
479                                 if (obj.options.xmax != null) {
480                                     xstop = xaxis.series_u2p(obj.options.xmax);
481                                 }
482                                 else if (xmaxoff != null) {
483                                     if ($.type(xmaxoff) == "number") {
484                                         xstop = xaxis.series_u2p(xaxis.max - xmaxoff);
485                                     }
486                                     else if ($.type(xmaxoff) == "string") {
487                                         xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff);
488                                     }
489                                 }
490                                 if (xstop != null && xstart != null) {
491                                     obj.gridStart = [xstart, y];
492                                     obj.gridStop = [xstop, y];
493                                     var numDash = Math.ceil((xstop - xstart)/dashPatLen);
494                                     var b=xstart, e;
495                                     for (var i=0; i<numDash; i++) {
496                                         for (var j=0; j<dashPat.length; j+=2) {
497                                             e = b+dashPat[j];
498                                             mr.draw([b, y], [e, y], this.canvas._ctx, opts);
499                                             b += dashPat[j];
500                                             if (j < dashPat.length-1) {
501                                                 b += dashPat[j+1];
502                                             }
503                                         }
504                                     }
505                                 }
506                             }
507                             break;
508
509                         case 'verticalLine':
510                             
511                             // style and shadow properties should be set before
512                             // every draw of marker renderer.
513                             if (obj.options.x != null) {
514                                 mr.style = 'line';
515                                 opts.closePath = false;
516                                 var yaxis = plot.axes[obj.options.yaxis],
517                                     ystart,
518                                     ystop,
519                                     x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x),
520                                     yminoff = obj.options.yminOffset || obj.options.yOffset,
521                                     ymaxoff = obj.options.ymaxOffset || obj.options.yOffset;
522                                 if (obj.options.ymin != null) {
523                                     ystart = yaxis.series_u2p(obj.options.ymin);
524                                 }
525                                 else if (yminoff != null) {
526                                     if ($.type(yminoff) == "number") {
527                                         ystart = yaxis.series_u2p(yaxis.min - yminoff);
528                                     }
529                                     else if ($.type(yminoff) == "string") {
530                                         ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff);
531                                     }
532                                 }
533                                 if (obj.options.ymax != null) {
534                                     ystop = yaxis.series_u2p(obj.options.ymax);
535                                 }
536                                 else if (ymaxoff != null) {
537                                     if ($.type(ymaxoff) == "number") {
538                                         ystop = yaxis.series_u2p(yaxis.max + ymaxoff);
539                                     }
540                                     else if ($.type(ymaxoff) == "string") {
541                                         ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff);
542                                     }
543                                 }
544                                 if (ystop != null && ystart != null) {
545                                     obj.gridStart = [x, ystart];
546                                     obj.gridStop = [x, ystop];
547                                     mr.draw([x, ystart], [x, ystop], this.canvas._ctx, opts);
548                                 }
549                             }
550                             break;
551
552                         case 'dashedVerticalLine':
553                             
554                             var dashPat = obj.options.dashPattern;
555                             var dashPatLen = 0;
556                             for (var i=0; i<dashPat.length; i++) {
557                                 dashPatLen += dashPat[i];
558                             }
559
560                             // style and shadow properties should be set before
561                             // every draw of marker renderer.
562                             if (obj.options.x != null) {
563                                 mr.style = 'line';
564                                 opts.closePath = false;
565                                 var yaxis = plot.axes[obj.options.yaxis],
566                                     ystart,
567                                     ystop,
568                                     x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x),
569                                     yminoff = obj.options.yminOffset || obj.options.yOffset,
570                                     ymaxoff = obj.options.ymaxOffset || obj.options.yOffset;
571                                 if (obj.options.ymin != null) {
572                                     ystart = yaxis.series_u2p(obj.options.ymin);
573                                 }
574                                 else if (yminoff != null) {
575                                     if ($.type(yminoff) == "number") {
576                                         ystart = yaxis.series_u2p(yaxis.min - yminoff);
577                                     }
578                                     else if ($.type(yminoff) == "string") {
579                                         ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff);
580                                     }
581                                 }
582                                 if (obj.options.ymax != null) {
583                                     ystop = yaxis.series_u2p(obj.options.ymax);
584                                 }
585                                 else if (ymaxoff != null) {
586                                     if ($.type(ymaxoff) == "number") {
587                                         ystop = yaxis.series_u2p(yaxis.max + ymaxoff);
588                                     }
589                                     else if ($.type(ymaxoff) == "string") {
590                                         ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff);
591                                     }
592                                 }
593
594
595                                 if (ystop != null && ystart != null) {
596                                     obj.gridStart = [x, ystart];
597                                     obj.gridStop = [x, ystop];
598                                     var numDash = Math.ceil((ystart - ystop)/dashPatLen);
599                                     var firstDashAdjust = ((numDash * dashPatLen) - (ystart - ystop))/2.0;
600                                     var b=ystart, e, bs, es;
601                                     for (var i=0; i<numDash; i++) {
602                                         for (var j=0; j<dashPat.length; j+=2) {
603                                             e = b - dashPat[j];
604                                             if (e < ystop) {
605                                                 e = ystop;
606                                             }
607                                             if (b < ystop) {
608                                                 b = ystop;
609                                             }
610                                             // es = e;
611                                             // if (i == 0) {
612                                             //  es += firstDashAdjust;
613                                             // }
614                                             mr.draw([x, b], [x, e], this.canvas._ctx, opts);
615                                             b -= dashPat[j];
616                                             if (j < dashPat.length-1) {
617                                                 b -= dashPat[j+1];
618                                             }
619                                         }
620                                     }
621                                 }
622                             }
623                             break;
624
625                         default:
626                             break;
627                     }
628                 }
629             }
630         }
631     };
632     
633     // called within context of plot
634     // create a canvas which we can draw on.
635     // insert it before the eventCanvas, so eventCanvas will still capture events.
636     $.jqplot.CanvasOverlay.postPlotDraw = function() {
637         var co = this.plugins.canvasOverlay;
638         // Memory Leaks patch    
639         if (co && co.highlightCanvas) {
640             co.highlightCanvas.resetCanvas();
641             co.highlightCanvas = null;
642         }
643         co.canvas = new $.jqplot.GenericCanvas();
644         
645         this.eventCanvas._elem.before(co.canvas.createElement(this._gridPadding, 'jqplot-overlayCanvas-canvas', this._plotDimensions, this));
646         co.canvas.setContext();
647         if (!co.deferDraw) {
648             co.draw(this);
649         }
650
651         var elem = document.createElement('div');
652         co._tooltipElem = $(elem);
653         elem = null;
654         co._tooltipElem.addClass('jqplot-canvasOverlay-tooltip');
655         co._tooltipElem.css({position:'absolute', display:'none'});
656         
657         this.eventCanvas._elem.before(co._tooltipElem);
658         this.eventCanvas._elem.bind('mouseleave', { elem: co._tooltipElem }, function (ev) { ev.data.elem.hide(); });
659
660         var co = null;
661     };
662
663
664     function showTooltip(plot, obj, gridpos, datapos) {
665         var co = plot.plugins.canvasOverlay;
666         var elem = co._tooltipElem;
667
668         var opts = obj.options, x, y;
669
670         elem.html($.jqplot.sprintf(opts.tooltipFormatString, datapos[0], datapos[1]));
671         
672         switch (opts.tooltipLocation) {
673             case 'nw':
674                 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset;
675                 y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true);
676                 break;
677             case 'n':
678                 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true)/2;
679                 y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true);
680                 break;
681             case 'ne':
682                 x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset;
683                 y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true);
684                 break;
685             case 'e':
686                 x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset;
687                 y = gridpos[1] + plot._gridPadding.top - elem.outerHeight(true)/2;
688                 break;
689             case 'se':
690                 x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset;
691                 y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset;
692                 break;
693             case 's':
694                 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true)/2;
695                 y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset;
696                 break;
697             case 'sw':
698                 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset;
699                 y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset;
700                 break;
701             case 'w':
702                 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset;
703                 y = gridpos[1] + plot._gridPadding.top - elem.outerHeight(true)/2;
704                 break;
705             default: // same as 'nw'
706                 x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset;
707                 y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true);
708                 break;
709         }
710
711         elem.css('left', x);
712         elem.css('top', y);
713         if (opts.fadeTooltip) {
714             // Fix for stacked up animations.  Thnanks Trevor!
715             elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed);
716         }
717         else {
718             elem.show();
719         }
720         elem = null;
721     }
722
723
724     function isNearLine(point, lstart, lstop, width) {
725         // r is point to test, p and q are end points.
726         var rx = point[0];
727         var ry = point[1];
728         var px = Math.round(lstop[0]);
729         var py = Math.round(lstop[1]);
730         var qx = Math.round(lstart[0]);
731         var qy = Math.round(lstart[1]);
732
733         var l = Math.sqrt(Math.pow(px-qx, 2) + Math.pow(py-qy, 2));
734
735         // scale error term by length of line.
736         var eps = width*l;
737         var res = Math.abs((qx-px) * (ry-py) - (qy-py) * (rx-px));
738         var ret = (res < eps) ? true : false;
739         return ret;
740     }
741
742
743     function handleMove(ev, gridpos, datapos, neighbor, plot) {
744         var co = plot.plugins.canvasOverlay;
745         var objs = co.objects;
746         var l = objs.length;
747         var obj, haveHighlight=false;
748         var elem;
749         for (var i=0; i<l; i++) {
750             obj = objs[i];
751             if (obj.options.showTooltip) {
752                 var n = isNearLine([gridpos.x, gridpos.y], obj.gridStart, obj.gridStop, obj.tooltipWidthFactor);
753                 datapos = [plot.axes[obj.options.xaxis].series_p2u(gridpos.x), plot.axes[obj.options.yaxis].series_p2u(gridpos.y)];
754
755                 // cases:
756                 //    near line, no highlighting
757                 //    near line, highliting on this line
758                 //    near line, highlighting another line
759                 //    not near any line, highlighting
760                 //    not near any line, no highlighting
761
762                 // near line, not currently highlighting
763                 if (n && co.highlightObjectIndex == null) {
764                     switch (obj.type) {
765                         case 'line':
766                             showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos);
767                             break;
768
769                         case 'horizontalLine':
770                         case 'dashedHorizontalLine':
771                             showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]);
772                             break;
773
774                         case 'verticalLine':
775                         case 'dashedVerticalLine':
776                             showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
777                             break;
778                         default:
779                             break;
780                     } 
781                     co.highlightObjectIndex = i;
782                     haveHighlight = true;
783                     break;
784                 }
785
786                 // near line, highlighting another line.
787                 else if (n && co.highlightObjectIndex !== i) {
788                     // turn off tooltip.
789                     elem = co._tooltipElem;
790                     if (obj.fadeTooltip) {
791                         elem.fadeOut(obj.tooltipFadeSpeed);
792                     }
793                     else {
794                         elem.hide();
795                     }
796
797                     // turn on right tooltip.
798                     switch (obj.type) {
799                         case 'line':
800                             showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos);
801                             break;
802
803                         case 'horizontalLine':
804                         case 'dashedHorizontalLine':
805                             showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]);
806                             break;
807
808                         case 'verticalLine':
809                         case 'dashedVerticalLine':
810                             showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
811                             break;
812                         default:
813                             break;
814                     }
815
816                     co.highlightObjectIndex = i;
817                     haveHighlight = true;
818                     break;
819                 }
820
821                 // near line, already highlighting this line, update
822                 else if (n) {
823                     switch (obj.type) {
824                         case 'line':
825                             showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos);
826                             break;
827
828                         case 'horizontalLine':
829                         case 'dashedHorizontalLine':
830                             showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]);
831                             break;
832
833                         case 'verticalLine':
834                         case 'dashedVerticalLine':
835                             showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]);
836                             break;
837                         default:
838                             break;
839                     }
840
841                     haveHighlight = true;
842                     break;
843                 }
844             }
845         }
846
847         // check if we are highlighting and not near a line, turn it off.
848         if (!haveHighlight && co.highlightObjectIndex !== null) {
849             elem = co._tooltipElem;
850             obj = co.getObject(co.highlightObjectIndex);
851             if (obj.fadeTooltip) {
852                 elem.fadeOut(obj.tooltipFadeSpeed);
853             }
854             else {
855                 elem.hide();
856             }
857             co.highlightObjectIndex = null;
858         }
859     }
860     
861     $.jqplot.postInitHooks.push($.jqplot.CanvasOverlay.postPlotInit);
862     $.jqplot.postDrawHooks.push($.jqplot.CanvasOverlay.postPlotDraw);
863     $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
864
865 })(jQuery);