mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into panel_edit_menu_poc
This commit is contained in:
commit
022cbdda31
@ -1,4 +1,6 @@
|
||||
# 1.9.0 (unreleased)
|
||||
- [Issue #877](https://github.com/grafana/grafana/issues/877). Graph: Smart auto decimal precision when using scaled unit formats
|
||||
- [Issue #850](https://github.com/grafana/grafana/issues/850). Graph: Shared tooltip that shows multiple series & crosshair line, thx @toni-moreno
|
||||
|
||||
# 1.8.1 (unreleased)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"version": "1.8.0",
|
||||
"url": "http://grafanarel.s3.amazonaws.com/grafana-1.8.0.tar.gz"
|
||||
"version": "1.8.1",
|
||||
"url": "http://grafanarel.s3.amazonaws.com/grafana-1.8.1.tar.gz"
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"company": "Coding Instinct AB"
|
||||
},
|
||||
"name": "grafana",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/torkelo/grafana.git"
|
||||
|
@ -7,6 +7,7 @@ function($, _, moment) {
|
||||
'use strict';
|
||||
|
||||
var kbn = {};
|
||||
kbn.valueFormats = {};
|
||||
|
||||
kbn.round_interval = function(interval) {
|
||||
switch (true) {
|
||||
@ -309,240 +310,27 @@ function($, _, moment) {
|
||||
].join(';') + '"></div>';
|
||||
};
|
||||
|
||||
kbn.byteFormat = function(size, decimals) {
|
||||
var ext, steps = 0;
|
||||
|
||||
if(_.isUndefined(decimals)) {
|
||||
decimals = 2;
|
||||
} else if (decimals === 0) {
|
||||
decimals = undefined;
|
||||
}
|
||||
|
||||
while (Math.abs(size) >= 1024) {
|
||||
steps++;
|
||||
size /= 1024;
|
||||
}
|
||||
|
||||
switch (steps) {
|
||||
case 0:
|
||||
ext = " B";
|
||||
break;
|
||||
case 1:
|
||||
ext = " KiB";
|
||||
break;
|
||||
case 2:
|
||||
ext = " MiB";
|
||||
break;
|
||||
case 3:
|
||||
ext = " GiB";
|
||||
break;
|
||||
case 4:
|
||||
ext = " TiB";
|
||||
break;
|
||||
case 5:
|
||||
ext = " PiB";
|
||||
break;
|
||||
case 6:
|
||||
ext = " EiB";
|
||||
break;
|
||||
case 7:
|
||||
ext = " ZiB";
|
||||
break;
|
||||
case 8:
|
||||
ext = " YiB";
|
||||
break;
|
||||
}
|
||||
|
||||
return (size.toFixed(decimals) + ext);
|
||||
kbn.valueFormats.percent = function(size, decimals) {
|
||||
return kbn.toFixed(size, decimals) + '%';
|
||||
};
|
||||
|
||||
kbn.bitFormat = function(size, decimals) {
|
||||
var ext, steps = 0;
|
||||
kbn.formatFuncCreator = function(factor, extArray) {
|
||||
return function(size, decimals, scaledDecimals) {
|
||||
var steps = 0;
|
||||
|
||||
if(_.isUndefined(decimals)) {
|
||||
decimals = 2;
|
||||
} else if (decimals === 0) {
|
||||
decimals = undefined;
|
||||
}
|
||||
while (Math.abs(size) >= factor) {
|
||||
steps++;
|
||||
size /= factor;
|
||||
}
|
||||
if (steps > 0) {
|
||||
decimals = scaledDecimals + (3 * steps);
|
||||
}
|
||||
|
||||
while (Math.abs(size) >= 1024) {
|
||||
steps++;
|
||||
size /= 1024;
|
||||
}
|
||||
|
||||
switch (steps) {
|
||||
case 0:
|
||||
ext = " b";
|
||||
break;
|
||||
case 1:
|
||||
ext = " Kib";
|
||||
break;
|
||||
case 2:
|
||||
ext = " Mib";
|
||||
break;
|
||||
case 3:
|
||||
ext = " Gib";
|
||||
break;
|
||||
case 4:
|
||||
ext = " Tib";
|
||||
break;
|
||||
case 5:
|
||||
ext = " Pib";
|
||||
break;
|
||||
case 6:
|
||||
ext = " Eib";
|
||||
break;
|
||||
case 7:
|
||||
ext = " Zib";
|
||||
break;
|
||||
case 8:
|
||||
ext = " Yib";
|
||||
break;
|
||||
}
|
||||
|
||||
return (size.toFixed(decimals) + ext);
|
||||
return kbn.toFixed(size, decimals) + extArray[steps];
|
||||
};
|
||||
};
|
||||
|
||||
kbn.bpsFormat = function(size, decimals) {
|
||||
var ext, steps = 0;
|
||||
|
||||
if(_.isUndefined(decimals)) {
|
||||
decimals = 2;
|
||||
} else if (decimals === 0) {
|
||||
decimals = undefined;
|
||||
}
|
||||
|
||||
while (Math.abs(size) >= 1000) {
|
||||
steps++;
|
||||
size /= 1000;
|
||||
}
|
||||
|
||||
switch (steps) {
|
||||
case 0:
|
||||
ext = " bps";
|
||||
break;
|
||||
case 1:
|
||||
ext = " Kbps";
|
||||
break;
|
||||
case 2:
|
||||
ext = " Mbps";
|
||||
break;
|
||||
case 3:
|
||||
ext = " Gbps";
|
||||
break;
|
||||
case 4:
|
||||
ext = " Tbps";
|
||||
break;
|
||||
case 5:
|
||||
ext = " Pbps";
|
||||
break;
|
||||
case 6:
|
||||
ext = " Ebps";
|
||||
break;
|
||||
case 7:
|
||||
ext = " Zbps";
|
||||
break;
|
||||
case 8:
|
||||
ext = " Ybps";
|
||||
break;
|
||||
}
|
||||
|
||||
return (size.toFixed(decimals) + ext);
|
||||
};
|
||||
|
||||
kbn.shortFormat = function(size, decimals) {
|
||||
var ext, steps = 0;
|
||||
|
||||
if(_.isUndefined(decimals)) {
|
||||
decimals = 2;
|
||||
} else if (decimals === 0) {
|
||||
decimals = undefined;
|
||||
}
|
||||
|
||||
while (Math.abs(size) >= 1000) {
|
||||
steps++;
|
||||
size /= 1000;
|
||||
}
|
||||
|
||||
switch (steps) {
|
||||
case 0:
|
||||
ext = "";
|
||||
break;
|
||||
case 1:
|
||||
ext = " K";
|
||||
break;
|
||||
case 2:
|
||||
ext = " Mil";
|
||||
break;
|
||||
case 3:
|
||||
ext = " Bil";
|
||||
break;
|
||||
case 4:
|
||||
ext = " Tri";
|
||||
break;
|
||||
case 5:
|
||||
ext = " Quadr";
|
||||
break;
|
||||
case 6:
|
||||
ext = " Quint";
|
||||
break;
|
||||
case 7:
|
||||
ext = " Sext";
|
||||
break;
|
||||
case 8:
|
||||
ext = " Sept";
|
||||
break;
|
||||
}
|
||||
|
||||
return (size.toFixed(decimals) + ext);
|
||||
};
|
||||
|
||||
kbn.getFormatFunction = function(formatName, decimals) {
|
||||
switch(formatName) {
|
||||
case 'short':
|
||||
return function(val) {
|
||||
return kbn.shortFormat(val, decimals);
|
||||
};
|
||||
case 'bytes':
|
||||
return function(val) {
|
||||
return kbn.byteFormat(val, decimals);
|
||||
};
|
||||
case 'bits':
|
||||
return function(val) {
|
||||
return kbn.bitFormat(val, decimals);
|
||||
};
|
||||
case 'bps':
|
||||
return function(val) {
|
||||
return kbn.bpsFormat(val, decimals);
|
||||
};
|
||||
case 's':
|
||||
return function(val) {
|
||||
return kbn.sFormat(val, decimals);
|
||||
};
|
||||
case 'ms':
|
||||
return function(val) {
|
||||
return kbn.msFormat(val, decimals);
|
||||
};
|
||||
case 'µs':
|
||||
return function(val) {
|
||||
return kbn.microsFormat(val, decimals);
|
||||
};
|
||||
case 'ns':
|
||||
return function(val) {
|
||||
return kbn.nanosFormat(val, decimals);
|
||||
};
|
||||
case 'percent':
|
||||
return function(val, axis) {
|
||||
return kbn.noneFormat(val, axis ? axis.tickDecimals : null) + ' %';
|
||||
};
|
||||
default:
|
||||
return function(val, axis) {
|
||||
return kbn.noneFormat(val, axis ? axis.tickDecimals : null);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
kbn.noneFormat = function(value, decimals) {
|
||||
kbn.toFixed = function(value, decimals) {
|
||||
var factor = decimals ? Math.pow(10, decimals) : 1;
|
||||
var formatted = String(Math.round(value * factor) / factor);
|
||||
|
||||
@ -553,7 +341,6 @@ function($, _, moment) {
|
||||
|
||||
// If tickDecimals was specified, ensure that we have exactly that
|
||||
// much precision; otherwise default to the value's own precision.
|
||||
|
||||
if (decimals != null) {
|
||||
var decimalPos = formatted.indexOf(".");
|
||||
var precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1;
|
||||
@ -565,97 +352,87 @@ function($, _, moment) {
|
||||
return formatted;
|
||||
};
|
||||
|
||||
kbn.msFormat = function(size, decimals) {
|
||||
// Less than 1 milli, downscale to micro
|
||||
if (size !== 0 && Math.abs(size) < 1) {
|
||||
return kbn.microsFormat(size * 1000, decimals);
|
||||
}
|
||||
else if (Math.abs(size) < 1000) {
|
||||
return size.toFixed(decimals) + " ms";
|
||||
kbn.valueFormats.bits = kbn.formatFuncCreator(1024, [' b', ' Kib', ' Mib', ' Gib', ' Tib', ' Pib', ' Eib', ' Zib', ' Yib']);
|
||||
kbn.valueFormats.bytes = kbn.formatFuncCreator(1024, [' B', ' KiB', ' MiB', ' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', ' YiB']);
|
||||
kbn.valueFormats.bps = kbn.formatFuncCreator(1000, [' bps', ' Kbps', ' Mbps', ' Gbps', ' Tbps', ' Pbps', ' Ebps', ' Zbps', ' Ybps']);
|
||||
kbn.valueFormats.short = kbn.formatFuncCreator(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Qaudr', ' Quint', ' Sext', ' Sept']);
|
||||
kbn.valueFormats.none = kbn.toFixed;
|
||||
|
||||
kbn.valueFormats.ms = function(size, decimals, scaledDecimals) {
|
||||
if (Math.abs(size) < 1000) {
|
||||
return kbn.toFixed(size, decimals) + " ms";
|
||||
}
|
||||
// Less than 1 min
|
||||
else if (Math.abs(size) < 60000) {
|
||||
return (size / 1000).toFixed(decimals) + " s";
|
||||
return kbn.toFixed(size / 1000, scaledDecimals + 3) + " s";
|
||||
}
|
||||
// Less than 1 hour, devide in minutes
|
||||
else if (Math.abs(size) < 3600000) {
|
||||
return (size / 60000).toFixed(decimals) + " min";
|
||||
return kbn.toFixed(size / 60000, scaledDecimals + 5) + " min";
|
||||
}
|
||||
// Less than one day, devide in hours
|
||||
else if (Math.abs(size) < 86400000) {
|
||||
return (size / 3600000).toFixed(decimals) + " hour";
|
||||
return kbn.toFixed(size / 3600000, scaledDecimals + 7) + " hour";
|
||||
}
|
||||
// Less than one year, devide in days
|
||||
else if (Math.abs(size) < 31536000000) {
|
||||
return (size / 86400000).toFixed(decimals) + " day";
|
||||
return kbn.toFixed(size / 86400000, scaledDecimals + 8) + " day";
|
||||
}
|
||||
|
||||
return (size / 31536000000).toFixed(decimals) + " year";
|
||||
return kbn.toFixed(size / 31536000000, scaledDecimals + 10) + " year";
|
||||
};
|
||||
|
||||
kbn.sFormat = function(size, decimals) {
|
||||
// Less than 1 sec, downscale to milli
|
||||
if (size !== 0 && Math.abs(size) < 1) {
|
||||
return kbn.msFormat(size * 1000, decimals);
|
||||
}
|
||||
// Less than 10 min, use seconds
|
||||
else if (Math.abs(size) < 600) {
|
||||
return size.toFixed(decimals) + " s";
|
||||
kbn.valueFormats.s = function(size, decimals, scaledDecimals) {
|
||||
if (Math.abs(size) < 600) {
|
||||
return kbn.toFixed(size, decimals) + " s";
|
||||
}
|
||||
// Less than 1 hour, devide in minutes
|
||||
else if (Math.abs(size) < 3600) {
|
||||
return (size / 60).toFixed(decimals) + " min";
|
||||
return kbn.toFixed(size / 60, scaledDecimals + 1) + " min";
|
||||
}
|
||||
// Less than one day, devide in hours
|
||||
else if (Math.abs(size) < 86400) {
|
||||
return (size / 3600).toFixed(decimals) + " hour";
|
||||
return kbn.toFixed(size / 3600, scaledDecimals + 4) + " hour";
|
||||
}
|
||||
// Less than one week, devide in days
|
||||
else if (Math.abs(size) < 604800) {
|
||||
return (size / 86400).toFixed(decimals) + " day";
|
||||
return kbn.toFixed(size / 86400, scaledDecimals + 5) + " day";
|
||||
}
|
||||
// Less than one year, devide in week
|
||||
else if (Math.abs(size) < 31536000) {
|
||||
return (size / 604800).toFixed(decimals) + " week";
|
||||
return kbn.toFixed(size / 604800, scaledDecimals + 6) + " week";
|
||||
}
|
||||
|
||||
return (size / 3.15569e7).toFixed(decimals) + " year";
|
||||
return kbn.toFixed(size / 3.15569e7, scaledDecimals + 7) + " year";
|
||||
};
|
||||
|
||||
kbn.microsFormat = function(size, decimals) {
|
||||
// Less than 1 micro, downscale to nano
|
||||
if (size !== 0 && Math.abs(size) < 1) {
|
||||
return kbn.nanosFormat(size * 1000, decimals);
|
||||
}
|
||||
else if (Math.abs(size) < 1000) {
|
||||
return size.toFixed(decimals) + " µs";
|
||||
kbn.valueFormats['µs'] = function(size, decimals, scaledDecimals) {
|
||||
if (Math.abs(size) < 1000) {
|
||||
return kbn.toFixed(size, decimals) + " µs";
|
||||
}
|
||||
else if (Math.abs(size) < 1000000) {
|
||||
return (size / 1000).toFixed(decimals) + " ms";
|
||||
return kbn.toFixed(size / 1000, scaledDecimals + 3) + " ms";
|
||||
}
|
||||
else {
|
||||
return (size / 1000000).toFixed(decimals) + " s";
|
||||
return kbn.toFixed(size / 1000000, scaledDecimals + 6) + " s";
|
||||
}
|
||||
};
|
||||
|
||||
kbn.nanosFormat = function(size, decimals) {
|
||||
if (Math.abs(size) < 1) {
|
||||
return size.toFixed(decimals) + " ns";
|
||||
}
|
||||
else if (Math.abs(size) < 1000) {
|
||||
return size.toFixed(0) + " ns";
|
||||
kbn.valueFormats.ns = function(size, decimals, scaledDecimals) {
|
||||
if (Math.abs(size) < 1000) {
|
||||
return kbn.toFixed(size, decimals) + " ns";
|
||||
}
|
||||
else if (Math.abs(size) < 1000000) {
|
||||
return (size / 1000).toFixed(decimals) + " µs";
|
||||
return kbn.toFixed(size / 1000, scaledDecimals + 3) + " µs";
|
||||
}
|
||||
else if (Math.abs(size) < 1000000000) {
|
||||
return (size / 1000000).toFixed(decimals) + " ms";
|
||||
return kbn.toFixed(size / 1000000, scaledDecimals + 6) + " ms";
|
||||
}
|
||||
else if (Math.abs(size) < 60000000000){
|
||||
return (size / 1000000000).toFixed(decimals) + " s";
|
||||
return kbn.toFixed(size / 1000000000, scaledDecimals + 9) + " s";
|
||||
}
|
||||
else {
|
||||
return (size / 60000000000).toFixed(decimals) + " m";
|
||||
return kbn.toFixed(size / 60000000000, scaledDecimals + 12) + " m";
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -40,6 +40,7 @@ require.config({
|
||||
'jquery.flot.stack': '../vendor/jquery/jquery.flot.stack',
|
||||
'jquery.flot.stackpercent':'../vendor/jquery/jquery.flot.stackpercent',
|
||||
'jquery.flot.time': '../vendor/jquery/jquery.flot.time',
|
||||
'jquery.flot.crosshair': '../vendor/jquery/jquery.flot.crosshair',
|
||||
|
||||
modernizr: '../vendor/modernizr-2.6.1',
|
||||
|
||||
@ -83,6 +84,7 @@ require.config({
|
||||
'jquery.flot.stack': ['jquery', 'jquery.flot'],
|
||||
'jquery.flot.stackpercent':['jquery', 'jquery.flot'],
|
||||
'jquery.flot.time': ['jquery', 'jquery.flot'],
|
||||
'jquery.flot.crosshair':['jquery', 'jquery.flot'],
|
||||
'angular-cookies': ['angular'],
|
||||
'angular-dragdrop': ['jquery','jquery-ui','angular'],
|
||||
'angular-loader': ['angular'],
|
||||
|
@ -54,7 +54,7 @@ function (_, kbn) {
|
||||
}
|
||||
};
|
||||
|
||||
TimeSeries.prototype.getFlotPairs = function (fillStyle, yFormats) {
|
||||
TimeSeries.prototype.getFlotPairs = function (fillStyle) {
|
||||
var result = [];
|
||||
|
||||
this.color = this.info.color;
|
||||
@ -100,21 +100,21 @@ function (_, kbn) {
|
||||
}
|
||||
|
||||
if (result.length) {
|
||||
|
||||
this.info.avg = (this.info.total / result.length);
|
||||
this.info.current = result[result.length-1][1];
|
||||
|
||||
var formater = kbn.getFormatFunction(yFormats[this.yaxis - 1], 2);
|
||||
this.info.avg = this.info.avg != null ? formater(this.info.avg) : null;
|
||||
this.info.current = this.info.current != null ? formater(this.info.current) : null;
|
||||
this.info.min = this.info.min != null ? formater(this.info.min) : null;
|
||||
this.info.max = this.info.max != null ? formater(this.info.max) : null;
|
||||
this.info.total = this.info.total != null ? formater(this.info.total) : null;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
TimeSeries.prototype.updateLegendValues = function(formater, decimals, scaledDecimals) {
|
||||
this.info.avg = this.info.avg != null ? formater(this.info.avg, decimals, scaledDecimals) : null;
|
||||
this.info.current = this.info.current != null ? formater(this.info.current, decimals, scaledDecimals) : null;
|
||||
this.info.min = this.info.min != null ? formater(this.info.min, decimals, scaledDecimals) : null;
|
||||
this.info.max = this.info.max != null ? formater(this.info.max, decimals, scaledDecimals) : null;
|
||||
this.info.total = this.info.total != null ? formater(this.info.total, decimals, scaledDecimals) : null;
|
||||
};
|
||||
|
||||
return TimeSeries;
|
||||
|
||||
});
|
||||
|
@ -3,9 +3,10 @@ define([
|
||||
'jquery',
|
||||
'kbn',
|
||||
'moment',
|
||||
'lodash'
|
||||
'lodash',
|
||||
'./grafanaGraph.tooltip'
|
||||
],
|
||||
function (angular, $, kbn, moment, _) {
|
||||
function (angular, $, kbn, moment, _, graphTooltip) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.directives');
|
||||
@ -15,8 +16,8 @@ function (angular, $, kbn, moment, _) {
|
||||
restrict: 'A',
|
||||
template: '<div> </div>',
|
||||
link: function(scope, elem) {
|
||||
var data, annotations;
|
||||
var dashboard = scope.dashboard;
|
||||
var data, annotations;
|
||||
var legendSideLastValue = null;
|
||||
|
||||
scope.$on('refresh',function() {
|
||||
@ -80,6 +81,18 @@ function (angular, $, kbn, moment, _) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateLegendValues(plot) {
|
||||
var yaxis = plot.getYAxes();
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var series = data[i];
|
||||
var axis = yaxis[series.yaxis - 1];
|
||||
var formater = kbn.valueFormats[scope.panel.y_formats[series.yaxis - 1]];
|
||||
series.updateLegendValues(formater, axis.tickDecimals, axis.scaledDecimals);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Function for rendering panel
|
||||
function render_panel() {
|
||||
if (shouldAbortRender()) {
|
||||
@ -91,6 +104,7 @@ function (angular, $, kbn, moment, _) {
|
||||
|
||||
// Populate element
|
||||
var options = {
|
||||
hooks: { draw: [updateLegendValues] },
|
||||
legend: { show: false },
|
||||
series: {
|
||||
stackpercent: panel.stack ? panel.percentage : false,
|
||||
@ -113,7 +127,8 @@ function (angular, $, kbn, moment, _) {
|
||||
show: panel.points,
|
||||
fill: 1,
|
||||
fillColor: false,
|
||||
radius: panel.pointradius
|
||||
radius: panel.points ? panel.pointradius : 2
|
||||
// little points when highlight points
|
||||
},
|
||||
shadowSize: 1
|
||||
},
|
||||
@ -130,6 +145,9 @@ function (angular, $, kbn, moment, _) {
|
||||
selection: {
|
||||
mode: "x",
|
||||
color: '#666'
|
||||
},
|
||||
crosshair: {
|
||||
mode: panel.tooltip.shared ? "x" : null
|
||||
}
|
||||
};
|
||||
|
||||
@ -299,7 +317,9 @@ function (angular, $, kbn, moment, _) {
|
||||
}
|
||||
|
||||
function configureAxisMode(axis, format) {
|
||||
axis.tickFormatter = kbn.getFormatFunction(format, 1);
|
||||
axis.tickFormatter = function(val, axis) {
|
||||
return kbn.valueFormats[format](val, axis.tickDecimals, axis.scaledDecimals);
|
||||
};
|
||||
}
|
||||
|
||||
function time_format(interval, ticks, min, max) {
|
||||
@ -324,40 +344,6 @@ function (angular, $, kbn, moment, _) {
|
||||
return "%H:%M";
|
||||
}
|
||||
|
||||
var $tooltip = $('<div id="tooltip">');
|
||||
|
||||
elem.bind("plothover", function (event, pos, item) {
|
||||
var group, value, timestamp, seriesInfo, format;
|
||||
|
||||
if (item) {
|
||||
seriesInfo = item.series.info;
|
||||
format = scope.panel.y_formats[seriesInfo.yaxis - 1];
|
||||
|
||||
if (seriesInfo.alias) {
|
||||
group = '<small style="font-size:0.9em;">' +
|
||||
'<i class="icon-circle" style="color:'+item.series.color+';"></i>' + ' ' +
|
||||
seriesInfo.alias +
|
||||
'</small><br>';
|
||||
} else {
|
||||
group = kbn.query_color_dot(item.series.color, 15) + ' ';
|
||||
}
|
||||
|
||||
if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') {
|
||||
value = item.datapoint[1] - item.datapoint[2];
|
||||
}
|
||||
else {
|
||||
value = item.datapoint[1];
|
||||
}
|
||||
|
||||
value = kbn.getFormatFunction(format, 2)(value, item.series.yaxis);
|
||||
timestamp = dashboard.formatDate(item.datapoint[0]);
|
||||
|
||||
$tooltip.html(group + value + " @ " + timestamp).place_tt(pos.pageX, pos.pageY);
|
||||
} else {
|
||||
$tooltip.detach();
|
||||
}
|
||||
});
|
||||
|
||||
function render_panel_as_graphite_png(url) {
|
||||
url += '&width=' + elem.width();
|
||||
url += '&height=' + elem.css('height').replace('px', '');
|
||||
@ -408,6 +394,8 @@ function (angular, $, kbn, moment, _) {
|
||||
elem.html('<img src="' + url + '"></img>');
|
||||
}
|
||||
|
||||
graphTooltip.register(elem, dashboard, scope);
|
||||
|
||||
elem.bind("plotselected", function (event, ranges) {
|
||||
scope.$apply(function() {
|
||||
timeSrv.setTime({
|
||||
|
122
src/app/directives/grafanaGraph.tooltip.js
Normal file
122
src/app/directives/grafanaGraph.tooltip.js
Normal file
@ -0,0 +1,122 @@
|
||||
define([
|
||||
'jquery',
|
||||
'kbn',
|
||||
],
|
||||
function ($, kbn) {
|
||||
'use strict';
|
||||
|
||||
function registerTooltipFeatures(elem, dashboard, scope) {
|
||||
|
||||
var $tooltip = $('<div id="tooltip">');
|
||||
|
||||
elem.mouseleave(function () {
|
||||
if(scope.panel.tooltip.shared) {
|
||||
var plot = elem.data().plot;
|
||||
$tooltip.detach();
|
||||
plot.clearCrosshair();
|
||||
plot.unhighlight();
|
||||
}
|
||||
});
|
||||
|
||||
function findHoverIndex(posX, series) {
|
||||
for (var j = 0; j < series.data.length; j++) {
|
||||
if (series.data[j][0] > posX) {
|
||||
return Math.max(j - 1, 0);
|
||||
}
|
||||
}
|
||||
return j - 1;
|
||||
}
|
||||
|
||||
elem.bind("plothover", function (event, pos, item) {
|
||||
var plot = elem.data().plot;
|
||||
var data = plot.getData();
|
||||
var group, value, timestamp, seriesInfo, format, i, series, hoverIndex, seriesHtml;
|
||||
|
||||
if (scope.panel.tooltip.shared) {
|
||||
plot.unhighlight();
|
||||
|
||||
//check if all series has same length if so, only one x index will
|
||||
//be checked and only for exact timestamp values
|
||||
var pointCount = data[0].data.length;
|
||||
for (i = 1; i < data.length; i++) {
|
||||
if (data[i].data.length !== pointCount) {
|
||||
console.log('WARNING: tootltip shared can not be shown becouse of series points do not align, different point counts');
|
||||
$tooltip.detach();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
seriesHtml = '';
|
||||
series = data[0];
|
||||
hoverIndex = findHoverIndex(pos.x, series);
|
||||
|
||||
//now we know the current X (j) position for X and Y values
|
||||
timestamp = dashboard.formatDate(series.data[hoverIndex][0]);
|
||||
var last_value = 0; //needed for stacked values
|
||||
|
||||
for (i = data.length-1; i >= 0; --i) {
|
||||
//stacked values should be added in reverse order
|
||||
series = data[i];
|
||||
seriesInfo = series.info;
|
||||
format = scope.panel.y_formats[seriesInfo.yaxis - 1];
|
||||
|
||||
if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') {
|
||||
value = series.data[hoverIndex][1];
|
||||
} else {
|
||||
last_value += series.data[hoverIndex][1];
|
||||
value = last_value;
|
||||
}
|
||||
|
||||
value = kbn.valueFormats[format](value, series.yaxis.tickDecimals);
|
||||
|
||||
if (seriesInfo.alias) {
|
||||
group = '<i class="icon-minus" style="color:' + series.color +';"></i> ' + seriesInfo.alias;
|
||||
} else {
|
||||
group = kbn.query_color_dot(series.color, 15) + ' ';
|
||||
}
|
||||
|
||||
//pre-pending new values
|
||||
seriesHtml = group + ': <span class="graph-tooltip-value">' + value + '</span><br>' + seriesHtml;
|
||||
|
||||
plot.highlight(i, hoverIndex);
|
||||
}
|
||||
|
||||
$tooltip.html('<div class="graph-tooltip small"><div class="graph-tooltip-time">'+ timestamp + '</div> ' + seriesHtml + '</div>')
|
||||
.place_tt(pos.pageX + 20, pos.pageY);
|
||||
return;
|
||||
}
|
||||
if (item) {
|
||||
seriesInfo = item.series.info;
|
||||
format = scope.panel.y_formats[seriesInfo.yaxis - 1];
|
||||
|
||||
if (seriesInfo.alias) {
|
||||
group = '<small style="font-size:0.9em;">' +
|
||||
'<i class="icon-circle" style="color:'+item.series.color+';"></i>' + ' ' +
|
||||
seriesInfo.alias +
|
||||
'</small><br>';
|
||||
} else {
|
||||
group = kbn.query_color_dot(item.series.color, 15) + ' ';
|
||||
}
|
||||
|
||||
if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') {
|
||||
value = item.datapoint[1] - item.datapoint[2];
|
||||
}
|
||||
else {
|
||||
value = item.datapoint[1];
|
||||
}
|
||||
|
||||
value = kbn.valueFormats[format](value, item.series.yaxis.tickDecimals);
|
||||
timestamp = dashboard.formatDate(item.datapoint[0]);
|
||||
|
||||
$tooltip.html(group + value + " @ " + timestamp).place_tt(pos.pageX, pos.pageY);
|
||||
} else {
|
||||
$tooltip.detach();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
register: registerTooltipFeatures
|
||||
};
|
||||
});
|
@ -15,7 +15,8 @@ define([
|
||||
'jquery.flot.selection',
|
||||
'jquery.flot.time',
|
||||
'jquery.flot.stack',
|
||||
'jquery.flot.stackpercent'
|
||||
'jquery.flot.stackpercent',
|
||||
'jquery.flot.crosshair'
|
||||
],
|
||||
function (angular, app, $, _, kbn, moment, TimeSeries) {
|
||||
'use strict';
|
||||
@ -160,7 +161,7 @@ function (angular, app, $, _, kbn, moment, TimeSeries) {
|
||||
|
||||
tooltip : {
|
||||
value_type: 'cumulative',
|
||||
query_as_alias: true
|
||||
shared: false,
|
||||
},
|
||||
|
||||
targets: [{}],
|
||||
|
@ -51,8 +51,16 @@
|
||||
<input type="radio" class="input-small" ng-model="panel.renderer" value="png" ng-change="get_data()" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h5>Tooltip</h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">shared <tip> Show all series values on the same time in the same tooltip and a x croshair to help follow all series</tip> </label><input type="checkbox" ng-model="panel.tooltip.shared" ng-checked="panel.tooltip.shared" ng-change="render()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Series specific overrides <tip>Regex match example: /server[0-3]/i </tip></h5>
|
||||
|
@ -215,9 +215,9 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
|
||||
if (id === title) { return; }
|
||||
|
||||
var self = this;
|
||||
self._getDashboardInternal(id, isTemp).then(function(dashboard) {
|
||||
self._getDashboardInternal(title, isTemp).then(function(dashboard) {
|
||||
if (dashboard !== null) {
|
||||
self.deleteDashboard(id);
|
||||
self.deleteDashboard(title);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -87,7 +87,7 @@ function (Settings) {
|
||||
// Example: "1m", "1h"
|
||||
playlist_timespan: "1m",
|
||||
|
||||
// If you want to specify password before saving, please specify it bellow
|
||||
// If you want to specify password before saving, please specify it below
|
||||
// The purpose of this password is not security, but to stop some users from accidentally changing dashboards
|
||||
admin: {
|
||||
password: ''
|
||||
|
@ -444,6 +444,18 @@ select.grafana-target-segment-input {
|
||||
max-width: 800px;
|
||||
max-height: 600px;
|
||||
overflow: hidden;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.grafana-tooltip hr {
|
||||
padding: 2px;
|
||||
color: #c8c8c8;
|
||||
margin: 0px;
|
||||
border-bottom:0px solid #c8c8c8;
|
||||
/*height:0px;
|
||||
background-color: rgb(58, 57, 57);*/
|
||||
}
|
||||
|
||||
.tooltip.in {
|
||||
|
@ -166,3 +166,19 @@
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.graph-tooltip {
|
||||
.graph-tooltip-time {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.graph-tooltip-value {
|
||||
font-weight: bold;
|
||||
float: right;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,10 @@ define([
|
||||
legend: {},
|
||||
grid: {},
|
||||
y_formats: [],
|
||||
seriesOverrides: []
|
||||
seriesOverrides: [],
|
||||
tooltip: {
|
||||
shared: true
|
||||
}
|
||||
};
|
||||
scope.hiddenSeries = {};
|
||||
scope.dashboard = { timezone: 'browser' };
|
||||
|
56
src/test/specs/graph-tooltip-specs.js
Normal file
56
src/test/specs/graph-tooltip-specs.js
Normal file
@ -0,0 +1,56 @@
|
||||
define([
|
||||
'jquery',
|
||||
'directives/grafanaGraph.tooltip'
|
||||
], function($, tooltip) {
|
||||
'use strict';
|
||||
|
||||
describe('graph tooltip', function() {
|
||||
var elem = $('<div></div>');
|
||||
var dashboard = {
|
||||
formatDate: sinon.stub().returns('date'),
|
||||
};
|
||||
var scope = {
|
||||
panel: {
|
||||
tooltip: {
|
||||
shared: true
|
||||
},
|
||||
y_formats: ['ms', 'none'],
|
||||
}
|
||||
};
|
||||
|
||||
var data = [
|
||||
{
|
||||
data: [[10,10], [12,20]],
|
||||
info: { yaxis: 1 },
|
||||
yaxis: { tickDecimals: 2 },
|
||||
},
|
||||
{
|
||||
data: [[10,10], [12,20]],
|
||||
info: { yaxis: 1 },
|
||||
yaxis: { tickDecimals: 2 },
|
||||
}
|
||||
];
|
||||
|
||||
var plot = {
|
||||
getData: sinon.stub().returns(data),
|
||||
highlight: sinon.stub(),
|
||||
unhighlight: sinon.stub()
|
||||
};
|
||||
|
||||
elem.data('plot', plot);
|
||||
|
||||
beforeEach(function() {
|
||||
tooltip.register(elem, dashboard, scope);
|
||||
elem.trigger('plothover', [{}, {x: 13}, {}]);
|
||||
});
|
||||
|
||||
it('should add tooltip', function() {
|
||||
var tooltipHtml = $(".graph-tooltip").text();
|
||||
expect(tooltipHtml).to.be('date : 40.00 ms : 20.00 ms');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -3,76 +3,31 @@ define([
|
||||
], function(kbn) {
|
||||
'use strict';
|
||||
|
||||
describe('millisecond formating', function() {
|
||||
function describeValueFormat(desc, value, tickSize, tickDecimals, result) {
|
||||
|
||||
it('should translate 4378634603 as 1.67 years', function() {
|
||||
var str = kbn.msFormat(4378634603, 2);
|
||||
expect(str).to.be('50.68 day');
|
||||
describe('value format: ' + desc, function() {
|
||||
it('should translate ' + value + ' as ' + result, function() {
|
||||
var scaledDecimals = tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10);
|
||||
var str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals);
|
||||
expect(str).to.be(result);
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate 3654454 as 1.02 hour', function() {
|
||||
var str = kbn.msFormat(3654454, 2);
|
||||
expect(str).to.be('1.02 hour');
|
||||
});
|
||||
}
|
||||
|
||||
it('should not downscale when value is zero', function() {
|
||||
var str = kbn.msFormat(0, 2);
|
||||
expect(str).to.be('0.00 ms');
|
||||
});
|
||||
describeValueFormat('ms', 0.0024, 0.0005, 4, '0.0024 ms');
|
||||
describeValueFormat('ms', 100, 1, 0, '100 ms');
|
||||
describeValueFormat('ms', 1250, 10, 0, '1.25 s');
|
||||
describeValueFormat('ms', 1250, 300, 0, '1.3 s');
|
||||
describeValueFormat('ms', 65150, 10000, 0, '1.1 min');
|
||||
describeValueFormat('ms', 6515000, 1500000, 0, '1.8 hour');
|
||||
describeValueFormat('ms', 651500000, 150000000, 0, '8 day');
|
||||
|
||||
describeValueFormat('none', 2.75e-10, 0, 10, '3e-10');
|
||||
describeValueFormat('none', 0, 0, 2, '0');
|
||||
|
||||
it('should translate 365445 as 6.09 min', function() {
|
||||
var str = kbn.msFormat(365445, 2);
|
||||
expect(str).to.be('6.09 min');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('high negative exponent, issue #696', function() {
|
||||
it('should ignore decimal correction if exponent', function() {
|
||||
var str = kbn.getFormatFunction('')(2.75e-10, { tickDecimals: 12 });
|
||||
expect(str).to.be('2.75e-10');
|
||||
});
|
||||
it('should format 0 correctly', function() {
|
||||
var str = kbn.getFormatFunction('')(0.0, { tickDecimals: 12 });
|
||||
expect(str).to.be('0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('none format tests', function() {
|
||||
it('should translate 2 as 2.0000 if axis decimals is 4', function() {
|
||||
var str = kbn.getFormatFunction('')(2, { tickDecimals: 4 });
|
||||
expect(str).to.be('2.0000');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nanosecond formatting', function () {
|
||||
it('should translate 25 to 25 ns', function () {
|
||||
var str = kbn.nanosFormat(25, 2);
|
||||
expect(str).to.be("25 ns");
|
||||
});
|
||||
|
||||
it('should translate 2558 to 2.56 µs', function () {
|
||||
var str = kbn.nanosFormat(2558, 2);
|
||||
expect(str).to.be("2.56 µs");
|
||||
});
|
||||
|
||||
it('should translate 2558000 to 2.56 ms', function () {
|
||||
var str = kbn.nanosFormat(2558000, 2);
|
||||
expect(str).to.be("2.56 ms");
|
||||
});
|
||||
|
||||
it('should translate 2019962000 to 2.02 s', function () {
|
||||
var str = kbn.nanosFormat(2049962000, 2);
|
||||
expect(str).to.be("2.05 s");
|
||||
});
|
||||
|
||||
it('should translate 95199620000 to 1.59 m', function () {
|
||||
var str = kbn.nanosFormat(95199620000, 2);
|
||||
expect(str).to.be("1.59 m");
|
||||
});
|
||||
|
||||
});
|
||||
describeValueFormat('ns', 25, 1, 0, '25 ns');
|
||||
describeValueFormat('ns', 2558, 50, 0, '2.56 µs');
|
||||
|
||||
describe('calculateInterval', function() {
|
||||
it('1h 100 resultion', function() {
|
||||
|
@ -43,6 +43,7 @@ require.config({
|
||||
'jquery.flot.stack': '../vendor/jquery/jquery.flot.stack',
|
||||
'jquery.flot.stackpercent':'../vendor/jquery/jquery.flot.stackpercent',
|
||||
'jquery.flot.time': '../vendor/jquery/jquery.flot.time',
|
||||
'jquery.flot.crosshair': '../vendor/jquery/jquery.flot.crosshair',
|
||||
|
||||
modernizr: '../vendor/modernizr-2.6.1',
|
||||
},
|
||||
@ -77,6 +78,7 @@ require.config({
|
||||
'jquery.flot.stack': ['jquery', 'jquery.flot'],
|
||||
'jquery.flot.stackpercent':['jquery', 'jquery.flot'],
|
||||
'jquery.flot.time': ['jquery', 'jquery.flot'],
|
||||
'jquery.flot.crosshair':['jquery', 'jquery.flot'],
|
||||
|
||||
'angular-route': ['angular'],
|
||||
'angular-cookies': ['angular'],
|
||||
@ -127,6 +129,7 @@ require([
|
||||
'specs/influxdb-datasource-specs',
|
||||
'specs/graph-ctrl-specs',
|
||||
'specs/grafanaGraph-specs',
|
||||
'specs/graph-tooltip-specs',
|
||||
'specs/seriesOverridesCtrl-specs',
|
||||
'specs/timeSrv-specs',
|
||||
'specs/templateSrv-specs',
|
||||
|
176
src/vendor/jquery/jquery.flot.crosshair.js
vendored
Normal file
176
src/vendor/jquery/jquery.flot.crosshair.js
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
/* Flot plugin for showing crosshairs when the mouse hovers over the plot.
|
||||
|
||||
Copyright (c) 2007-2014 IOLA and Ole Laursen.
|
||||
Licensed under the MIT license.
|
||||
|
||||
The plugin supports these options:
|
||||
|
||||
crosshair: {
|
||||
mode: null or "x" or "y" or "xy"
|
||||
color: color
|
||||
lineWidth: number
|
||||
}
|
||||
|
||||
Set the mode to one of "x", "y" or "xy". The "x" mode enables a vertical
|
||||
crosshair that lets you trace the values on the x axis, "y" enables a
|
||||
horizontal crosshair and "xy" enables them both. "color" is the color of the
|
||||
crosshair (default is "rgba(170, 0, 0, 0.80)"), "lineWidth" is the width of
|
||||
the drawn lines (default is 1).
|
||||
|
||||
The plugin also adds four public methods:
|
||||
|
||||
- setCrosshair( pos )
|
||||
|
||||
Set the position of the crosshair. Note that this is cleared if the user
|
||||
moves the mouse. "pos" is in coordinates of the plot and should be on the
|
||||
form { x: xpos, y: ypos } (you can use x2/x3/... if you're using multiple
|
||||
axes), which is coincidentally the same format as what you get from a
|
||||
"plothover" event. If "pos" is null, the crosshair is cleared.
|
||||
|
||||
- clearCrosshair()
|
||||
|
||||
Clear the crosshair.
|
||||
|
||||
- lockCrosshair(pos)
|
||||
|
||||
Cause the crosshair to lock to the current location, no longer updating if
|
||||
the user moves the mouse. Optionally supply a position (passed on to
|
||||
setCrosshair()) to move it to.
|
||||
|
||||
Example usage:
|
||||
|
||||
var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } };
|
||||
$("#graph").bind( "plothover", function ( evt, position, item ) {
|
||||
if ( item ) {
|
||||
// Lock the crosshair to the data point being hovered
|
||||
myFlot.lockCrosshair({
|
||||
x: item.datapoint[ 0 ],
|
||||
y: item.datapoint[ 1 ]
|
||||
});
|
||||
} else {
|
||||
// Return normal crosshair operation
|
||||
myFlot.unlockCrosshair();
|
||||
}
|
||||
});
|
||||
|
||||
- unlockCrosshair()
|
||||
|
||||
Free the crosshair to move again after locking it.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
var options = {
|
||||
crosshair: {
|
||||
mode: null, // one of null, "x", "y" or "xy",
|
||||
color: "rgba(170, 0, 0, 0.80)",
|
||||
lineWidth: 1
|
||||
}
|
||||
};
|
||||
|
||||
function init(plot) {
|
||||
// position of crosshair in pixels
|
||||
var crosshair = { x: -1, y: -1, locked: false };
|
||||
|
||||
plot.setCrosshair = function setCrosshair(pos) {
|
||||
if (!pos)
|
||||
crosshair.x = -1;
|
||||
else {
|
||||
var o = plot.p2c(pos);
|
||||
crosshair.x = Math.max(0, Math.min(o.left, plot.width()));
|
||||
crosshair.y = Math.max(0, Math.min(o.top, plot.height()));
|
||||
}
|
||||
|
||||
plot.triggerRedrawOverlay();
|
||||
};
|
||||
|
||||
plot.clearCrosshair = plot.setCrosshair; // passes null for pos
|
||||
|
||||
plot.lockCrosshair = function lockCrosshair(pos) {
|
||||
if (pos)
|
||||
plot.setCrosshair(pos);
|
||||
crosshair.locked = true;
|
||||
};
|
||||
|
||||
plot.unlockCrosshair = function unlockCrosshair() {
|
||||
crosshair.locked = false;
|
||||
};
|
||||
|
||||
function onMouseOut(e) {
|
||||
if (crosshair.locked)
|
||||
return;
|
||||
|
||||
if (crosshair.x != -1) {
|
||||
crosshair.x = -1;
|
||||
plot.triggerRedrawOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseMove(e) {
|
||||
if (crosshair.locked)
|
||||
return;
|
||||
|
||||
if (plot.getSelection && plot.getSelection()) {
|
||||
crosshair.x = -1; // hide the crosshair while selecting
|
||||
return;
|
||||
}
|
||||
|
||||
var offset = plot.offset();
|
||||
crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width()));
|
||||
crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height()));
|
||||
plot.triggerRedrawOverlay();
|
||||
}
|
||||
|
||||
plot.hooks.bindEvents.push(function (plot, eventHolder) {
|
||||
if (!plot.getOptions().crosshair.mode)
|
||||
return;
|
||||
|
||||
eventHolder.mouseout(onMouseOut);
|
||||
eventHolder.mousemove(onMouseMove);
|
||||
});
|
||||
|
||||
plot.hooks.drawOverlay.push(function (plot, ctx) {
|
||||
var c = plot.getOptions().crosshair;
|
||||
if (!c.mode)
|
||||
return;
|
||||
|
||||
var plotOffset = plot.getPlotOffset();
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(plotOffset.left, plotOffset.top);
|
||||
|
||||
if (crosshair.x != -1) {
|
||||
var adj = plot.getOptions().crosshair.lineWidth % 2 ? 0.5 : 0;
|
||||
|
||||
ctx.strokeStyle = c.color;
|
||||
ctx.lineWidth = c.lineWidth;
|
||||
ctx.lineJoin = "round";
|
||||
|
||||
ctx.beginPath();
|
||||
if (c.mode.indexOf("x") != -1) {
|
||||
var drawX = Math.floor(crosshair.x) + adj;
|
||||
ctx.moveTo(drawX, 0);
|
||||
ctx.lineTo(drawX, plot.height());
|
||||
}
|
||||
if (c.mode.indexOf("y") != -1) {
|
||||
var drawY = Math.floor(crosshair.y) + adj;
|
||||
ctx.moveTo(0, drawY);
|
||||
ctx.lineTo(plot.width(), drawY);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.restore();
|
||||
});
|
||||
|
||||
plot.hooks.shutdown.push(function (plot, eventHolder) {
|
||||
eventHolder.unbind("mouseout", onMouseOut);
|
||||
eventHolder.unbind("mousemove", onMouseMove);
|
||||
});
|
||||
}
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: 'crosshair',
|
||||
version: '1.0'
|
||||
});
|
||||
})(jQuery);
|
2
src/vendor/jquery/jquery.flot.js
vendored
2
src/vendor/jquery/jquery.flot.js
vendored
@ -1728,6 +1728,8 @@ Licensed under the MIT license.
|
||||
axis.delta = delta;
|
||||
axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
|
||||
axis.tickSize = opts.tickSize || size;
|
||||
// grafana addition
|
||||
axis.scaledDecimals = axis.tickDecimals - Math.floor(Math.log(axis.tickSize) / Math.LN10);
|
||||
|
||||
// Time mode was moved to a plug-in in 0.8, and since so many people use it
|
||||
// we'll add an especially friendly reminder to make sure they included it.
|
||||
|
Loading…
Reference in New Issue
Block a user