4 * description: easy-to-use tooltips for Flot charts
6 * author: Krzysztof Urbas @krzysu [myviews.pl]
7 * website: https://github.com/krzysu/flot.tooltip
10 * released under MIT License, 2012
15 // plugin options, default values
16 var defaultOptions = {
19 content: "%s | X: %x | Y: %y",
20 // allowed templates are:
21 // %s -> series label,
24 // %x.2 -> precision of X value,
35 onHover: function(flotItem, $tooltipEl) {}
40 var FlotTooltip = function(plot) {
43 this.tipPosition = {x: 0, y: 0};
48 // main plugin function
49 FlotTooltip.prototype.init = function(plot) {
53 plot.hooks.bindEvents.push(function (plot, eventHolder) {
56 that.plotOptions = plot.getOptions();
58 // if not enabled return
59 if (that.plotOptions.tooltip === false || typeof that.plotOptions.tooltip === 'undefined') return;
61 // shortcut to access tooltip options
62 that.tooltipOptions = that.plotOptions.tooltipOpts;
64 // create tooltip DOM element
65 var $tip = that.getDomElement();
68 $( plot.getPlaceholder() ).bind("plothover", function (event, pos, item) {
72 // convert tooltip content template to real tipText
73 tipText = that.stringFormat(that.tooltipOptions.content, item);
77 left: that.tipPosition.x + that.tooltipOptions.shifts.x,
78 top: that.tipPosition.y + that.tooltipOptions.shifts.y
83 if(typeof that.tooltipOptions.onHover === 'function') {
84 that.tooltipOptions.onHover(item, $tip);
92 eventHolder.mousemove( function(e) {
96 that.updateTooltipPosition(pos);
102 * get or create tooltip DOM element
103 * @return jQuery object
105 FlotTooltip.prototype.getDomElement = function() {
108 if( $('#flotTip').length > 0 ){
109 $tip = $('#flotTip');
112 $tip = $('<div />').attr('id', 'flotTip');
113 $tip.appendTo('body').hide().css({position: 'absolute'});
115 if(this.tooltipOptions.defaultTheme) {
117 'background': '#fff',
119 'padding': '0.4em 0.6em',
120 'border-radius': '0.5em',
121 'font-size': '0.8em',
122 'border': '1px solid #111'
131 FlotTooltip.prototype.updateTooltipPosition = function(pos) {
132 this.tipPosition.x = pos.x;
133 this.tipPosition.y = pos.y;
137 * core function, create tooltip content
138 * @param {string} content - template with tooltip content
139 * @param {object} item - Flot item
140 * @return {string} real tooltip content for current item
142 FlotTooltip.prototype.stringFormat = function(content, item) {
144 var percentPattern = /%p\.{0,1}(\d{0,})/;
145 var seriesPattern = /%s/;
146 var xPattern = /%x\.{0,1}(\d{0,})/;
147 var yPattern = /%y\.{0,1}(\d{0,})/;
149 // if it is a function callback get the content string
150 if( typeof(content) === 'function' ) {
151 content = content(item.series.data[item.dataIndex][0], item.series.data[item.dataIndex][1]);
154 // percent match for pie charts
155 if( typeof (item.series.percent) !== 'undefined' ) {
156 content = this.adjustValPrecision(percentPattern, content, item.series.percent);
160 if( typeof(item.series.label) !== 'undefined' ) {
161 content = content.replace(seriesPattern, item.series.label);
164 // time mode axes with custom dateFormat
165 if(this.isTimeMode('xaxis', item) && this.isXDateFormat(item)) {
166 content = content.replace(xPattern, this.timestampToDate(item.series.data[item.dataIndex][0], this.tooltipOptions.xDateFormat));
169 if(this.isTimeMode('yaxis', item) && this.isYDateFormat(item)) {
170 content = content.replace(yPattern, this.timestampToDate(item.series.data[item.dataIndex][1], this.tooltipOptions.yDateFormat));
173 // set precision if defined
174 if( typeof item.series.data[item.dataIndex][0] === 'number' ) {
175 content = this.adjustValPrecision(xPattern, content, item.series.data[item.dataIndex][0]);
177 if( typeof item.series.data[item.dataIndex][1] === 'number' ) {
178 content = this.adjustValPrecision(yPattern, content, item.series.data[item.dataIndex][1]);
181 // if no value customization, use tickFormatter by default
182 if(typeof item.series.xaxis.tickFormatter !== 'undefined') {
183 content = content.replace(xPattern, item.series.xaxis.tickFormatter(item.series.data[item.dataIndex][0], item.series.xaxis));
185 if(typeof item.series.yaxis.tickFormatter !== 'undefined') {
186 content = content.replace(yPattern, item.series.yaxis.tickFormatter(item.series.data[item.dataIndex][1], item.series.yaxis));
192 // helpers just for readability
193 FlotTooltip.prototype.isTimeMode = function(axisName, item) {
194 return (typeof item.series[axisName].options.mode !== 'undefined' && item.series[axisName].options.mode === 'time');
197 FlotTooltip.prototype.isXDateFormat = function(item) {
198 return (typeof this.tooltipOptions.xDateFormat !== 'undefined' && this.tooltipOptions.xDateFormat !== null);
201 FlotTooltip.prototype.isYDateFormat = function(item) {
202 return (typeof this.tooltipOptions.yDateFormat !== 'undefined' && this.tooltipOptions.yDateFormat !== null);
206 FlotTooltip.prototype.timestampToDate = function(tmst, dateFormat) {
207 var theDate = new Date(tmst);
208 return $.plot.formatDate(theDate, dateFormat);
212 FlotTooltip.prototype.adjustValPrecision = function(pattern, content, value) {
215 if( content.match(pattern) !== null ) {
216 if(RegExp.$1 !== '') {
217 precision = RegExp.$1;
218 value = value.toFixed(precision);
220 // only replace content if precision exists
221 content = content.replace(pattern, value);
228 // to have controll over existing instances
229 var tooltipsCollection = [];
232 var init = function(plot) {
233 tooltipsCollection.push( new FlotTooltip(plot) );
236 // define Flot plugin
237 $.plot.plugins.push({
239 options: defaultOptions,