Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HiveStatistics/sources/HeuristicLab.Services.Hive.Statistics/3.3/Scripts/jqPlot/plugins/jqplot.cursor.js @ 11552

Last change on this file since 11552 was 11222, checked in by mroscoe, 10 years ago
File size: 45.0 KB
Line 
1/**
2 * jqPlot
3 * Pure JavaScript plotting plugin using jQuery
4 *
5 * Version: 1.0.8
6 * Revision: 1250
7 *
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.
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   
33    /**
34     * Class: $.jqplot.Cursor
35     * Plugin class representing the cursor as displayed on the plot.
36     */
37    $.jqplot.Cursor = function(options) {
38        // Group: Properties
39        //
40        // prop: style
41        // CSS spec for cursor style
42        this.style = 'crosshair';
43        this.previousCursor = 'auto';
44        // prop: show
45        // whether to show the cursor or not.
46        this.show = $.jqplot.config.enablePlugins;
47        // prop: showTooltip
48        // show a cursor position tooltip.  Location of the tooltip
49        // will be controlled by followMouse and tooltipLocation.
50        this.showTooltip = true;
51        // prop: followMouse
52        // Tooltip follows the mouse, it is not at a fixed location.
53        // Tooltip will show on the grid at the location given by
54        // tooltipLocation, offset from the grid edge by tooltipOffset.
55        this.followMouse = false;
56        // prop: tooltipLocation
57        // Where to position tooltip.  If followMouse is true, this is
58        // relative to the cursor, otherwise, it is relative to the grid.
59        // One of 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
60        this.tooltipLocation = 'se';
61        // prop: tooltipOffset
62        // Pixel offset of tooltip from the grid boudaries or cursor center.
63        this.tooltipOffset = 6;
64        // prop: showTooltipGridPosition
65        // show the grid pixel coordinates of the mouse.
66        this.showTooltipGridPosition = false;
67        // prop: showTooltipUnitPosition
68        // show the unit (data) coordinates of the mouse.
69        this.showTooltipUnitPosition = true;
70        // prop: showTooltipDataPosition
71        // Used with showVerticalLine to show intersecting data points in the tooltip.
72        this.showTooltipDataPosition = false;
73        // prop: tooltipFormatString
74        // sprintf format string for the tooltip.
75        // Uses Ash Searle's javascript sprintf implementation
76        // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
77        // See http://perldoc.perl.org/functions/sprintf.html for reference
78        // Note, if showTooltipDataPosition is true, the default tooltipFormatString
79        // will be set to the cursorLegendFormatString, not the default given here.
80        this.tooltipFormatString = '%.4P, %.4P';
81        // prop: useAxesFormatters
82        // Use the x and y axes formatters to format the text in the tooltip.
83        this.useAxesFormatters = true;
84        // prop: tooltipAxisGroups
85        // Show position for the specified axes.
86        // This is an array like [['xaxis', 'yaxis'], ['xaxis', 'y2axis']]
87        // Default is to compute automatically for all visible axes.
88        this.tooltipAxisGroups = [];
89        // prop: zoom
90        // Enable plot zooming.
91        this.zoom = false;
92        // zoomProxy and zoomTarget properties are not directly set by user. 
93        // They Will be set through call to zoomProxy method.
94        this.zoomProxy = false;
95        this.zoomTarget = false;
96        // prop: looseZoom
97        // Will expand zoom range to provide more rounded tick values.
98        // Works only with linear, log and date axes.
99        this.looseZoom = true;
100        // prop: clickReset
101        // Will reset plot zoom if single click on plot without drag.
102        this.clickReset = false;
103        // prop: dblClickReset
104        // Will reset plot zoom if double click on plot without drag.
105        this.dblClickReset = true;
106        // prop: showVerticalLine
107        // draw a vertical line across the plot which follows the cursor.
108        // When the line is near a data point, a special legend and/or tooltip can
109        // be updated with the data values.
110        this.showVerticalLine = false;
111        // prop: showHorizontalLine
112        // draw a horizontal line across the plot which follows the cursor.
113        this.showHorizontalLine = false;
114        // prop: constrainZoomTo
115        // 'none', 'x' or 'y'
116        this.constrainZoomTo = 'none';
117        // // prop: autoscaleConstraint
118        // // when a constrained axis is specified, true will
119        // // auatoscale the adjacent axis.
120        // this.autoscaleConstraint = true;
121        this.shapeRenderer = new $.jqplot.ShapeRenderer();
122        this._zoom = {start:[], end:[], started: false, zooming:false, isZoomed:false, axes:{start:{}, end:{}}, gridpos:{}, datapos:{}};
123        this._tooltipElem;
124        this.zoomCanvas;
125        this.cursorCanvas;
126        // prop: intersectionThreshold
127        // pixel distance from data point or marker to consider cursor lines intersecting with point.
128        // If data point markers are not shown, this should be >= 1 or will often miss point intersections.
129        this.intersectionThreshold = 2;
130        // prop: showCursorLegend
131        // Replace the plot legend with an enhanced legend displaying intersection information.
132        this.showCursorLegend = false;
133        // prop: cursorLegendFormatString
134        // Format string used in the cursor legend.  If showTooltipDataPosition is true,
135        // this will also be the default format string used by tooltipFormatString.
136        this.cursorLegendFormatString = $.jqplot.Cursor.cursorLegendFormatString;
137        // whether the cursor is over the grid or not.
138        this._oldHandlers = {onselectstart: null, ondrag: null, onmousedown: null};
139        // prop: constrainOutsideZoom
140        // True to limit actual zoom area to edges of grid, even when zooming
141        // outside of plot area.  That is, can't zoom out by mousing outside plot.
142        this.constrainOutsideZoom = true;
143        // prop: showTooltipOutsideZoom
144        // True will keep updating the tooltip when zooming of the grid.
145        this.showTooltipOutsideZoom = false;
146        // true if mouse is over grid, false if not.
147        this.onGrid = false;
148        $.extend(true, this, options);
149    };
150   
151    $.jqplot.Cursor.cursorLegendFormatString = '%s x:%s, y:%s';
152   
153    // called with scope of plot
154    $.jqplot.Cursor.init = function (target, data, opts){
155        // add a cursor attribute to the plot
156        var options = opts || {};
157        this.plugins.cursor = new $.jqplot.Cursor(options.cursor);
158        var c = this.plugins.cursor;
159
160        if (c.show) {
161            $.jqplot.eventListenerHooks.push(['jqplotMouseEnter', handleMouseEnter]);
162            $.jqplot.eventListenerHooks.push(['jqplotMouseLeave', handleMouseLeave]);
163            $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMouseMove]);
164           
165            if (c.showCursorLegend) {             
166                opts.legend = opts.legend || {};
167                opts.legend.renderer =  $.jqplot.CursorLegendRenderer;
168                opts.legend.formatString = this.plugins.cursor.cursorLegendFormatString;
169                opts.legend.show = true;
170            }
171           
172            if (c.zoom) {
173                $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleMouseDown]);
174               
175                if (c.clickReset) {
176                    $.jqplot.eventListenerHooks.push(['jqplotClick', handleClick]);
177                }
178               
179                if (c.dblClickReset) {
180                    $.jqplot.eventListenerHooks.push(['jqplotDblClick', handleDblClick]);
181                }             
182            }
183   
184            this.resetZoom = function() {
185                var axes = this.axes;
186                if (!c.zoomProxy) {
187                    for (var ax in axes) {
188                        axes[ax].reset();
189                        axes[ax]._ticks = [];
190                        // fake out tick creation algorithm to make sure original auto
191                        // computed format string is used if _overrideFormatString is true
192                        if (c._zoom.axes[ax] !== undefined) {
193                            axes[ax]._autoFormatString = c._zoom.axes[ax].tickFormatString;
194                        }
195                    }
196                    this.redraw();
197                }
198                else {
199                    var ctx = this.plugins.cursor.zoomCanvas._ctx;
200                    ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
201                    ctx = null;
202                }
203                this.plugins.cursor._zoom.isZoomed = false;
204                this.target.trigger('jqplotResetZoom', [this, this.plugins.cursor]);
205            };
206           
207
208            if (c.showTooltipDataPosition) {
209                c.showTooltipUnitPosition = false;
210                c.showTooltipGridPosition = false;
211                if (options.cursor.tooltipFormatString == undefined) {
212                    c.tooltipFormatString = $.jqplot.Cursor.cursorLegendFormatString;
213                }
214            }
215        }
216    };
217   
218    // called with context of plot
219    $.jqplot.Cursor.postDraw = function() {
220        var c = this.plugins.cursor;
221       
222        // Memory Leaks patch
223        if (c.zoomCanvas) {
224            c.zoomCanvas.resetCanvas();
225            c.zoomCanvas = null;
226        }
227       
228        if (c.cursorCanvas) {
229            c.cursorCanvas.resetCanvas();
230            c.cursorCanvas = null;
231        }
232       
233        if (c._tooltipElem) {
234            c._tooltipElem.emptyForce();
235            c._tooltipElem = null;
236        }
237
238       
239        if (c.zoom) {
240            c.zoomCanvas = new $.jqplot.GenericCanvas();
241            this.eventCanvas._elem.before(c.zoomCanvas.createElement(this._gridPadding, 'jqplot-zoom-canvas', this._plotDimensions, this));
242            c.zoomCanvas.setContext();
243        }
244
245        var elem = document.createElement('div');
246        c._tooltipElem = $(elem);
247        elem = null;
248        c._tooltipElem.addClass('jqplot-cursor-tooltip');
249        c._tooltipElem.css({position:'absolute', display:'none'});
250       
251       
252        if (c.zoomCanvas) {
253            c.zoomCanvas._elem.before(c._tooltipElem);
254        }
255
256        else {
257            this.eventCanvas._elem.before(c._tooltipElem);
258        }
259
260        if (c.showVerticalLine || c.showHorizontalLine) {
261            c.cursorCanvas = new $.jqplot.GenericCanvas();
262            this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions, this));
263            c.cursorCanvas.setContext();
264        }
265
266        // if we are showing the positions in unit coordinates, and no axes groups
267        // were specified, create a default set.
268        if (c.showTooltipUnitPosition){
269            if (c.tooltipAxisGroups.length === 0) {
270                var series = this.series;
271                var s;
272                var temp = [];
273                for (var i=0; i<series.length; i++) {
274                    s = series[i];
275                    var ax = s.xaxis+','+s.yaxis;
276                    if ($.inArray(ax, temp) == -1) {
277                        temp.push(ax);
278                    }
279                }
280                for (var i=0; i<temp.length; i++) {
281                    c.tooltipAxisGroups.push(temp[i].split(','));
282                }
283            }
284        }
285    };
286   
287    // Group: methods
288    //
289    // method: $.jqplot.Cursor.zoomProxy
290    // links targetPlot to controllerPlot so that plot zooming of
291    // targetPlot will be controlled by zooming on the controllerPlot.
292    // controllerPlot will not actually zoom, but acts as an
293    // overview plot.  Note, the zoom options must be set to true for
294    // zoomProxy to work.
295    $.jqplot.Cursor.zoomProxy = function(targetPlot, controllerPlot) {
296        var tc = targetPlot.plugins.cursor;
297        var cc = controllerPlot.plugins.cursor;
298        tc.zoomTarget = true;
299        tc.zoom = true;
300        tc.style = 'auto';
301        tc.dblClickReset = false;
302        cc.zoom = true;
303        cc.zoomProxy = true;
304             
305        controllerPlot.target.bind('jqplotZoom', plotZoom);
306        controllerPlot.target.bind('jqplotResetZoom', plotReset);
307
308        function plotZoom(ev, gridpos, datapos, plot, cursor) {
309            tc.doZoom(gridpos, datapos, targetPlot, cursor);
310        }
311
312        function plotReset(ev, plot, cursor) {
313            targetPlot.resetZoom();
314        }
315    };
316   
317    $.jqplot.Cursor.prototype.resetZoom = function(plot, cursor) {
318        var axes = plot.axes;
319        var cax = cursor._zoom.axes;
320        if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) {
321            for (var ax in axes) {
322                // axes[ax]._ticks = [];
323                // axes[ax].min = cax[ax].min;
324                // axes[ax].max = cax[ax].max;
325                // axes[ax].numberTicks = cax[ax].numberTicks;
326                // axes[ax].tickInterval = cax[ax].tickInterval;
327                // // for date axes
328                // axes[ax].daTickInterval = cax[ax].daTickInterval;
329                axes[ax].reset();
330                axes[ax]._ticks = [];
331                // fake out tick creation algorithm to make sure original auto
332                // computed format string is used if _overrideFormatString is true
333                axes[ax]._autoFormatString = cax[ax].tickFormatString;
334            }
335            plot.redraw();
336            cursor._zoom.isZoomed = false;
337        }
338        else {
339            var ctx = cursor.zoomCanvas._ctx;
340            ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
341            ctx = null;
342        }
343        plot.target.trigger('jqplotResetZoom', [plot, cursor]);
344    };
345   
346    $.jqplot.Cursor.resetZoom = function(plot) {
347        plot.resetZoom();
348    };
349   
350    $.jqplot.Cursor.prototype.doZoom = function (gridpos, datapos, plot, cursor) {
351        var c = cursor;
352        var axes = plot.axes;
353        var zaxes = c._zoom.axes;
354        var start = zaxes.start;
355        var end = zaxes.end;
356        var min, max, dp, span,
357            newmin, newmax, curax, _numberTicks, ret;
358        var ctx = plot.plugins.cursor.zoomCanvas._ctx;
359        // don't zoom if zoom area is too small (in pixels)
360        if ((c.constrainZoomTo == 'none' && Math.abs(gridpos.x - c._zoom.start[0]) > 6 && Math.abs(gridpos.y - c._zoom.start[1]) > 6) || (c.constrainZoomTo == 'x' && Math.abs(gridpos.x - c._zoom.start[0]) > 6) ||  (c.constrainZoomTo == 'y' && Math.abs(gridpos.y - c._zoom.start[1]) > 6)) {
361            if (!plot.plugins.cursor.zoomProxy) {
362                for (var ax in datapos) {
363                    // make a copy of the original axes to revert back.
364                    if (c._zoom.axes[ax] == undefined) {
365                        c._zoom.axes[ax] = {};
366                        c._zoom.axes[ax].numberTicks = axes[ax].numberTicks;
367                        c._zoom.axes[ax].tickInterval = axes[ax].tickInterval;
368                        // for date axes...
369                        c._zoom.axes[ax].daTickInterval = axes[ax].daTickInterval;
370                        c._zoom.axes[ax].min = axes[ax].min;
371                        c._zoom.axes[ax].max = axes[ax].max;
372                        c._zoom.axes[ax].tickFormatString = (axes[ax].tickOptions != null) ? axes[ax].tickOptions.formatString :  '';
373                    }
374
375
376                    if ((c.constrainZoomTo == 'none') || (c.constrainZoomTo == 'x' && ax.charAt(0) == 'x') || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'y')) {   
377                        dp = datapos[ax];
378                        if (dp != null) {           
379                            if (dp > start[ax]) {
380                                newmin = start[ax];
381                                newmax = dp;
382                            }
383                            else {
384                                span = start[ax] - dp;
385                                newmin = dp;
386                                newmax = start[ax];
387                            }
388
389                            curax = axes[ax];
390
391                            _numberTicks = null;
392
393                            // if aligning this axis, use number of ticks from previous axis.
394                            // Do I need to reset somehow if alignTicks is changed and then graph is replotted??
395                            if (curax.alignTicks) {
396                                if (curax.name === 'x2axis' && plot.axes.xaxis.show) {
397                                    _numberTicks = plot.axes.xaxis.numberTicks;
398                                }
399                                else if (curax.name.charAt(0) === 'y' && curax.name !== 'yaxis' && curax.name !== 'yMidAxis' && plot.axes.yaxis.show) {
400                                    _numberTicks = plot.axes.yaxis.numberTicks;
401                                }
402                            }
403                           
404                            if (this.looseZoom && (axes[ax].renderer.constructor === $.jqplot.LinearAxisRenderer || axes[ax].renderer.constructor === $.jqplot.LogAxisRenderer )) { //} || axes[ax].renderer.constructor === $.jqplot.DateAxisRenderer)) {
405
406                                ret = $.jqplot.LinearTickGenerator(newmin, newmax, curax._scalefact, _numberTicks);
407
408                                // if new minimum is less than "true" minimum of axis display, adjust it
409                                if (axes[ax].tickInset && ret[0] < axes[ax].min + axes[ax].tickInset * axes[ax].tickInterval) {
410                                    ret[0] += ret[4];
411                                    ret[2] -= 1;
412                                }
413
414                                // if new maximum is greater than "true" max of axis display, adjust it
415                                if (axes[ax].tickInset && ret[1] > axes[ax].max - axes[ax].tickInset * axes[ax].tickInterval) {
416                                    ret[1] -= ret[4];
417                                    ret[2] -= 1;
418                                }
419
420                                // for log axes, don't fall below current minimum, this will look bad and can't have 0 in range anyway.
421                                if (axes[ax].renderer.constructor === $.jqplot.LogAxisRenderer && ret[0] < axes[ax].min) {
422                                    // remove a tick and shift min up
423                                    ret[0] += ret[4];
424                                    ret[2] -= 1;
425                                }
426
427                                axes[ax].min = ret[0];
428                                axes[ax].max = ret[1];
429                                axes[ax]._autoFormatString = ret[3];
430                                axes[ax].numberTicks = ret[2];
431                                axes[ax].tickInterval = ret[4];
432                                // for date axes...
433                                axes[ax].daTickInterval = [ret[4]/1000, 'seconds'];
434                            }
435                            else {
436                                axes[ax].min = newmin;
437                                axes[ax].max = newmax;
438                                axes[ax].tickInterval = null;
439                                axes[ax].numberTicks = null;
440                                // for date axes...
441                                axes[ax].daTickInterval = null;
442                            }
443
444                            axes[ax]._ticks = [];
445                        }
446                    }
447                           
448                    // if ((c.constrainZoomTo == 'x' && ax.charAt(0) == 'y' && c.autoscaleConstraint) || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'x' && c.autoscaleConstraint)) {
449                    //     dp = datapos[ax];
450                    //     if (dp != null) {
451                    //         axes[ax].max == null;
452                    //         axes[ax].min = null;
453                    //     }
454                    // }
455                }
456                ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
457                plot.redraw();
458                c._zoom.isZoomed = true;
459                ctx = null;
460            }
461            plot.target.trigger('jqplotZoom', [gridpos, datapos, plot, cursor]);
462        }
463    };
464   
465    $.jqplot.preInitHooks.push($.jqplot.Cursor.init);
466    $.jqplot.postDrawHooks.push($.jqplot.Cursor.postDraw);
467   
468    function updateTooltip(gridpos, datapos, plot) {
469        var c = plot.plugins.cursor;
470        var s = '';
471        var addbr = false;
472        if (c.showTooltipGridPosition) {
473            s = gridpos.x+', '+gridpos.y;
474            addbr = true;
475        }
476        if (c.showTooltipUnitPosition) {
477            var g;
478            for (var i=0; i<c.tooltipAxisGroups.length; i++) {
479                g = c.tooltipAxisGroups[i];
480                if (addbr) {
481                    s += '<br />';
482                }
483                if (c.useAxesFormatters) {
484                    for (var j=0; j<g.length; j++) {
485                        if (j) {
486                            s += ', ';
487                        }
488                        var af = plot.axes[g[j]]._ticks[0].formatter;
489                        var afstr = plot.axes[g[j]]._ticks[0].formatString;
490                        s += af(afstr, datapos[g[j]]);
491                    }
492                }
493                else {
494                    s += $.jqplot.sprintf(c.tooltipFormatString, datapos[g[0]], datapos[g[1]]);
495                }
496                addbr = true;
497            }
498        }
499       
500        if (c.showTooltipDataPosition) {
501            var series = plot.series;
502            var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y);
503            var addbr = false;
504       
505            for (var i = 0; i< series.length; i++) {
506                if (series[i].show) {
507                    var idx = series[i].index;
508                    var label = series[i].label.toString();
509                    var cellid = $.inArray(idx, ret.indices);
510                    var sx = undefined;
511                    var sy = undefined;
512                    if (cellid != -1) {
513                        var data = ret.data[cellid].data;
514                        if (c.useAxesFormatters) {
515                            var xf = series[i]._xaxis._ticks[0].formatter;
516                            var yf = series[i]._yaxis._ticks[0].formatter;
517                            var xfstr = series[i]._xaxis._ticks[0].formatString;
518                            var yfstr = series[i]._yaxis._ticks[0].formatString;
519                            sx = xf(xfstr, data[0]);
520                            sy = yf(yfstr, data[1]);
521                        }
522                        else {
523                            sx = data[0];
524                            sy = data[1];
525                        }
526                        if (addbr) {
527                            s += '<br />';
528                        }
529                        s += $.jqplot.sprintf(c.tooltipFormatString, label, sx, sy);
530                        addbr = true;
531                    }
532                }
533            }
534           
535        }
536        c._tooltipElem.html(s);
537    }
538   
539    function moveLine(gridpos, plot) {
540        var c = plot.plugins.cursor;
541        var ctx = c.cursorCanvas._ctx;
542        ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
543        if (c.showVerticalLine) {
544            c.shapeRenderer.draw(ctx, [[gridpos.x, 0], [gridpos.x, ctx.canvas.height]]);
545        }
546        if (c.showHorizontalLine) {
547            c.shapeRenderer.draw(ctx, [[0, gridpos.y], [ctx.canvas.width, gridpos.y]]);
548        }
549        var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y);
550        if (c.showCursorLegend) {
551            var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label');
552            for (var i=0; i<cells.length; i++) {
553                var idx = $(cells[i]).data('seriesIndex');
554                var series = plot.series[idx];
555                var label = series.label.toString();
556                var cellid = $.inArray(idx, ret.indices);
557                var sx = undefined;
558                var sy = undefined;
559                if (cellid != -1) {
560                    var data = ret.data[cellid].data;
561                    if (c.useAxesFormatters) {
562                        var xf = series._xaxis._ticks[0].formatter;
563                        var yf = series._yaxis._ticks[0].formatter;
564                        var xfstr = series._xaxis._ticks[0].formatString;
565                        var yfstr = series._yaxis._ticks[0].formatString;
566                        sx = xf(xfstr, data[0]);
567                        sy = yf(yfstr, data[1]);
568                    }
569                    else {
570                        sx = data[0];
571                        sy = data[1];
572                    }
573                }
574                if (plot.legend.escapeHtml) {
575                    $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy));
576                }
577                else {
578                    $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy));
579                }
580            }       
581        }
582        ctx = null;
583    }
584       
585    function getIntersectingPoints(plot, x, y) {
586        var ret = {indices:[], data:[]};
587        var s, i, d0, d, j, r, p;
588        var threshold;
589        var c = plot.plugins.cursor;
590        for (var i=0; i<plot.series.length; i++) {
591            s = plot.series[i];
592            r = s.renderer;
593            if (s.show) {
594                threshold = c.intersectionThreshold;
595                if (s.showMarker) {
596                    threshold += s.markerRenderer.size/2;
597                }
598                for (var j=0; j<s.gridData.length; j++) {
599                    p = s.gridData[j];
600                    // check vertical line
601                    if (c.showVerticalLine) {
602                        if (Math.abs(x-p[0]) <= threshold) {
603                            ret.indices.push(i);
604                            ret.data.push({seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]});
605                        }
606                    }
607                }
608            }
609        }
610        return ret;
611    }
612   
613    function moveTooltip(gridpos, plot) {
614        var c = plot.plugins.cursor; 
615        var elem = c._tooltipElem;
616        switch (c.tooltipLocation) {
617            case 'nw':
618                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
619                var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
620                break;
621            case 'n':
622                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
623                var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
624                break;
625            case 'ne':
626                var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
627                var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
628                break;
629            case 'e':
630                var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
631                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
632                break;
633            case 'se':
634                var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
635                var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
636                break;
637            case 's':
638                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
639                var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
640                break;
641            case 'sw':
642                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
643                var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
644                break;
645            case 'w':
646                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
647                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
648                break;
649            default:
650                var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
651                var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
652                break;
653        }
654           
655        elem.css('left', x);
656        elem.css('top', y);
657        elem = null;
658    }
659   
660    function positionTooltip(plot) {
661        // fake a grid for positioning
662        var grid = plot._gridPadding;
663        var c = plot.plugins.cursor;
664        var elem = c._tooltipElem; 
665        switch (c.tooltipLocation) {
666            case 'nw':
667                var a = grid.left + c.tooltipOffset;
668                var b = grid.top + c.tooltipOffset;
669                elem.css('left', a);
670                elem.css('top', b);
671                break;
672            case 'n':
673                var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2;
674                var b = grid.top + c.tooltipOffset;
675                elem.css('left', a);
676                elem.css('top', b);
677                break;
678            case 'ne':
679                var a = grid.right + c.tooltipOffset;
680                var b = grid.top + c.tooltipOffset;
681                elem.css({right:a, top:b});
682                break;
683            case 'e':
684                var a = grid.right + c.tooltipOffset;
685                var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2;
686                elem.css({right:a, top:b});
687                break;
688            case 'se':
689                var a = grid.right + c.tooltipOffset;
690                var b = grid.bottom + c.tooltipOffset;
691                elem.css({right:a, bottom:b});
692                break;
693            case 's':
694                var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2;
695                var b = grid.bottom + c.tooltipOffset;
696                elem.css({left:a, bottom:b});
697                break;
698            case 'sw':
699                var a = grid.left + c.tooltipOffset;
700                var b = grid.bottom + c.tooltipOffset;
701                elem.css({left:a, bottom:b});
702                break;
703            case 'w':
704                var a = grid.left + c.tooltipOffset;
705                var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2;
706                elem.css({left:a, top:b});
707                break;
708            default:  // same as 'se'
709                var a = grid.right - c.tooltipOffset;
710                var b = grid.bottom + c.tooltipOffset;
711                elem.css({right:a, bottom:b});
712                break;
713        }
714        elem = null;
715    }
716   
717    function handleClick (ev, gridpos, datapos, neighbor, plot) {
718        ev.preventDefault();
719        ev.stopImmediatePropagation();
720        var c = plot.plugins.cursor;
721        if (c.clickReset) {
722            c.resetZoom(plot, c);
723        }
724        var sel = window.getSelection;
725        if (document.selection && document.selection.empty)
726        {
727            document.selection.empty();
728        }
729        else if (sel && !sel().isCollapsed) {
730            sel().collapse();
731        }
732        return false;
733    }
734   
735    function handleDblClick (ev, gridpos, datapos, neighbor, plot) {
736        ev.preventDefault();
737        ev.stopImmediatePropagation();
738        var c = plot.plugins.cursor;
739        if (c.dblClickReset) {
740            c.resetZoom(plot, c);
741        }
742        var sel = window.getSelection;
743        if (document.selection && document.selection.empty)
744        {
745            document.selection.empty();
746        }
747        else if (sel && !sel().isCollapsed) {
748            sel().collapse();
749        }
750        return false;
751    }
752   
753    function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) {
754        var c = plot.plugins.cursor;
755        c.onGrid = false;
756        if (c.show) {
757            $(ev.target).css('cursor', c.previousCursor);
758            if (c.showTooltip && !(c._zoom.zooming && c.showTooltipOutsideZoom && !c.constrainOutsideZoom)) {
759                c._tooltipElem.empty();
760                c._tooltipElem.hide();
761            }
762            if (c.zoom) {
763                c._zoom.gridpos = gridpos;
764                c._zoom.datapos = datapos;
765            }
766            if (c.showVerticalLine || c.showHorizontalLine) {
767                var ctx = c.cursorCanvas._ctx;
768                ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
769                ctx = null;
770            }
771            if (c.showCursorLegend) {
772                var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label');
773                for (var i=0; i<cells.length; i++) {
774                    var idx = $(cells[i]).data('seriesIndex');
775                    var series = plot.series[idx];
776                    var label = series.label.toString();
777                    if (plot.legend.escapeHtml) {
778                        $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined));
779                    }
780                    else {
781                        $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined));
782                    }
783               
784                }       
785            }
786        }
787    }
788   
789    function handleMouseEnter(ev, gridpos, datapos, neighbor, plot) {
790        var c = plot.plugins.cursor;
791        c.onGrid = true;
792        if (c.show) {
793            c.previousCursor = ev.target.style.cursor;
794            ev.target.style.cursor = c.style;
795            if (c.showTooltip) {
796                updateTooltip(gridpos, datapos, plot);
797                if (c.followMouse) {
798                    moveTooltip(gridpos, plot);
799                }
800                else {
801                    positionTooltip(plot);
802                }
803                c._tooltipElem.show();
804            }
805            if (c.showVerticalLine || c.showHorizontalLine) {
806                moveLine(gridpos, plot);
807            }
808        }
809
810    }   
811   
812    function handleMouseMove(ev, gridpos, datapos, neighbor, plot) {
813        var c = plot.plugins.cursor;
814        if (c.show) {
815            if (c.showTooltip) {
816                updateTooltip(gridpos, datapos, plot);
817                if (c.followMouse) {
818                    moveTooltip(gridpos, plot);
819                }
820            }
821            if (c.showVerticalLine || c.showHorizontalLine) {
822                moveLine(gridpos, plot);
823            }
824        }
825    }
826           
827    function getEventPosition(ev) {
828        var plot = ev.data.plot;
829        var go = plot.eventCanvas._elem.offset();
830        var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
831        //////
832        // TO DO: handle yMidAxis
833        //////
834        var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null};
835        var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
836        var ax = plot.axes;
837        var n, axis;
838        for (n=11; n>0; n--) {
839            axis = an[n-1];
840            if (ax[axis].show) {
841                dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
842            }
843        }
844
845        return {offsets:go, gridPos:gridPos, dataPos:dataPos};
846    }   
847   
848    function handleZoomMove(ev) {
849        var plot = ev.data.plot;
850        var c = plot.plugins.cursor;
851        // don't do anything if not on grid.
852        if (c.show && c.zoom && c._zoom.started && !c.zoomTarget) {
853            ev.preventDefault();
854            var ctx = c.zoomCanvas._ctx;
855            var positions = getEventPosition(ev);
856            var gridpos = positions.gridPos;
857            var datapos = positions.dataPos;
858            c._zoom.gridpos = gridpos;
859            c._zoom.datapos = datapos;
860            c._zoom.zooming = true;
861            var xpos = gridpos.x;
862            var ypos = gridpos.y;
863            var height = ctx.canvas.height;
864            var width = ctx.canvas.width;
865            if (c.showTooltip && !c.onGrid && c.showTooltipOutsideZoom) {
866                updateTooltip(gridpos, datapos, plot);
867                if (c.followMouse) {
868                    moveTooltip(gridpos, plot);
869                }
870            }
871            if (c.constrainZoomTo == 'x') {
872                c._zoom.end = [xpos, height];
873            }
874            else if (c.constrainZoomTo == 'y') {
875                c._zoom.end = [width, ypos];
876            }
877            else {
878                c._zoom.end = [xpos, ypos];
879            }
880            var sel = window.getSelection;
881            if (document.selection && document.selection.empty)
882            {
883                document.selection.empty();
884            }
885            else if (sel && !sel().isCollapsed) {
886                sel().collapse();
887            }
888            drawZoomBox.call(c);
889            ctx = null;
890        }
891    }
892   
893    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
894        var c = plot.plugins.cursor;
895        if(plot.plugins.mobile){
896            $(document).one('vmouseup.jqplot_cursor', {plot:plot}, handleMouseUp);
897        } else {
898            $(document).one('mouseup.jqplot_cursor', {plot:plot}, handleMouseUp);
899        }
900        var axes = plot.axes;
901        if (document.onselectstart != undefined) {
902            c._oldHandlers.onselectstart = document.onselectstart;
903            document.onselectstart = function () { return false; };
904        }
905        if (document.ondrag != undefined) {
906            c._oldHandlers.ondrag = document.ondrag;
907            document.ondrag = function () { return false; };
908        }
909        if (document.onmousedown != undefined) {
910            c._oldHandlers.onmousedown = document.onmousedown;
911            document.onmousedown = function () { return false; };
912        }
913        if (c.zoom) {
914            if (!c.zoomProxy) {
915                var ctx = c.zoomCanvas._ctx;
916                ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
917                ctx = null;
918            }
919            if (c.constrainZoomTo == 'x') {
920                c._zoom.start = [gridpos.x, 0];
921            }
922            else if (c.constrainZoomTo == 'y') {
923                c._zoom.start = [0, gridpos.y];
924            }
925            else {
926                c._zoom.start = [gridpos.x, gridpos.y];
927            }
928            c._zoom.started = true;
929            for (var ax in datapos) {
930                // get zoom starting position.
931                c._zoom.axes.start[ax] = datapos[ax];
932            } 
933           if(plot.plugins.mobile){
934                $(document).bind('vmousemove.jqplotCursor', {plot:plot}, handleZoomMove);             
935            } else {
936                $(document).bind('mousemove.jqplotCursor', {plot:plot}, handleZoomMove);             
937            }
938
939        }
940    }
941   
942    function handleMouseUp(ev) {
943        var plot = ev.data.plot;
944        var c = plot.plugins.cursor;
945        if (c.zoom && c._zoom.zooming && !c.zoomTarget) {
946            var xpos = c._zoom.gridpos.x;
947            var ypos = c._zoom.gridpos.y;
948            var datapos = c._zoom.datapos;
949            var height = c.zoomCanvas._ctx.canvas.height;
950            var width = c.zoomCanvas._ctx.canvas.width;
951            var axes = plot.axes;
952           
953            if (c.constrainOutsideZoom && !c.onGrid) {
954                if (xpos < 0) { xpos = 0; }
955                else if (xpos > width) { xpos = width; }
956                if (ypos < 0) { ypos = 0; }
957                else if (ypos > height) { ypos = height; }
958               
959                for (var axis in datapos) {
960                    if (datapos[axis]) {
961                        if (axis.charAt(0) == 'x') {
962                            datapos[axis] = axes[axis].series_p2u(xpos);
963                        }
964                        else {
965                            datapos[axis] = axes[axis].series_p2u(ypos);
966                        }
967                    }
968                }
969            }
970           
971            if (c.constrainZoomTo == 'x') {
972                ypos = height;
973            }
974            else if (c.constrainZoomTo == 'y') {
975                xpos = width;
976            }
977            c._zoom.end = [xpos, ypos];
978            c._zoom.gridpos = {x:xpos, y:ypos};
979           
980            c.doZoom(c._zoom.gridpos, datapos, plot, c);
981        }
982        c._zoom.started = false;
983        c._zoom.zooming = false;
984       
985        $(document).unbind('mousemove.jqplotCursor', handleZoomMove);
986       
987        if (document.onselectstart != undefined && c._oldHandlers.onselectstart != null){
988            document.onselectstart = c._oldHandlers.onselectstart;
989            c._oldHandlers.onselectstart = null;
990        }
991        if (document.ondrag != undefined && c._oldHandlers.ondrag != null){
992            document.ondrag = c._oldHandlers.ondrag;
993            c._oldHandlers.ondrag = null;
994        }
995        if (document.onmousedown != undefined && c._oldHandlers.onmousedown != null){
996            document.onmousedown = c._oldHandlers.onmousedown;
997            c._oldHandlers.onmousedown = null;
998        }
999
1000    }
1001   
1002    function drawZoomBox() {
1003        var start = this._zoom.start;
1004        var end = this._zoom.end;
1005        var ctx = this.zoomCanvas._ctx;
1006        var l, t, h, w;
1007        if (end[0] > start[0]) {
1008            l = start[0];
1009            w = end[0] - start[0];
1010        }
1011        else {
1012            l = end[0];
1013            w = start[0] - end[0];
1014        }
1015        if (end[1] > start[1]) {
1016            t = start[1];
1017            h = end[1] - start[1];
1018        }
1019        else {
1020            t = end[1];
1021            h = start[1] - end[1];
1022        }
1023        ctx.fillStyle = 'rgba(0,0,0,0.2)';
1024        ctx.strokeStyle = '#999999';
1025        ctx.lineWidth = 1.0;
1026        ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
1027        ctx.fillRect(0,0,ctx.canvas.width, ctx.canvas.height);
1028        ctx.clearRect(l, t, w, h);
1029        // IE won't show transparent fill rect, so stroke a rect also.
1030        ctx.strokeRect(l,t,w,h);
1031        ctx = null;
1032    }
1033   
1034    $.jqplot.CursorLegendRenderer = function(options) {
1035        $.jqplot.TableLegendRenderer.call(this, options);
1036        this.formatString = '%s';
1037    };
1038   
1039    $.jqplot.CursorLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
1040    $.jqplot.CursorLegendRenderer.prototype.constructor = $.jqplot.CursorLegendRenderer;
1041   
1042    // called in context of a Legend
1043    $.jqplot.CursorLegendRenderer.prototype.draw = function() {
1044        if (this._elem) {
1045            this._elem.emptyForce();
1046            this._elem = null;
1047        }
1048        if (this.show) {
1049            var series = this._series, s;
1050            // make a table.  one line label per row.
1051            var elem = document.createElement('table');
1052            this._elem = $(elem);
1053            elem = null;
1054            this._elem.addClass('jqplot-legend jqplot-cursor-legend');
1055            this._elem.css('position', 'absolute');
1056       
1057            var pad = false;
1058            for (var i = 0; i< series.length; i++) {
1059                s = series[i];
1060                if (s.show && s.showLabel) {
1061                    var lt = $.jqplot.sprintf(this.formatString, s.label.toString());
1062                    if (lt) {
1063                        var color = s.color;
1064                        if (s._stack && !s.fill) {
1065                            color = '';
1066                        }
1067                        addrow.call(this, lt, color, pad, i);
1068                        pad = true;
1069                    }
1070                    // let plugins add more rows to legend.  Used by trend line plugin.
1071                    for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) {
1072                        var item = $.jqplot.addLegendRowHooks[j].call(this, s);
1073                        if (item) {
1074                            addrow.call(this, item.label, item.color, pad);
1075                            pad = true;
1076                        }
1077                    }
1078                }
1079            }
1080            series = s = null;
1081            delete series;
1082            delete s;
1083        }
1084       
1085        function addrow(label, color, pad, idx) {
1086            var rs = (pad) ? this.rowSpacing : '0';
1087            var tr = $('<tr class="jqplot-legend jqplot-cursor-legend"></tr>').appendTo(this._elem);
1088            tr.data('seriesIndex', idx);
1089            $('<td class="jqplot-legend jqplot-cursor-legend-swatch" style="padding-top:'+rs+';">'+
1090                '<div style="border:1px solid #cccccc;padding:0.2em;">'+
1091                '<div class="jqplot-cursor-legend-swatch" style="background-color:'+color+';"></div>'+
1092                '</div></td>').appendTo(tr);
1093            var td = $('<td class="jqplot-legend jqplot-cursor-legend-label" style="vertical-align:middle;padding-top:'+rs+';"></td>');
1094            td.appendTo(tr);
1095            td.data('seriesIndex', idx);
1096            if (this.escapeHtml) {
1097                td.text(label);
1098            }
1099            else {
1100                td.html(label);
1101            }
1102            tr = null;
1103            td = null;
1104        }
1105        return this._elem;
1106    };
1107   
1108})(jQuery);
Note: See TracBrowser for help on using the repository browser.