Subversion-Projekte lars-tiefland.cienc

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
9 lars 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);