You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
22 KiB
484 lines
22 KiB
/** |
|
* jqPlot |
|
* Pure JavaScript plotting plugin using jQuery |
|
* |
|
* Version: 1.0.9 |
|
* Revision: dff2f04 |
|
* |
|
* Copyright (c) 2009-2016 Chris Leonello |
|
* jqPlot is currently available for use in all personal or commercial projects |
|
* under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL |
|
* version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can |
|
* choose the license that best suits your project and use it accordingly. |
|
* |
|
* Although not required, the author would appreciate an email letting him |
|
* know of any substantial use of jqPlot. You can reach the author at: |
|
* chris at jqplot dot com or see http://www.jqplot.com/info.php . |
|
* |
|
* If you are feeling kind and generous, consider supporting the project by |
|
* making a donation at: http://www.jqplot.com/donate.php . |
|
* |
|
* sprintf functions contained in jqplot.sprintf.js by Ash Searle: |
|
* |
|
* version 2007.04.27 |
|
* author Ash Searle |
|
* http://hexmen.com/blog/2007/03/printf-sprintf/ |
|
* http://hexmen.com/js/sprintf.js |
|
* The author (Ash Searle) has placed this code in the public domain: |
|
* "This code is unrestricted: you are free to use it however you like." |
|
* |
|
*/ |
|
(function($) { |
|
$.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]); |
|
|
|
/** |
|
* Class: $.jqplot.Highlighter |
|
* Plugin which will highlight data points when they are moused over. |
|
* |
|
* To use this plugin, include the js |
|
* file in your source: |
|
* |
|
* > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script> |
|
* |
|
* A tooltip providing information about the data point is enabled by default. |
|
* To disable the tooltip, set "showTooltip" to false. |
|
* |
|
* You can control what data is displayed in the tooltip with various |
|
* options. The "tooltipAxes" option controls whether the x, y or both |
|
* data values are displayed. |
|
* |
|
* Some chart types (e.g. hi-low-close) have more than one y value per |
|
* data point. To display the additional values in the tooltip, set the |
|
* "yvalues" option to the desired number of y values present (3 for a hlc chart). |
|
* |
|
* By default, data values will be formatted with the same formatting |
|
* specifiers as used to format the axis ticks. A custom format code |
|
* can be supplied with the tooltipFormatString option. This will apply |
|
* to all values in the tooltip. |
|
* |
|
* For more complete control, the "formatString" option can be set. This |
|
* Allows conplete control over tooltip formatting. Values are passed to |
|
* the format string in an order determined by the "tooltipAxes" and "yvalues" |
|
* options. So, if you have a hi-low-close chart and you just want to display |
|
* the hi-low-close values in the tooltip, you could set a formatString like: |
|
* |
|
* > highlighter: { |
|
* > tooltipAxes: 'y', |
|
* > yvalues: 3, |
|
* > formatString:'<table class="jqplot-highlighter"> |
|
* > <tr><td>hi:</td><td>%s</td></tr> |
|
* > <tr><td>low:</td><td>%s</td></tr> |
|
* > <tr><td>close:</td><td>%s</td></tr></table>' |
|
* > } |
|
* |
|
*/ |
|
$.jqplot.Highlighter = function(options) { |
|
// Group: Properties |
|
// |
|
//prop: show |
|
// true to show the highlight. |
|
this.show = $.jqplot.config.enablePlugins; |
|
// prop: markerRenderer |
|
// Renderer used to draw the marker of the highlighted point. |
|
// Renderer will assimilate attributes from the data point being highlighted, |
|
// so no attributes need set on the renderer directly. |
|
// Default is to turn off shadow drawing on the highlighted point. |
|
this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false}); |
|
// prop: showMarker |
|
// true to show the marker |
|
this.showMarker = true; |
|
// prop: lineWidthAdjust |
|
// Pixels to add to the lineWidth of the highlight. |
|
this.lineWidthAdjust = 2.5; |
|
// prop: sizeAdjust |
|
// Pixels to add to the overall size of the highlight. |
|
this.sizeAdjust = 5; |
|
// prop: showTooltip |
|
// Show a tooltip with data point values. |
|
this.showTooltip = true; |
|
// prop: tooltipLocation |
|
// Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' |
|
this.tooltipLocation = 'nw'; |
|
// prop: fadeTooltip |
|
// true = fade in/out tooltip, flase = show/hide tooltip |
|
this.fadeTooltip = true; |
|
// prop: tooltipFadeSpeed |
|
// 'slow', 'def', 'fast', or number of milliseconds. |
|
this.tooltipFadeSpeed = "fast"; |
|
// prop: tooltipOffset |
|
// Pixel offset of tooltip from the highlight. |
|
this.tooltipOffset = 2; |
|
// prop: tooltipAxes |
|
// Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx' |
|
// 'both' and 'xy' are equivalent, 'yx' reverses order of labels. |
|
this.tooltipAxes = 'both'; |
|
// prop; tooltipSeparator |
|
// String to use to separate x and y axes in tooltip. |
|
this.tooltipSeparator = ', '; |
|
// prop; tooltipContentEditor |
|
// Function used to edit/augment/replace the formatted tooltip contents. |
|
// Called as str = tooltipContentEditor(str, seriesIndex, pointIndex) |
|
// where str is the generated tooltip html and seriesIndex and pointIndex identify |
|
// the data point being highlighted. Should return the html for the tooltip contents. |
|
this.tooltipContentEditor = null; |
|
// prop: useAxesFormatters |
|
// Use the x and y axes formatters to format the text in the tooltip. |
|
this.useAxesFormatters = true; |
|
// prop: tooltipFormatString |
|
// sprintf format string for the tooltip. |
|
// Uses Ash Searle's javascript sprintf implementation |
|
// found here: http://hexmen.com/blog/2007/03/printf-sprintf/ |
|
// See http://perldoc.perl.org/functions/sprintf.html for reference. |
|
// Additional "p" and "P" format specifiers added by Chris Leonello. |
|
this.tooltipFormatString = '%.5P'; |
|
// prop: formatString |
|
// alternative to tooltipFormatString |
|
// will format the whole tooltip text, populating with x, y values as |
|
// indicated by tooltipAxes option. So, you could have a tooltip like: |
|
// 'Date: %s, number of cats: %d' to format the whole tooltip at one go. |
|
// If useAxesFormatters is true, values will be formatted according to |
|
// Axes formatters and you can populate your tooltip string with |
|
// %s placeholders. |
|
this.formatString = null; |
|
// prop: yvalues |
|
// Number of y values to expect in the data point array. |
|
// Typically this is 1. Certain plots, like OHLC, will |
|
// have more y values in each data point array. |
|
this.yvalues = 1; |
|
// prop: bringSeriesToFront |
|
// This option requires jQuery 1.4+ |
|
// True to bring the series of the highlighted point to the front |
|
// of other series. |
|
this.bringSeriesToFront = false; |
|
this._tooltipElem; |
|
this.isHighlighting = false; |
|
this.currentNeighbor = null; |
|
|
|
$.extend(true, this, options); |
|
}; |
|
|
|
var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']; |
|
var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7}; |
|
var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e']; |
|
|
|
// axis.renderer.tickrenderer.formatter |
|
|
|
// called with scope of plot |
|
$.jqplot.Highlighter.init = function (target, data, opts){ |
|
var options = opts || {}; |
|
// add a highlighter attribute to the plot |
|
this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter); |
|
}; |
|
|
|
// called within scope of series |
|
$.jqplot.Highlighter.parseOptions = function (defaults, options) { |
|
// Add a showHighlight option to the series |
|
// and set it to true by default. |
|
this.showHighlight = true; |
|
}; |
|
|
|
// called within context of plot |
|
// create a canvas which we can draw on. |
|
// insert it before the eventCanvas, so eventCanvas will still capture events. |
|
$.jqplot.Highlighter.postPlotDraw = function() { |
|
// Memory Leaks patch |
|
if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) { |
|
this.plugins.highlighter.highlightCanvas.resetCanvas(); |
|
this.plugins.highlighter.highlightCanvas = null; |
|
} |
|
|
|
if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) { |
|
this.plugins.highlighter._tooltipElem.emptyForce(); |
|
this.plugins.highlighter._tooltipElem = null; |
|
} |
|
|
|
this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas(); |
|
|
|
this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this)); |
|
this.plugins.highlighter.highlightCanvas.setContext(); |
|
|
|
var elem = document.createElement('div'); |
|
this.plugins.highlighter._tooltipElem = $(elem); |
|
elem = null; |
|
this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip'); |
|
this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'}); |
|
|
|
this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem); |
|
}; |
|
|
|
$.jqplot.preInitHooks.push($.jqplot.Highlighter.init); |
|
$.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions); |
|
$.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw); |
|
|
|
function draw(plot, neighbor) { |
|
var hl = plot.plugins.highlighter; |
|
var s = plot.series[neighbor.seriesIndex]; |
|
var smr = s.markerRenderer; |
|
var mr = hl.markerRenderer; |
|
mr.style = smr.style; |
|
mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust; |
|
mr.size = smr.size + hl.sizeAdjust; |
|
var rgba = $.jqplot.getColorComponents(smr.color); |
|
var newrgb = [rgba[0], rgba[1], rgba[2]]; |
|
var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]); |
|
mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')'; |
|
mr.init(); |
|
var x_pos = s.gridData[neighbor.pointIndex][0]; |
|
var y_pos = s.gridData[neighbor.pointIndex][1]; |
|
// Adjusting with s._barNudge |
|
if (s.renderer.constructor == $.jqplot.BarRenderer) { |
|
if (s.barDirection == "vertical") { |
|
x_pos += s._barNudge; |
|
} |
|
else { |
|
y_pos -= s._barNudge; |
|
} |
|
} |
|
mr.draw(x_pos, y_pos, hl.highlightCanvas._ctx); |
|
} |
|
|
|
function showTooltip(plot, series, neighbor) { |
|
// neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]} |
|
// gridData should be x,y pixel coords on the grid. |
|
// add the plot._gridPadding to that to get x,y in the target. |
|
var hl = plot.plugins.highlighter; |
|
var elem = hl._tooltipElem; |
|
var serieshl = series.highlighter || {}; |
|
|
|
var opts = $.extend(true, {}, hl, serieshl); |
|
|
|
if (opts.useAxesFormatters) { |
|
var xf = series._xaxis._ticks[0].formatter; |
|
var yf = series._yaxis._ticks[0].formatter; |
|
var xfstr = series._xaxis._ticks[0].formatString; |
|
var yfstr = series._yaxis._ticks[0].formatString; |
|
var str; |
|
var xstr = xf(xfstr, neighbor.data[0]); |
|
var ystrs = []; |
|
for (var i=1; i<opts.yvalues+1; i++) { |
|
ystrs.push(yf(yfstr, neighbor.data[i])); |
|
} |
|
if (typeof opts.formatString === 'string') { |
|
switch (opts.tooltipAxes) { |
|
case 'both': |
|
case 'xy': |
|
ystrs.unshift(xstr); |
|
ystrs.unshift(opts.formatString); |
|
str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); |
|
break; |
|
case 'yx': |
|
ystrs.push(xstr); |
|
ystrs.unshift(opts.formatString); |
|
str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); |
|
break; |
|
case 'x': |
|
str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString, xstr]); |
|
break; |
|
case 'y': |
|
ystrs.unshift(opts.formatString); |
|
str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); |
|
break; |
|
default: // same as xy |
|
ystrs.unshift(xstr); |
|
ystrs.unshift(opts.formatString); |
|
str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); |
|
break; |
|
} |
|
} |
|
else { |
|
switch (opts.tooltipAxes) { |
|
case 'both': |
|
case 'xy': |
|
str = xstr; |
|
for (var i=0; i<ystrs.length; i++) { |
|
str += opts.tooltipSeparator + ystrs[i]; |
|
} |
|
break; |
|
case 'yx': |
|
str = ''; |
|
for (var i=0; i<ystrs.length; i++) { |
|
str += ystrs[i] + opts.tooltipSeparator; |
|
} |
|
str += xstr; |
|
break; |
|
case 'x': |
|
str = xstr; |
|
break; |
|
case 'y': |
|
str = ystrs.join(opts.tooltipSeparator); |
|
break; |
|
default: // same as 'xy' |
|
str = xstr; |
|
for (var i=0; i<ystrs.length; i++) { |
|
str += opts.tooltipSeparator + ystrs[i]; |
|
} |
|
break; |
|
|
|
} |
|
} |
|
} |
|
else { |
|
var str; |
|
if (typeof opts.formatString === 'string') { |
|
str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString].concat(neighbor.data)); |
|
} |
|
|
|
else { |
|
if (opts.tooltipAxes == 'both' || opts.tooltipAxes == 'xy') { |
|
str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]); |
|
} |
|
else if (opts.tooltipAxes == 'yx') { |
|
str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]); |
|
} |
|
else if (opts.tooltipAxes == 'x') { |
|
str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]); |
|
} |
|
else if (opts.tooltipAxes == 'y') { |
|
str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]); |
|
} |
|
} |
|
} |
|
if ($.isFunction(opts.tooltipContentEditor)) { |
|
// args str, seriesIndex, pointIndex are essential so the hook can look up |
|
// extra data for the point. |
|
str = opts.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot); |
|
} |
|
elem.html(str); |
|
var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]}; |
|
var ms = 0; |
|
var fact = 0.707; |
|
if (series.markerRenderer.show == true) { |
|
ms = (series.markerRenderer.size + opts.sizeAdjust)/2; |
|
} |
|
|
|
var loc = locations; |
|
if (series.fillToZero && series.fill && neighbor.data[1] < 0) { |
|
loc = oppositeLocations; |
|
} |
|
|
|
switch (loc[locationIndicies[opts.tooltipLocation]]) { |
|
case 'nw': |
|
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; |
|
var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; |
|
break; |
|
case 'n': |
|
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; |
|
var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - ms; |
|
break; |
|
case 'ne': |
|
var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms; |
|
var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; |
|
break; |
|
case 'e': |
|
var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + ms; |
|
var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; |
|
break; |
|
case 'se': |
|
var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms; |
|
var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms; |
|
break; |
|
case 's': |
|
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; |
|
var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + ms; |
|
break; |
|
case 'sw': |
|
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; |
|
var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms; |
|
break; |
|
case 'w': |
|
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - ms; |
|
var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; |
|
break; |
|
default: // same as 'nw' |
|
var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; |
|
var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; |
|
break; |
|
} |
|
if (series.renderer.constructor == $.jqplot.BarRenderer) { |
|
if (series.barDirection == 'vertical') { |
|
x += series._barNudge; |
|
} |
|
else { |
|
y -= series._barNudge; |
|
} |
|
} |
|
elem.css('left', x); |
|
elem.css('top', y); |
|
if (opts.fadeTooltip) { |
|
// Fix for stacked up animations. Thnanks Trevor! |
|
elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed); |
|
} |
|
else { |
|
elem.show(); |
|
} |
|
elem = null; |
|
|
|
} |
|
|
|
function handleMove(ev, gridpos, datapos, neighbor, plot) { |
|
var hl = plot.plugins.highlighter; |
|
var c = plot.plugins.cursor; |
|
if (hl.show) { |
|
if (neighbor == null && hl.isHighlighting) { |
|
var evt = jQuery.Event('jqplotHighlighterUnhighlight'); |
|
plot.target.trigger(evt); |
|
|
|
var ctx = hl.highlightCanvas._ctx; |
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); |
|
if (hl.fadeTooltip) { |
|
hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed); |
|
} |
|
else { |
|
hl._tooltipElem.hide(); |
|
} |
|
if (hl.bringSeriesToFront) { |
|
plot.restorePreviousSeriesOrder(); |
|
} |
|
hl.isHighlighting = false; |
|
hl.currentNeighbor = null; |
|
ctx = null; |
|
} |
|
else if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) { |
|
var evt = jQuery.Event('jqplotHighlighterHighlight'); |
|
evt.which = ev.which; |
|
evt.pageX = ev.pageX; |
|
evt.pageY = ev.pageY; |
|
var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data, plot]; |
|
plot.target.trigger(evt, ins); |
|
|
|
hl.isHighlighting = true; |
|
hl.currentNeighbor = neighbor; |
|
if (hl.showMarker) { |
|
draw(plot, neighbor); |
|
} |
|
if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) { |
|
showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor); |
|
} |
|
if (hl.bringSeriesToFront) { |
|
plot.moveSeriesToFront(neighbor.seriesIndex); |
|
} |
|
} |
|
// check to see if we're highlighting the wrong point. |
|
else if (neighbor != null && hl.isHighlighting && hl.currentNeighbor != neighbor) { |
|
// highlighting the wrong point. |
|
|
|
// if new series allows highlighting, highlight new point. |
|
if (plot.series[neighbor.seriesIndex].showHighlight) { |
|
var ctx = hl.highlightCanvas._ctx; |
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); |
|
hl.isHighlighting = true; |
|
hl.currentNeighbor = neighbor; |
|
if (hl.showMarker) { |
|
draw(plot, neighbor); |
|
} |
|
if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) { |
|
showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor); |
|
} |
|
if (hl.bringSeriesToFront) { |
|
plot.moveSeriesToFront(neighbor.seriesIndex); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
})(jQuery); |