PluginProbe
HAL / 2.0.2
HAL v2.0.2
trunk 1.0 1.1 1.2 1.3 1.4 1.4.2 1.4.3 1.4.4 2.0 2.0.1 2.0.10 2.0.2 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.2 2.3 2.4 2.4.1 2.4.2 2.5 All 30 releases
hal / js / jqplot.highlighter.js

jqplot.highlighter.js in HAL 2.0.2, at js/jqplot.highlighter.js

465 lines 21.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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);