Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
875 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
    $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
33
 
34
    /**
35
     * Class: $.jqplot.Highlighter
36
     * Plugin which will highlight data points when they are moused over.
37
     *
38
     * To use this plugin, include the js
39
     * file in your source:
40
     *
41
     * > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script>
42
     *
43
     * A tooltip providing information about the data point is enabled by default.
44
     * To disable the tooltip, set "showTooltip" to false.
45
     *
46
     * You can control what data is displayed in the tooltip with various
47
     * options.  The "tooltipAxes" option controls whether the x, y or both
48
     * data values are displayed.
49
     *
50
     * Some chart types (e.g. hi-low-close) have more than one y value per
51
     * data point. To display the additional values in the tooltip, set the
52
     * "yvalues" option to the desired number of y values present (3 for a hlc chart).
53
     *
54
     * By default, data values will be formatted with the same formatting
55
     * specifiers as used to format the axis ticks.  A custom format code
56
     * can be supplied with the tooltipFormatString option.  This will apply
57
     * to all values in the tooltip.
58
     *
59
     * For more complete control, the "formatString" option can be set.  This
60
     * Allows conplete control over tooltip formatting.  Values are passed to
61
     * the format string in an order determined by the "tooltipAxes" and "yvalues"
62
     * options.  So, if you have a hi-low-close chart and you just want to display
63
     * the hi-low-close values in the tooltip, you could set a formatString like:
64
     *
65
     * > highlighter: {
66
     * >     tooltipAxes: 'y',
67
     * >     yvalues: 3,
68
     * >     formatString:'<table class="jqplot-highlighter">
69
     * >         <tr><td>hi:</td><td>%s</td></tr>
70
     * >         <tr><td>low:</td><td>%s</td></tr>
71
     * >         <tr><td>close:</td><td>%s</td></tr></table>'
72
     * > }
73
     *
74
     */
75
    $.jqplot.Highlighter = function(options) {
76
        // Group: Properties
77
        //
78
        //prop: show
79
        // true to show the highlight.
80
        this.show = $.jqplot.config.enablePlugins;
81
        // prop: markerRenderer
82
        // Renderer used to draw the marker of the highlighted point.
83
        // Renderer will assimilate attributes from the data point being highlighted,
84
        // so no attributes need set on the renderer directly.
85
        // Default is to turn off shadow drawing on the highlighted point.
86
        this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
87
        // prop: showMarker
88
        // true to show the marker
89
        this.showMarker  = true;
90
        // prop: lineWidthAdjust
91
        // Pixels to add to the lineWidth of the highlight.
92
        this.lineWidthAdjust = 2.5;
93
        // prop: sizeAdjust
94
        // Pixels to add to the overall size of the highlight.
95
        this.sizeAdjust = 5;
96
        // prop: showTooltip
97
        // Show a tooltip with data point values.
98
        this.showTooltip = true;
99
        // prop: tooltipLocation
100
        // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
101
        this.tooltipLocation = 'nw';
102
        // prop: fadeTooltip
103
        // true = fade in/out tooltip, flase = show/hide tooltip
104
        this.fadeTooltip = true;
105
        // prop: tooltipFadeSpeed
106
        // 'slow', 'def', 'fast', or number of milliseconds.
107
        this.tooltipFadeSpeed = "fast";
108
        // prop: tooltipOffset
109
        // Pixel offset of tooltip from the highlight.
110
        this.tooltipOffset = 2;
111
        // prop: tooltipAxes
112
        // Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx'
113
        // 'both' and 'xy' are equivalent, 'yx' reverses order of labels.
114
        this.tooltipAxes = 'both';
115
        // prop; tooltipSeparator
116
        // String to use to separate x and y axes in tooltip.
117
        this.tooltipSeparator = ', ';
118
        // prop; tooltipContentEditor
119
        // Function used to edit/augment/replace the formatted tooltip contents.
120
        // Called as str = tooltipContentEditor(str, seriesIndex, pointIndex)
121
        // where str is the generated tooltip html and seriesIndex and pointIndex identify
122
        // the data point being highlighted. Should return the html for the tooltip contents.
123
        this.tooltipContentEditor = null;
124
        // prop: useAxesFormatters
125
        // Use the x and y axes formatters to format the text in the tooltip.
126
        this.useAxesFormatters = true;
127
        // prop: tooltipFormatString
128
        // sprintf format string for the tooltip.
129
        // Uses Ash Searle's javascript sprintf implementation
130
        // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
131
        // See http://perldoc.perl.org/functions/sprintf.html for reference.
132
        // Additional "p" and "P" format specifiers added by Chris Leonello.
133
        this.tooltipFormatString = '%.5P';
134
        // prop: formatString
135
        // alternative to tooltipFormatString
136
        // will format the whole tooltip text, populating with x, y values as
137
        // indicated by tooltipAxes option.  So, you could have a tooltip like:
138
        // 'Date: %s, number of cats: %d' to format the whole tooltip at one go.
139
        // If useAxesFormatters is true, values will be formatted according to
140
        // Axes formatters and you can populate your tooltip string with
141
        // %s placeholders.
142
        this.formatString = null;
143
        // prop: yvalues
144
        // Number of y values to expect in the data point array.
145
        // Typically this is 1.  Certain plots, like OHLC, will
146
        // have more y values in each data point array.
147
        this.yvalues = 1;
148
        // prop: bringSeriesToFront
149
        // This option requires jQuery 1.4+
150
        // True to bring the series of the highlighted point to the front
151
        // of other series.
152
        this.bringSeriesToFront = false;
153
        this._tooltipElem;
154
        this.isHighlighting = false;
155
        this.currentNeighbor = null;
156
 
157
        $.extend(true, this, options);
158
    };
159
 
160
    var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
161
    var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7};
162
    var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e'];
163
 
164
    // axis.renderer.tickrenderer.formatter
165
 
166
    // called with scope of plot
167
    $.jqplot.Highlighter.init = function (target, data, opts){
168
        var options = opts || {};
169
        // add a highlighter attribute to the plot
170
        this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter);
171
    };
172
 
173
    // called within scope of series
174
    $.jqplot.Highlighter.parseOptions = function (defaults, options) {
175
        // Add a showHighlight option to the series
176
        // and set it to true by default.
177
        this.showHighlight = true;
178
    };
179
 
180
    // called within context of plot
181
    // create a canvas which we can draw on.
182
    // insert it before the eventCanvas, so eventCanvas will still capture events.
183
    $.jqplot.Highlighter.postPlotDraw = function() {
184
        // Memory Leaks patch
185
        if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) {
186
            this.plugins.highlighter.highlightCanvas.resetCanvas();
187
            this.plugins.highlighter.highlightCanvas = null;
188
        }
189
 
190
        if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) {
191
            this.plugins.highlighter._tooltipElem.emptyForce();
192
            this.plugins.highlighter._tooltipElem = null;
193
        }
194
 
195
        this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas();
196
 
197
        this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this));
198
        this.plugins.highlighter.highlightCanvas.setContext();
199
 
200
        var elem = document.createElement('div');
201
        this.plugins.highlighter._tooltipElem = $(elem);
202
        elem = null;
203
        this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip');
204
        this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'});
205
 
206
        this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem);
207
    };
208
 
209
    $.jqplot.preInitHooks.push($.jqplot.Highlighter.init);
210
    $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions);
211
    $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw);
212
 
213
    function draw(plot, neighbor) {
214
        var hl = plot.plugins.highlighter;
215
        var s = plot.series[neighbor.seriesIndex];
216
        var smr = s.markerRenderer;
217
        var mr = hl.markerRenderer;
218
        mr.style = smr.style;
219
        mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust;
220
        mr.size = smr.size + hl.sizeAdjust;
221
        var rgba = $.jqplot.getColorComponents(smr.color);
222
        var newrgb = [rgba[0], rgba[1], rgba[2]];
223
        var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
224
        mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
225
        mr.init();
226
        mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx);
227
    }
228
 
229
    function showTooltip(plot, series, neighbor) {
230
        // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}
231
        // gridData should be x,y pixel coords on the grid.
232
        // add the plot._gridPadding to that to get x,y in the target.
233
        var hl = plot.plugins.highlighter;
234
        var elem = hl._tooltipElem;
235
        var serieshl = series.highlighter || {};
236
 
237
        var opts = $.extend(true, {}, hl, serieshl);
238
 
239
        if (opts.useAxesFormatters) {
240
            var xf = series._xaxis._ticks[0].formatter;
241
            var yf = series._yaxis._ticks[0].formatter;
242
            var xfstr = series._xaxis._ticks[0].formatString;
243
            var yfstr = series._yaxis._ticks[0].formatString;
244
            var str;
245
            var xstr = xf(xfstr, neighbor.data[0]);
246
            var ystrs = [];
247
            for (var i=1; i<opts.yvalues+1; i++) {
248
                ystrs.push(yf(yfstr, neighbor.data[i]));
249
            }
250
            if (typeof opts.formatString === 'string') {
251
                switch (opts.tooltipAxes) {
252
                    case 'both':
253
                    case 'xy':
254
                        ystrs.unshift(xstr);
255
                        ystrs.unshift(opts.formatString);
256
                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
257
                        break;
258
                    case 'yx':
259
                        ystrs.push(xstr);
260
                        ystrs.unshift(opts.formatString);
261
                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
262
                        break;
263
                    case 'x':
264
                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString, xstr]);
265
                        break;
266
                    case 'y':
267
                        ystrs.unshift(opts.formatString);
268
                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
269
                        break;
270
                    default: // same as xy
271
                        ystrs.unshift(xstr);
272
                        ystrs.unshift(opts.formatString);
273
                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
274
                        break;
275
                }
276
            }
277
            else {
278
                switch (opts.tooltipAxes) {
279
                    case 'both':
280
                    case 'xy':
281
                        str = xstr;
282
                        for (var i=0; i<ystrs.length; i++) {
283
                            str += opts.tooltipSeparator + ystrs[i];
284
                        }
285
                        break;
286
                    case 'yx':
287
                        str = '';
288
                        for (var i=0; i<ystrs.length; i++) {
289
                            str += ystrs[i] + opts.tooltipSeparator;
290
                        }
291
                        str += xstr;
292
                        break;
293
                    case 'x':
294
                        str = xstr;
295
                        break;
296
                    case 'y':
297
                        str = ystrs.join(opts.tooltipSeparator);
298
                        break;
299
                    default: // same as 'xy'
300
                        str = xstr;
301
                        for (var i=0; i<ystrs.length; i++) {
302
                            str += opts.tooltipSeparator + ystrs[i];
303
                        }
304
                        break;
305
 
306
                }
307
            }
308
        }
309
        else {
310
            var str;
311
            if (typeof opts.formatString ===  'string') {
312
                str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString].concat(neighbor.data));
313
            }
314
 
315
            else {
316
                if (opts.tooltipAxes == 'both' || opts.tooltipAxes == 'xy') {
317
                    str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]);
318
                }
319
                else if (opts.tooltipAxes == 'yx') {
320
                    str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]);
321
                }
322
                else if (opts.tooltipAxes == 'x') {
323
                    str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]);
324
                }
325
                else if (opts.tooltipAxes == 'y') {
326
                    str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]);
327
                }
328
            }
329
        }
330
        if ($.isFunction(opts.tooltipContentEditor)) {
331
            // args str, seriesIndex, pointIndex are essential so the hook can look up
332
            // extra data for the point.
333
            str = opts.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot);
334
        }
335
        elem.html(str);
336
        var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]};
337
        var ms = 0;
338
        var fact = 0.707;
339
        if (series.markerRenderer.show == true) {
340
            ms = (series.markerRenderer.size + opts.sizeAdjust)/2;
341
        }
342
 
343
        var loc = locations;
344
        if (series.fillToZero && series.fill && neighbor.data[1] < 0) {
345
          loc = oppositeLocations;
346
        }
347
 
348
        switch (loc[locationIndicies[opts.tooltipLocation]]) {
349
            case 'nw':
350
                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
351
                var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
352
                break;
353
            case 'n':
354
                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
355
                var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - ms;
356
                break;
357
            case 'ne':
358
                var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms;
359
                var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
360
                break;
361
            case 'e':
362
                var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + ms;
363
                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
364
                break;
365
            case 'se':
366
                var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms;
367
                var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms;
368
                break;
369
            case 's':
370
                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
371
                var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + ms;
372
                break;
373
            case 'sw':
374
                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
375
                var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms;
376
                break;
377
            case 'w':
378
                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - ms;
379
                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
380
                break;
381
            default: // same as 'nw'
382
                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
383
                var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
384
                break;
385
        }
386
        elem.css('left', x);
387
        elem.css('top', y);
388
        if (opts.fadeTooltip) {
389
            // Fix for stacked up animations.  Thnanks Trevor!
390
            elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed);
391
        }
392
        else {
393
            elem.show();
394
        }
395
        elem = null;
396
 
397
    }
398
 
399
    function handleMove(ev, gridpos, datapos, neighbor, plot) {
400
        var hl = plot.plugins.highlighter;
401
        var c = plot.plugins.cursor;
402
        if (hl.show) {
403
            if (neighbor == null && hl.isHighlighting) {
404
                var evt = jQuery.Event('jqplotHighlighterUnhighlight');
405
                plot.target.trigger(evt);
406
 
407
                var ctx = hl.highlightCanvas._ctx;
408
                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
409
                if (hl.fadeTooltip) {
410
                    hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed);
411
                }
412
                else {
413
                    hl._tooltipElem.hide();
414
                }
415
                if (hl.bringSeriesToFront) {
416
                    plot.restorePreviousSeriesOrder();
417
                }
418
                hl.isHighlighting = false;
419
                hl.currentNeighbor = null;
420
                ctx = null;
421
            }
422
            else if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) {
423
                var evt = jQuery.Event('jqplotHighlighterHighlight');
424
                evt.which = ev.which;
425
                evt.pageX = ev.pageX;
426
                evt.pageY = ev.pageY;
427
                var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data, plot];
428
                plot.target.trigger(evt, ins);
429
 
430
                hl.isHighlighting = true;
431
                hl.currentNeighbor = neighbor;
432
                if (hl.showMarker) {
433
                    draw(plot, neighbor);
434
                }
435
                if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) {
436
                    showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
437
                }
438
                if (hl.bringSeriesToFront) {
439
                    plot.moveSeriesToFront(neighbor.seriesIndex);
440
                }
441
            }
442
            // check to see if we're highlighting the wrong point.
443
            else if (neighbor != null && hl.isHighlighting && hl.currentNeighbor != neighbor) {
444
                // highlighting the wrong point.
445
 
446
                // if new series allows highlighting, highlight new point.
447
                if (plot.series[neighbor.seriesIndex].showHighlight) {
448
                    var ctx = hl.highlightCanvas._ctx;
449
                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
450
                    hl.isHighlighting = true;
451
                    hl.currentNeighbor = neighbor;
452
                    if (hl.showMarker) {
453
                        draw(plot, neighbor);
454
                    }
455
                    if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) {
456
                        showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
457
                    }
458
                    if (hl.bringSeriesToFront) {
459
                        plot.moveSeriesToFront(neighbor.seriesIndex);
460
                    }
461
                }
462
            }
463
        }
464
    }
465
})(jQuery);