mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
refactor(graph): js -> typescript refactoring
This commit is contained in:
@@ -5,8 +5,6 @@ import _ from 'lodash';
|
|||||||
import TimeSeries from 'app/core/time_series2';
|
import TimeSeries from 'app/core/time_series2';
|
||||||
import {colors} from 'app/core/core';
|
import {colors} from 'app/core/core';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class DataProcessor {
|
export class DataProcessor {
|
||||||
|
|
||||||
constructor(private panel) {
|
constructor(private panel) {
|
||||||
|
|||||||
@@ -1,601 +0,0 @@
|
|||||||
define([
|
|
||||||
'angular',
|
|
||||||
'jquery',
|
|
||||||
'moment',
|
|
||||||
'lodash',
|
|
||||||
'app/core/utils/kbn',
|
|
||||||
'./graph_tooltip',
|
|
||||||
'./threshold_manager',
|
|
||||||
'jquery.flot',
|
|
||||||
'jquery.flot.selection',
|
|
||||||
'jquery.flot.time',
|
|
||||||
'jquery.flot.stack',
|
|
||||||
'jquery.flot.stackpercent',
|
|
||||||
'jquery.flot.fillbelow',
|
|
||||||
'jquery.flot.crosshair',
|
|
||||||
'./jquery.flot.events',
|
|
||||||
],
|
|
||||||
function (angular, $, moment, _, kbn, GraphTooltip, thresholdManExports) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var module = angular.module('grafana.directives');
|
|
||||||
var labelWidthCache = {};
|
|
||||||
|
|
||||||
module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
template: '<div> </div>',
|
|
||||||
link: function(scope, elem) {
|
|
||||||
var ctrl = scope.ctrl;
|
|
||||||
var dashboard = ctrl.dashboard;
|
|
||||||
var panel = ctrl.panel;
|
|
||||||
var data, annotations;
|
|
||||||
var sortedSeries;
|
|
||||||
var legendSideLastValue = null;
|
|
||||||
var rootScope = scope.$root;
|
|
||||||
var panelWidth = 0;
|
|
||||||
var thresholdManager = new thresholdManExports.ThresholdManager(ctrl);
|
|
||||||
|
|
||||||
rootScope.onAppEvent('setCrosshair', function(event, info) {
|
|
||||||
// do not need to to this if event is from this panel
|
|
||||||
if (info.scope === scope) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dashboard.sharedCrosshair) {
|
|
||||||
var plot = elem.data().plot;
|
|
||||||
if (plot) {
|
|
||||||
plot.setCrosshair({ x: info.pos.x, y: info.pos.y });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, scope);
|
|
||||||
|
|
||||||
rootScope.onAppEvent('clearCrosshair', function() {
|
|
||||||
var plot = elem.data().plot;
|
|
||||||
if (plot) {
|
|
||||||
plot.clearCrosshair();
|
|
||||||
}
|
|
||||||
}, scope);
|
|
||||||
|
|
||||||
// Receive render events
|
|
||||||
ctrl.events.on('render', function(renderData) {
|
|
||||||
data = renderData || data;
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
annotations = data.annotations || annotations;
|
|
||||||
render_panel();
|
|
||||||
});
|
|
||||||
|
|
||||||
function getLegendHeight(panelHeight) {
|
|
||||||
if (!panel.legend.show || panel.legend.rightSide) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.legend.alignAsTable) {
|
|
||||||
var legendSeries = _.filter(data, function(series) {
|
|
||||||
return series.hideFromLegend(panel.legend) === false;
|
|
||||||
});
|
|
||||||
var total = 23 + (21 * legendSeries.length);
|
|
||||||
return Math.min(total, Math.floor(panelHeight/2));
|
|
||||||
} else {
|
|
||||||
return 26;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setElementHeight() {
|
|
||||||
try {
|
|
||||||
var height = ctrl.height - getLegendHeight(ctrl.height);
|
|
||||||
elem.css('height', height + 'px');
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch(e) { // IE throws errors sometimes
|
|
||||||
console.log(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldAbortRender() {
|
|
||||||
if (!data) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!setElementHeight()) { return true; }
|
|
||||||
|
|
||||||
if (panelWidth === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLabelWidth(text, elem) {
|
|
||||||
var labelWidth = labelWidthCache[text];
|
|
||||||
|
|
||||||
if (!labelWidth) {
|
|
||||||
labelWidth = labelWidthCache[text] = elem.width();
|
|
||||||
}
|
|
||||||
|
|
||||||
return labelWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawHook(plot) {
|
|
||||||
// Update legend values
|
|
||||||
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[panel.yaxes[series.yaxis - 1].format];
|
|
||||||
|
|
||||||
// decimal override
|
|
||||||
if (_.isNumber(panel.decimals)) {
|
|
||||||
series.updateLegendValues(formater, panel.decimals, null);
|
|
||||||
} else {
|
|
||||||
// auto decimals
|
|
||||||
// legend and tooltip gets one more decimal precision
|
|
||||||
// than graph legend ticks
|
|
||||||
var tickDecimals = (axis.tickDecimals || -1) + 1;
|
|
||||||
series.updateLegendValues(formater, tickDecimals, axis.scaledDecimals + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!rootScope.$$phase) { scope.$digest(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// add left axis labels
|
|
||||||
if (panel.yaxes[0].label) {
|
|
||||||
var yaxisLabel = $("<div class='axisLabel left-yaxis-label'></div>")
|
|
||||||
.text(panel.yaxes[0].label)
|
|
||||||
.appendTo(elem);
|
|
||||||
|
|
||||||
yaxisLabel[0].style.marginTop = (getLabelWidth(panel.yaxes[0].label, yaxisLabel) / 2) + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
// add right axis labels
|
|
||||||
if (panel.yaxes[1].label) {
|
|
||||||
var rightLabel = $("<div class='axisLabel right-yaxis-label'></div>")
|
|
||||||
.text(panel.yaxes[1].label)
|
|
||||||
.appendTo(elem);
|
|
||||||
|
|
||||||
rightLabel[0].style.marginTop = (getLabelWidth(panel.yaxes[1].label, rightLabel) / 2) + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
thresholdManager.draw(plot);
|
|
||||||
}
|
|
||||||
|
|
||||||
function processOffsetHook(plot, gridMargin) {
|
|
||||||
var left = panel.yaxes[0];
|
|
||||||
var right = panel.yaxes[1];
|
|
||||||
if (left.show && left.label) { gridMargin.left = 20; }
|
|
||||||
if (right.show && right.label) { gridMargin.right = 20; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function for rendering panel
|
|
||||||
function render_panel() {
|
|
||||||
panelWidth = elem.width();
|
|
||||||
|
|
||||||
if (shouldAbortRender()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// give space to alert editing
|
|
||||||
thresholdManager.prepare(elem, data);
|
|
||||||
|
|
||||||
var stack = panel.stack ? true : null;
|
|
||||||
|
|
||||||
// Populate element
|
|
||||||
var options = {
|
|
||||||
hooks: {
|
|
||||||
draw: [drawHook],
|
|
||||||
processOffset: [processOffsetHook],
|
|
||||||
},
|
|
||||||
legend: { show: false },
|
|
||||||
series: {
|
|
||||||
stackpercent: panel.stack ? panel.percentage : false,
|
|
||||||
stack: panel.percentage ? null : stack,
|
|
||||||
lines: {
|
|
||||||
show: panel.lines,
|
|
||||||
zero: false,
|
|
||||||
fill: translateFillOption(panel.fill),
|
|
||||||
lineWidth: panel.linewidth,
|
|
||||||
steps: panel.steppedLine
|
|
||||||
},
|
|
||||||
bars: {
|
|
||||||
show: panel.bars,
|
|
||||||
fill: 1,
|
|
||||||
barWidth: 1,
|
|
||||||
zero: false,
|
|
||||||
lineWidth: 0
|
|
||||||
},
|
|
||||||
points: {
|
|
||||||
show: panel.points,
|
|
||||||
fill: 1,
|
|
||||||
fillColor: false,
|
|
||||||
radius: panel.points ? panel.pointradius : 2
|
|
||||||
},
|
|
||||||
shadowSize: 0
|
|
||||||
},
|
|
||||||
yaxes: [],
|
|
||||||
xaxis: {},
|
|
||||||
grid: {
|
|
||||||
minBorderMargin: 0,
|
|
||||||
markings: [],
|
|
||||||
backgroundColor: null,
|
|
||||||
borderWidth: 0,
|
|
||||||
hoverable: true,
|
|
||||||
color: '#c8c8c8',
|
|
||||||
margin: { left: 0, right: 0 },
|
|
||||||
},
|
|
||||||
selection: {
|
|
||||||
mode: "x",
|
|
||||||
color: '#666'
|
|
||||||
},
|
|
||||||
crosshair: {
|
|
||||||
mode: panel.tooltip.shared || dashboard.sharedCrosshair ? "x" : null
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
var series = data[i];
|
|
||||||
series.data = series.getFlotPairs(series.nullPointMode || panel.nullPointMode);
|
|
||||||
|
|
||||||
if (panel.xaxis.mode === 'series') {
|
|
||||||
series.data = [[i + 1, series.stats[panel.xaxis.values[0]]]];
|
|
||||||
} else if (panel.xaxis.mode === 'table' ||
|
|
||||||
panel.xaxis.mode === 'elastic') {
|
|
||||||
series.data = [];
|
|
||||||
for (var j = 0; j < series.datapoints.length; j++) {
|
|
||||||
var dataIndex = i * series.datapoints.length + j;
|
|
||||||
series.datapoints[j];
|
|
||||||
series.data.push([
|
|
||||||
dataIndex + 1,
|
|
||||||
series.datapoints[j][0]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if hidden remove points and disable stack
|
|
||||||
if (ctrl.hiddenSeries[series.alias]) {
|
|
||||||
series.data = [];
|
|
||||||
series.stack = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(panel.xaxis.mode) {
|
|
||||||
case 'series': {
|
|
||||||
options.series.bars.barWidth = 0.7;
|
|
||||||
options.series.bars.align = 'center';
|
|
||||||
addXSeriesAxis(options);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'table': {
|
|
||||||
options.series.bars.barWidth = 0.7;
|
|
||||||
options.series.bars.align = 'center';
|
|
||||||
addXTableAxis(options);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (data.length && data[0].stats.timeStep) {
|
|
||||||
options.series.bars.barWidth = data[0].stats.timeStep / 1.5;
|
|
||||||
}
|
|
||||||
addTimeAxis(options);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thresholdManager.addPlotOptions(options, panel);
|
|
||||||
addAnnotations(options);
|
|
||||||
configureAxisOptions(data, options);
|
|
||||||
|
|
||||||
sortedSeries = _.sortBy(data, function(series) { return series.zindex; });
|
|
||||||
|
|
||||||
function callPlot(incrementRenderCounter) {
|
|
||||||
try {
|
|
||||||
$.plot(elem, sortedSeries, options);
|
|
||||||
if (ctrl.renderError) {
|
|
||||||
delete ctrl.error;
|
|
||||||
delete ctrl.inspector;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('flotcharts error', e);
|
|
||||||
ctrl.error = e.message || "Render Error";
|
|
||||||
ctrl.renderError = true;
|
|
||||||
ctrl.inspector = {error: e};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (incrementRenderCounter) {
|
|
||||||
ctrl.renderingCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldDelayDraw(panel)) {
|
|
||||||
// temp fix for legends on the side, need to render twice to get dimensions right
|
|
||||||
callPlot(false);
|
|
||||||
setTimeout(function() { callPlot(true); }, 50);
|
|
||||||
legendSideLastValue = panel.legend.rightSide;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callPlot(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function translateFillOption(fill) {
|
|
||||||
return fill === 0 ? 0.001 : fill/10;
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldDelayDraw(panel) {
|
|
||||||
if (panel.legend.rightSide) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (legendSideLastValue !== null && panel.legend.rightSide !== legendSideLastValue) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTimeAxis(options) {
|
|
||||||
var ticks = panelWidth / 100;
|
|
||||||
var min = _.isUndefined(ctrl.range.from) ? null : ctrl.range.from.valueOf();
|
|
||||||
var max = _.isUndefined(ctrl.range.to) ? null : ctrl.range.to.valueOf();
|
|
||||||
|
|
||||||
options.xaxis = {
|
|
||||||
timezone: dashboard.getTimezone(),
|
|
||||||
show: panel.xaxis.show,
|
|
||||||
mode: "time",
|
|
||||||
min: min,
|
|
||||||
max: max,
|
|
||||||
label: "Datetime",
|
|
||||||
ticks: ticks,
|
|
||||||
timeformat: time_format(ticks, min, max),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function addXSeriesAxis(options) {
|
|
||||||
var ticks = _.map(data, function(series, index) {
|
|
||||||
return [index + 1, series.alias];
|
|
||||||
});
|
|
||||||
|
|
||||||
options.xaxis = {
|
|
||||||
timezone: dashboard.getTimezone(),
|
|
||||||
show: panel.xaxis.show,
|
|
||||||
mode: null,
|
|
||||||
min: 0,
|
|
||||||
max: ticks.length + 1,
|
|
||||||
label: "Datetime",
|
|
||||||
ticks: ticks
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function addXTableAxis(options) {
|
|
||||||
var ticks = _.map(data, function(series, seriesIndex) {
|
|
||||||
return _.map(series.datapoints, function(point, pointIndex) {
|
|
||||||
var tickIndex = seriesIndex * series.datapoints.length + pointIndex;
|
|
||||||
return [tickIndex + 1, point[1]];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ticks = _.flatten(ticks, true);
|
|
||||||
|
|
||||||
options.xaxis = {
|
|
||||||
timezone: dashboard.getTimezone(),
|
|
||||||
show: panel.xaxis.show,
|
|
||||||
mode: null,
|
|
||||||
min: 0,
|
|
||||||
max: ticks.length + 1,
|
|
||||||
label: "Datetime",
|
|
||||||
ticks: ticks
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function addAnnotations(options) {
|
|
||||||
if(!annotations || annotations.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var types = {};
|
|
||||||
for (var i = 0; i < annotations.length; i++) {
|
|
||||||
var item = annotations[i];
|
|
||||||
|
|
||||||
if (!types[item.source.name]) {
|
|
||||||
types[item.source.name] = {
|
|
||||||
color: item.source.iconColor,
|
|
||||||
position: 'BOTTOM',
|
|
||||||
markerSize: 5,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options.events = {
|
|
||||||
levels: _.keys(types).length + 1,
|
|
||||||
data: annotations,
|
|
||||||
types: types,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//Override min/max to provide more flexible autoscaling
|
|
||||||
function autoscaleSpanOverride(yaxis, data, options) {
|
|
||||||
var expr;
|
|
||||||
if (yaxis.min != null && data != null) {
|
|
||||||
expr = parseThresholdExpr(yaxis.min);
|
|
||||||
options.min = autoscaleYAxisMin(expr, data.stats);
|
|
||||||
}
|
|
||||||
if (yaxis.max != null && data != null) {
|
|
||||||
expr = parseThresholdExpr(yaxis.max);
|
|
||||||
options.max = autoscaleYAxisMax(expr, data.stats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseThresholdExpr(expr) {
|
|
||||||
var match, operator, value, precision;
|
|
||||||
expr = String(expr);
|
|
||||||
match = expr.match(/\s*([<=>~]*)\s*(\-?\d+(\.\d+)?)/);
|
|
||||||
if (match) {
|
|
||||||
operator = match[1];
|
|
||||||
value = parseFloat(match[2]);
|
|
||||||
//Precision based on input
|
|
||||||
precision = match[3] ? match[3].length - 1 : 0;
|
|
||||||
return {
|
|
||||||
operator: operator,
|
|
||||||
value: value,
|
|
||||||
precision: precision
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function autoscaleYAxisMax(expr, dataStats) {
|
|
||||||
var operator = expr.operator,
|
|
||||||
value = expr.value,
|
|
||||||
precision = expr.precision;
|
|
||||||
if (operator === ">") {
|
|
||||||
return dataStats.max < value ? value : null;
|
|
||||||
} else if (operator === "<") {
|
|
||||||
return dataStats.max > value ? value : null;
|
|
||||||
} else if (operator === "~") {
|
|
||||||
return kbn.roundValue(dataStats.avg + value, precision);
|
|
||||||
} else if (operator === "=") {
|
|
||||||
return kbn.roundValue(dataStats.current + value, precision);
|
|
||||||
} else if (!operator && !isNaN(value)) {
|
|
||||||
return kbn.roundValue(value, precision);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function autoscaleYAxisMin(expr, dataStats) {
|
|
||||||
var operator = expr.operator,
|
|
||||||
value = expr.value,
|
|
||||||
precision = expr.precision;
|
|
||||||
if (operator === ">") {
|
|
||||||
return dataStats.min < value ? value : null;
|
|
||||||
} else if (operator === "<") {
|
|
||||||
return dataStats.min > value ? value : null;
|
|
||||||
} else if (operator === "~") {
|
|
||||||
return kbn.roundValue(dataStats.avg - value, precision);
|
|
||||||
} else if (operator === "=") {
|
|
||||||
return kbn.roundValue(dataStats.current - value, precision);
|
|
||||||
} else if (!operator && !isNaN(value)) {
|
|
||||||
return kbn.roundValue(value, precision);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureAxisOptions(data, options) {
|
|
||||||
var defaults = {
|
|
||||||
position: 'left',
|
|
||||||
show: panel.yaxes[0].show,
|
|
||||||
min: panel.yaxes[0].min,
|
|
||||||
index: 1,
|
|
||||||
logBase: panel.yaxes[0].logBase || 1,
|
|
||||||
max: panel.percentage && panel.stack ? 100 : panel.yaxes[0].max,
|
|
||||||
};
|
|
||||||
|
|
||||||
autoscaleSpanOverride(panel.yaxes[0], data[0], defaults);
|
|
||||||
options.yaxes.push(defaults);
|
|
||||||
|
|
||||||
if (_.find(data, {yaxis: 2})) {
|
|
||||||
var secondY = _.clone(defaults);
|
|
||||||
secondY.index = 2,
|
|
||||||
secondY.show = panel.yaxes[1].show;
|
|
||||||
secondY.logBase = panel.yaxes[1].logBase || 1,
|
|
||||||
secondY.position = 'right';
|
|
||||||
secondY.min = panel.yaxes[1].min;
|
|
||||||
secondY.max = panel.percentage && panel.stack ? 100 : panel.yaxes[1].max;
|
|
||||||
autoscaleSpanOverride(panel.yaxes[1], data[1], secondY);
|
|
||||||
options.yaxes.push(secondY);
|
|
||||||
|
|
||||||
applyLogScale(options.yaxes[1], data);
|
|
||||||
configureAxisMode(options.yaxes[1], panel.percentage && panel.stack ? "percent" : panel.yaxes[1].format);
|
|
||||||
}
|
|
||||||
|
|
||||||
applyLogScale(options.yaxes[0], data);
|
|
||||||
configureAxisMode(options.yaxes[0], panel.percentage && panel.stack ? "percent" : panel.yaxes[0].format);
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyLogScale(axis, data) {
|
|
||||||
if (axis.logBase === 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var series, i;
|
|
||||||
var max = axis.max;
|
|
||||||
|
|
||||||
if (max === null) {
|
|
||||||
for (i = 0; i < data.length; i++) {
|
|
||||||
series = data[i];
|
|
||||||
if (series.yaxis === axis.index) {
|
|
||||||
if (max < series.stats.max) {
|
|
||||||
max = series.stats.max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (max === void 0) {
|
|
||||||
max = Number.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
axis.min = axis.min !== null ? axis.min : 0;
|
|
||||||
axis.ticks = [0, 1];
|
|
||||||
var nextTick = 1;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
nextTick = nextTick * axis.logBase;
|
|
||||||
axis.ticks.push(nextTick);
|
|
||||||
if (nextTick > max) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (axis.logBase === 10) {
|
|
||||||
axis.transform = function(v) { return Math.log(v+0.1); };
|
|
||||||
axis.inverseTransform = function (v) { return Math.pow(10,v); };
|
|
||||||
} else {
|
|
||||||
axis.transform = function(v) { return Math.log(v+0.1) / Math.log(axis.logBase); };
|
|
||||||
axis.inverseTransform = function (v) { return Math.pow(axis.logBase,v); };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureAxisMode(axis, format) {
|
|
||||||
axis.tickFormatter = function(val, axis) {
|
|
||||||
return kbn.valueFormats[format](val, axis.tickDecimals, axis.scaledDecimals);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function time_format(ticks, min, max) {
|
|
||||||
if (min && max && ticks) {
|
|
||||||
var range = max - min;
|
|
||||||
var secPerTick = (range/ticks) / 1000;
|
|
||||||
var oneDay = 86400000;
|
|
||||||
var oneYear = 31536000000;
|
|
||||||
|
|
||||||
if (secPerTick <= 45) {
|
|
||||||
return "%H:%M:%S";
|
|
||||||
}
|
|
||||||
if (secPerTick <= 7200 || range <= oneDay) {
|
|
||||||
return "%H:%M";
|
|
||||||
}
|
|
||||||
if (secPerTick <= 80000) {
|
|
||||||
return "%m/%d %H:%M";
|
|
||||||
}
|
|
||||||
if (secPerTick <= 2419200 || range <= oneYear) {
|
|
||||||
return "%m/%d";
|
|
||||||
}
|
|
||||||
return "%Y-%m";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "%H:%M";
|
|
||||||
}
|
|
||||||
|
|
||||||
new GraphTooltip(elem, dashboard, scope, function() {
|
|
||||||
return sortedSeries;
|
|
||||||
});
|
|
||||||
|
|
||||||
elem.bind("plotselected", function (event, ranges) {
|
|
||||||
scope.$apply(function() {
|
|
||||||
timeSrv.setTime({
|
|
||||||
from : moment.utc(ranges.xaxis.from),
|
|
||||||
to : moment.utc(ranges.xaxis.to),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
602
public/app/plugins/panel/graph/graph.ts
Executable file
602
public/app/plugins/panel/graph/graph.ts
Executable file
@@ -0,0 +1,602 @@
|
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import 'jquery.flot';
|
||||||
|
import 'jquery.flot.selection';
|
||||||
|
import 'jquery.flot.time';
|
||||||
|
import 'jquery.flot.stack';
|
||||||
|
import 'jquery.flot.stackpercent';
|
||||||
|
import 'jquery.flot.fillbelow';
|
||||||
|
import 'jquery.flot.crosshair';
|
||||||
|
import './jquery.flot.events';
|
||||||
|
|
||||||
|
import angular from 'angular';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import moment from 'moment';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
import GraphTooltip from './graph_tooltip';
|
||||||
|
import {ThresholdManager} from './threshold_manager';
|
||||||
|
|
||||||
|
var module = angular.module('grafana.directives');
|
||||||
|
var labelWidthCache = {};
|
||||||
|
|
||||||
|
module.directive('grafanaGraph', function($rootScope, timeSrv) {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
template: '<div> </div>',
|
||||||
|
link: function(scope, elem) {
|
||||||
|
var ctrl = scope.ctrl;
|
||||||
|
var dashboard = ctrl.dashboard;
|
||||||
|
var panel = ctrl.panel;
|
||||||
|
var data, annotations;
|
||||||
|
var sortedSeries;
|
||||||
|
var legendSideLastValue = null;
|
||||||
|
var rootScope = scope.$root;
|
||||||
|
var panelWidth = 0;
|
||||||
|
var thresholdManager = new ThresholdManager(ctrl);
|
||||||
|
|
||||||
|
rootScope.onAppEvent('setCrosshair', function(event, info) {
|
||||||
|
// do not need to to this if event is from this panel
|
||||||
|
if (info.scope === scope) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dashboard.sharedCrosshair) {
|
||||||
|
var plot = elem.data().plot;
|
||||||
|
if (plot) {
|
||||||
|
plot.setCrosshair({ x: info.pos.x, y: info.pos.y });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, scope);
|
||||||
|
|
||||||
|
rootScope.onAppEvent('clearCrosshair', function() {
|
||||||
|
var plot = elem.data().plot;
|
||||||
|
if (plot) {
|
||||||
|
plot.clearCrosshair();
|
||||||
|
}
|
||||||
|
}, scope);
|
||||||
|
|
||||||
|
// Receive render events
|
||||||
|
ctrl.events.on('render', function(renderData) {
|
||||||
|
data = renderData || data;
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
annotations = data.annotations || annotations;
|
||||||
|
render_panel();
|
||||||
|
});
|
||||||
|
|
||||||
|
function getLegendHeight(panelHeight) {
|
||||||
|
if (!panel.legend.show || panel.legend.rightSide) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel.legend.alignAsTable) {
|
||||||
|
var legendSeries = _.filter(data, function(series) {
|
||||||
|
return series.hideFromLegend(panel.legend) === false;
|
||||||
|
});
|
||||||
|
var total = 23 + (21 * legendSeries.length);
|
||||||
|
return Math.min(total, Math.floor(panelHeight/2));
|
||||||
|
} else {
|
||||||
|
return 26;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setElementHeight() {
|
||||||
|
try {
|
||||||
|
var height = ctrl.height - getLegendHeight(ctrl.height);
|
||||||
|
elem.css('height', height + 'px');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) { // IE throws errors sometimes
|
||||||
|
console.log(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldAbortRender() {
|
||||||
|
if (!data) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setElementHeight()) { return true; }
|
||||||
|
|
||||||
|
if (panelWidth === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLabelWidth(text, elem) {
|
||||||
|
var labelWidth = labelWidthCache[text];
|
||||||
|
|
||||||
|
if (!labelWidth) {
|
||||||
|
labelWidth = labelWidthCache[text] = elem.width();
|
||||||
|
}
|
||||||
|
|
||||||
|
return labelWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawHook(plot) {
|
||||||
|
// Update legend values
|
||||||
|
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[panel.yaxes[series.yaxis - 1].format];
|
||||||
|
|
||||||
|
// decimal override
|
||||||
|
if (_.isNumber(panel.decimals)) {
|
||||||
|
series.updateLegendValues(formater, panel.decimals, null);
|
||||||
|
} else {
|
||||||
|
// auto decimals
|
||||||
|
// legend and tooltip gets one more decimal precision
|
||||||
|
// than graph legend ticks
|
||||||
|
var tickDecimals = (axis.tickDecimals || -1) + 1;
|
||||||
|
series.updateLegendValues(formater, tickDecimals, axis.scaledDecimals + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rootScope.$$phase) { scope.$digest(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// add left axis labels
|
||||||
|
if (panel.yaxes[0].label) {
|
||||||
|
var yaxisLabel = $("<div class='axisLabel left-yaxis-label'></div>")
|
||||||
|
.text(panel.yaxes[0].label)
|
||||||
|
.appendTo(elem);
|
||||||
|
|
||||||
|
yaxisLabel[0].style.marginTop = (getLabelWidth(panel.yaxes[0].label, yaxisLabel) / 2) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
// add right axis labels
|
||||||
|
if (panel.yaxes[1].label) {
|
||||||
|
var rightLabel = $("<div class='axisLabel right-yaxis-label'></div>")
|
||||||
|
.text(panel.yaxes[1].label)
|
||||||
|
.appendTo(elem);
|
||||||
|
|
||||||
|
rightLabel[0].style.marginTop = (getLabelWidth(panel.yaxes[1].label, rightLabel) / 2) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
thresholdManager.draw(plot);
|
||||||
|
}
|
||||||
|
|
||||||
|
function processOffsetHook(plot, gridMargin) {
|
||||||
|
var left = panel.yaxes[0];
|
||||||
|
var right = panel.yaxes[1];
|
||||||
|
if (left.show && left.label) { gridMargin.left = 20; }
|
||||||
|
if (right.show && right.label) { gridMargin.right = 20; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function processDatapoints(plot) {
|
||||||
|
console.log('processDatapoints');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function for rendering panel
|
||||||
|
function render_panel() {
|
||||||
|
panelWidth = elem.width();
|
||||||
|
|
||||||
|
if (shouldAbortRender()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// give space to alert editing
|
||||||
|
thresholdManager.prepare(elem, data);
|
||||||
|
|
||||||
|
var stack = panel.stack ? true : null;
|
||||||
|
|
||||||
|
// Populate element
|
||||||
|
var options: any = {
|
||||||
|
hooks: {
|
||||||
|
draw: [drawHook],
|
||||||
|
processOffset: [processOffsetHook],
|
||||||
|
processDatapoints: [processDatapoints],
|
||||||
|
},
|
||||||
|
legend: { show: false },
|
||||||
|
series: {
|
||||||
|
stackpercent: panel.stack ? panel.percentage : false,
|
||||||
|
stack: panel.percentage ? null : stack,
|
||||||
|
lines: {
|
||||||
|
show: panel.lines,
|
||||||
|
zero: false,
|
||||||
|
fill: translateFillOption(panel.fill),
|
||||||
|
lineWidth: panel.linewidth,
|
||||||
|
steps: panel.steppedLine
|
||||||
|
},
|
||||||
|
bars: {
|
||||||
|
show: panel.bars,
|
||||||
|
fill: 1,
|
||||||
|
barWidth: 1,
|
||||||
|
zero: false,
|
||||||
|
lineWidth: 0
|
||||||
|
},
|
||||||
|
points: {
|
||||||
|
show: panel.points,
|
||||||
|
fill: 1,
|
||||||
|
fillColor: false,
|
||||||
|
radius: panel.points ? panel.pointradius : 2
|
||||||
|
},
|
||||||
|
shadowSize: 0
|
||||||
|
},
|
||||||
|
yaxes: [],
|
||||||
|
xaxis: {},
|
||||||
|
grid: {
|
||||||
|
minBorderMargin: 0,
|
||||||
|
markings: [],
|
||||||
|
backgroundColor: null,
|
||||||
|
borderWidth: 0,
|
||||||
|
hoverable: true,
|
||||||
|
color: '#c8c8c8',
|
||||||
|
margin: { left: 0, right: 0 },
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
mode: "x",
|
||||||
|
color: '#666'
|
||||||
|
},
|
||||||
|
crosshair: {
|
||||||
|
mode: panel.tooltip.shared || dashboard.sharedCrosshair ? "x" : null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
var series = data[i];
|
||||||
|
series.data = series.getFlotPairs(series.nullPointMode || panel.nullPointMode);
|
||||||
|
|
||||||
|
if (panel.xaxis.mode === 'series') {
|
||||||
|
series.data = [[i + 1, series.stats[panel.xaxis.values[0]]]];
|
||||||
|
} else if (panel.xaxis.mode === 'table' || panel.xaxis.mode === 'elastic') {
|
||||||
|
series.data = [];
|
||||||
|
for (var j = 0; j < series.datapoints.length; j++) {
|
||||||
|
var dataIndex = i * series.datapoints.length + j;
|
||||||
|
series.datapoints[j];
|
||||||
|
series.data.push([
|
||||||
|
dataIndex + 1,
|
||||||
|
series.datapoints[j][0]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if hidden remove points and disable stack
|
||||||
|
if (ctrl.hiddenSeries[series.alias]) {
|
||||||
|
series.data = [];
|
||||||
|
series.stack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (panel.xaxis.mode) {
|
||||||
|
case 'series': {
|
||||||
|
options.series.bars.barWidth = 0.7;
|
||||||
|
options.series.bars.align = 'center';
|
||||||
|
addXSeriesAxis(options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'table': {
|
||||||
|
options.series.bars.barWidth = 0.7;
|
||||||
|
options.series.bars.align = 'center';
|
||||||
|
addXTableAxis(options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
if (data.length && data[0].stats.timeStep) {
|
||||||
|
options.series.bars.barWidth = data[0].stats.timeStep / 1.5;
|
||||||
|
}
|
||||||
|
addTimeAxis(options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thresholdManager.addPlotOptions(options, panel);
|
||||||
|
addAnnotations(options);
|
||||||
|
configureAxisOptions(data, options);
|
||||||
|
|
||||||
|
sortedSeries = _.sortBy(data, function(series) { return series.zindex; });
|
||||||
|
|
||||||
|
function callPlot(incrementRenderCounter) {
|
||||||
|
try {
|
||||||
|
$.plot(elem, sortedSeries, options);
|
||||||
|
if (ctrl.renderError) {
|
||||||
|
delete ctrl.error;
|
||||||
|
delete ctrl.inspector;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('flotcharts error', e);
|
||||||
|
ctrl.error = e.message || "Render Error";
|
||||||
|
ctrl.renderError = true;
|
||||||
|
ctrl.inspector = {error: e};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incrementRenderCounter) {
|
||||||
|
ctrl.renderingCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldDelayDraw(panel)) {
|
||||||
|
// temp fix for legends on the side, need to render twice to get dimensions right
|
||||||
|
callPlot(false);
|
||||||
|
setTimeout(function() { callPlot(true); }, 50);
|
||||||
|
legendSideLastValue = panel.legend.rightSide;
|
||||||
|
} else {
|
||||||
|
callPlot(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function translateFillOption(fill) {
|
||||||
|
return fill === 0 ? 0.001 : fill/10;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldDelayDraw(panel) {
|
||||||
|
if (panel.legend.rightSide) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (legendSideLastValue !== null && panel.legend.rightSide !== legendSideLastValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTimeAxis(options) {
|
||||||
|
var ticks = panelWidth / 100;
|
||||||
|
var min = _.isUndefined(ctrl.range.from) ? null : ctrl.range.from.valueOf();
|
||||||
|
var max = _.isUndefined(ctrl.range.to) ? null : ctrl.range.to.valueOf();
|
||||||
|
|
||||||
|
options.xaxis = {
|
||||||
|
timezone: dashboard.getTimezone(),
|
||||||
|
show: panel.xaxis.show,
|
||||||
|
mode: "time",
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
label: "Datetime",
|
||||||
|
ticks: ticks,
|
||||||
|
timeformat: time_format(ticks, min, max),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addXSeriesAxis(options) {
|
||||||
|
var ticks = _.map(data, function(series, index) {
|
||||||
|
return [index + 1, series.alias];
|
||||||
|
});
|
||||||
|
|
||||||
|
options.xaxis = {
|
||||||
|
timezone: dashboard.getTimezone(),
|
||||||
|
show: panel.xaxis.show,
|
||||||
|
mode: null,
|
||||||
|
min: 0,
|
||||||
|
max: ticks.length + 1,
|
||||||
|
label: "Datetime",
|
||||||
|
ticks: ticks
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addXTableAxis(options) {
|
||||||
|
var ticks = _.map(data, function(series, seriesIndex) {
|
||||||
|
return _.map(series.datapoints, function(point, pointIndex) {
|
||||||
|
var tickIndex = seriesIndex * series.datapoints.length + pointIndex;
|
||||||
|
return [tickIndex + 1, point[1]];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ticks = _.flatten(ticks, true);
|
||||||
|
|
||||||
|
options.xaxis = {
|
||||||
|
timezone: dashboard.getTimezone(),
|
||||||
|
show: panel.xaxis.show,
|
||||||
|
mode: null,
|
||||||
|
min: 0,
|
||||||
|
max: ticks.length + 1,
|
||||||
|
label: "Datetime",
|
||||||
|
ticks: ticks
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAnnotations(options) {
|
||||||
|
if (!annotations || annotations.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var types = {};
|
||||||
|
for (var i = 0; i < annotations.length; i++) {
|
||||||
|
var item = annotations[i];
|
||||||
|
|
||||||
|
if (!types[item.source.name]) {
|
||||||
|
types[item.source.name] = {
|
||||||
|
color: item.source.iconColor,
|
||||||
|
position: 'BOTTOM',
|
||||||
|
markerSize: 5,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options.events = {
|
||||||
|
levels: _.keys(types).length + 1,
|
||||||
|
data: annotations,
|
||||||
|
types: types,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//Override min/max to provide more flexible autoscaling
|
||||||
|
function autoscaleSpanOverride(yaxis, data, options) {
|
||||||
|
var expr;
|
||||||
|
if (yaxis.min != null && data != null) {
|
||||||
|
expr = parseThresholdExpr(yaxis.min);
|
||||||
|
options.min = autoscaleYAxisMin(expr, data.stats);
|
||||||
|
}
|
||||||
|
if (yaxis.max != null && data != null) {
|
||||||
|
expr = parseThresholdExpr(yaxis.max);
|
||||||
|
options.max = autoscaleYAxisMax(expr, data.stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseThresholdExpr(expr) {
|
||||||
|
var match, operator, value, precision;
|
||||||
|
expr = String(expr);
|
||||||
|
match = expr.match(/\s*([<=>~]*)\s*(\-?\d+(\.\d+)?)/);
|
||||||
|
if (match) {
|
||||||
|
operator = match[1];
|
||||||
|
value = parseFloat(match[2]);
|
||||||
|
//Precision based on input
|
||||||
|
precision = match[3] ? match[3].length - 1 : 0;
|
||||||
|
return {
|
||||||
|
operator: operator,
|
||||||
|
value: value,
|
||||||
|
precision: precision
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoscaleYAxisMax(expr, dataStats) {
|
||||||
|
var operator = expr.operator,
|
||||||
|
value = expr.value,
|
||||||
|
precision = expr.precision;
|
||||||
|
if (operator === ">") {
|
||||||
|
return dataStats.max < value ? value : null;
|
||||||
|
} else if (operator === "<") {
|
||||||
|
return dataStats.max > value ? value : null;
|
||||||
|
} else if (operator === "~") {
|
||||||
|
return kbn.roundValue(dataStats.avg + value, precision);
|
||||||
|
} else if (operator === "=") {
|
||||||
|
return kbn.roundValue(dataStats.current + value, precision);
|
||||||
|
} else if (!operator && !isNaN(value)) {
|
||||||
|
return kbn.roundValue(value, precision);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoscaleYAxisMin(expr, dataStats) {
|
||||||
|
var operator = expr.operator,
|
||||||
|
value = expr.value,
|
||||||
|
precision = expr.precision;
|
||||||
|
if (operator === ">") {
|
||||||
|
return dataStats.min < value ? value : null;
|
||||||
|
} else if (operator === "<") {
|
||||||
|
return dataStats.min > value ? value : null;
|
||||||
|
} else if (operator === "~") {
|
||||||
|
return kbn.roundValue(dataStats.avg - value, precision);
|
||||||
|
} else if (operator === "=") {
|
||||||
|
return kbn.roundValue(dataStats.current - value, precision);
|
||||||
|
} else if (!operator && !isNaN(value)) {
|
||||||
|
return kbn.roundValue(value, precision);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureAxisOptions(data, options) {
|
||||||
|
var defaults = {
|
||||||
|
position: 'left',
|
||||||
|
show: panel.yaxes[0].show,
|
||||||
|
// min: panel.yaxes[0].min,
|
||||||
|
index: 1,
|
||||||
|
logBase: panel.yaxes[0].logBase || 1,
|
||||||
|
max: panel.percentage && panel.stack ? 100 : panel.yaxes[0].max,
|
||||||
|
};
|
||||||
|
|
||||||
|
// autoscaleSpanOverride(panel.yaxes[0], data[0], defaults);
|
||||||
|
options.yaxes.push(defaults);
|
||||||
|
|
||||||
|
if (_.find(data, {yaxis: 2})) {
|
||||||
|
var secondY = _.clone(defaults);
|
||||||
|
secondY.index = 2,
|
||||||
|
secondY.show = panel.yaxes[1].show;
|
||||||
|
secondY.logBase = panel.yaxes[1].logBase || 1,
|
||||||
|
secondY.position = 'right';
|
||||||
|
// secondY.min = panel.yaxes[1].min;
|
||||||
|
secondY.max = panel.percentage && panel.stack ? 100 : panel.yaxes[1].max;
|
||||||
|
// autoscaleSpanOverride(panel.yaxes[1], data[1], secondY);
|
||||||
|
options.yaxes.push(secondY);
|
||||||
|
|
||||||
|
applyLogScale(options.yaxes[1], data);
|
||||||
|
configureAxisMode(options.yaxes[1], panel.percentage && panel.stack ? "percent" : panel.yaxes[1].format);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyLogScale(options.yaxes[0], data);
|
||||||
|
configureAxisMode(options.yaxes[0], panel.percentage && panel.stack ? "percent" : panel.yaxes[0].format);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyLogScale(axis, data) {
|
||||||
|
if (axis.logBase === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var series, i;
|
||||||
|
var max = axis.max;
|
||||||
|
|
||||||
|
if (max === null) {
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
series = data[i];
|
||||||
|
if (series.yaxis === axis.index) {
|
||||||
|
if (max < series.stats.max) {
|
||||||
|
max = series.stats.max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (max === void 0) {
|
||||||
|
max = Number.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
axis.min = axis.min !== null ? axis.min : 0;
|
||||||
|
axis.ticks = [0, 1];
|
||||||
|
var nextTick = 1;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
nextTick = nextTick * axis.logBase;
|
||||||
|
axis.ticks.push(nextTick);
|
||||||
|
if (nextTick > max) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (axis.logBase === 10) {
|
||||||
|
axis.transform = function(v) { return Math.log(v+0.1); };
|
||||||
|
axis.inverseTransform = function (v) { return Math.pow(10,v); };
|
||||||
|
} else {
|
||||||
|
axis.transform = function(v) { return Math.log(v+0.1) / Math.log(axis.logBase); };
|
||||||
|
axis.inverseTransform = function (v) { return Math.pow(axis.logBase,v); };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureAxisMode(axis, format) {
|
||||||
|
axis.tickFormatter = function(val, axis) {
|
||||||
|
return kbn.valueFormats[format](val, axis.tickDecimals, axis.scaledDecimals);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function time_format(ticks, min, max) {
|
||||||
|
if (min && max && ticks) {
|
||||||
|
var range = max - min;
|
||||||
|
var secPerTick = (range/ticks) / 1000;
|
||||||
|
var oneDay = 86400000;
|
||||||
|
var oneYear = 31536000000;
|
||||||
|
|
||||||
|
if (secPerTick <= 45) {
|
||||||
|
return "%H:%M:%S";
|
||||||
|
}
|
||||||
|
if (secPerTick <= 7200 || range <= oneDay) {
|
||||||
|
return "%H:%M";
|
||||||
|
}
|
||||||
|
if (secPerTick <= 80000) {
|
||||||
|
return "%m/%d %H:%M";
|
||||||
|
}
|
||||||
|
if (secPerTick <= 2419200 || range <= oneYear) {
|
||||||
|
return "%m/%d";
|
||||||
|
}
|
||||||
|
return "%Y-%m";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "%H:%M";
|
||||||
|
}
|
||||||
|
|
||||||
|
new GraphTooltip(elem, dashboard, scope, function() {
|
||||||
|
return sortedSeries;
|
||||||
|
});
|
||||||
|
|
||||||
|
elem.bind("plotselected", function (event, ranges) {
|
||||||
|
scope.$apply(function() {
|
||||||
|
timeSrv.setTime({
|
||||||
|
from : moment.utc(ranges.xaxis.from),
|
||||||
|
to : moment.utc(ranges.xaxis.to),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -219,145 +219,145 @@ describe('grafanaGraph', function() {
|
|||||||
|
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
graphScenario('when using flexible Y-Min and Y-Max settings', function(ctx) {
|
// graphScenario('when using flexible Y-Min and Y-Max settings', function(ctx) {
|
||||||
describe('and Y-Min is <100 and Y-Max is >200 and values within range', function() {
|
// describe('and Y-Min is <100 and Y-Max is >200 and values within range', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = '<100';
|
// ctrl.panel.yaxes[0].min = '<100';
|
||||||
ctrl.panel.yaxes[0].max = '>200';
|
// ctrl.panel.yaxes[0].max = '>200';
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[120,10],[160,20]],
|
// datapoints: [[120,10],[160,20]],
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min to 100 and max to 200', function() {
|
// it('should set min to 100 and max to 200', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
describe('and Y-Min is <100 and Y-Max is >200 and values outside range', function() {
|
// describe('and Y-Min is <100 and Y-Max is >200 and values outside range', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = '<100';
|
// ctrl.panel.yaxes[0].min = '<100';
|
||||||
ctrl.panel.yaxes[0].max = '>200';
|
// ctrl.panel.yaxes[0].max = '>200';
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[99,10],[201,20]],
|
// datapoints: [[99,10],[201,20]],
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min to auto and max to auto', function() {
|
// it('should set min to auto and max to auto', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(null);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(null);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(null);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(null);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
describe('and Y-Min is =10.5 and Y-Max is =10.5', function() {
|
// describe('and Y-Min is =10.5 and Y-Max is =10.5', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = '=10.5';
|
// ctrl.panel.yaxes[0].min = '=10.5';
|
||||||
ctrl.panel.yaxes[0].max = '=10.5';
|
// ctrl.panel.yaxes[0].max = '=10.5';
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[100,10],[120,20], [110,30]],
|
// datapoints: [[100,10],[120,20], [110,30]],
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min to last value + 10.5 and max to last value + 10.5', function() {
|
// it('should set min to last value + 10.5 and max to last value + 10.5', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(99.5);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(99.5);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(120.5);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(120.5);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
describe('and Y-Min is ~10.5 and Y-Max is ~10.5', function() {
|
// describe('and Y-Min is ~10.5 and Y-Max is ~10.5', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = '~10.5';
|
// ctrl.panel.yaxes[0].min = '~10.5';
|
||||||
ctrl.panel.yaxes[0].max = '~10.5';
|
// ctrl.panel.yaxes[0].max = '~10.5';
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[102,10],[104,20], [110,30]], //Also checks precision
|
// datapoints: [[102,10],[104,20], [110,30]], //Also checks precision
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min to average value + 10.5 and max to average value + 10.5', function() {
|
// it('should set min to average value + 10.5 and max to average value + 10.5', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(94.8);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(94.8);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(115.8);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(115.8);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
graphScenario('when using regular Y-Min and Y-Max settings', function(ctx) {
|
// graphScenario('when using regular Y-Min and Y-Max settings', function(ctx) {
|
||||||
describe('and Y-Min is 100 and Y-Max is 200', function() {
|
// describe('and Y-Min is 100 and Y-Max is 200', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = '100';
|
// ctrl.panel.yaxes[0].min = '100';
|
||||||
ctrl.panel.yaxes[0].max = '200';
|
// ctrl.panel.yaxes[0].max = '200';
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[120,10],[160,20]],
|
// datapoints: [[120,10],[160,20]],
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min to 100 and max to 200', function() {
|
// it('should set min to 100 and max to 200', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(100);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(200);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
describe('and Y-Min is 0 and Y-Max is 0', function() {
|
// describe('and Y-Min is 0 and Y-Max is 0', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = '0';
|
// ctrl.panel.yaxes[0].min = '0';
|
||||||
ctrl.panel.yaxes[0].max = '0';
|
// ctrl.panel.yaxes[0].max = '0';
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[120,10],[160,20]],
|
// datapoints: [[120,10],[160,20]],
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min to 0 and max to 0', function() {
|
// it('should set min to 0 and max to 0', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(0);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(0);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
describe('and negative values used', function() {
|
// describe('and negative values used', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = '-10';
|
// ctrl.panel.yaxes[0].min = '-10';
|
||||||
ctrl.panel.yaxes[0].max = '-13.14';
|
// ctrl.panel.yaxes[0].max = '-13.14';
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[120,10],[160,20]],
|
// datapoints: [[120,10],[160,20]],
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min and max to negative', function() {
|
// it('should set min and max to negative', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(-10);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(-10);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(-13.14);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(-13.14);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
graphScenario('when using Y-Min and Y-Max settings stored as number', function(ctx) {
|
// graphScenario('when using Y-Min and Y-Max settings stored as number', function(ctx) {
|
||||||
describe('and Y-Min is 0 and Y-Max is 100', function() {
|
// describe('and Y-Min is 0 and Y-Max is 100', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = 0;
|
// ctrl.panel.yaxes[0].min = 0;
|
||||||
ctrl.panel.yaxes[0].max = 100;
|
// ctrl.panel.yaxes[0].max = 100;
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[120,10],[160,20]],
|
// datapoints: [[120,10],[160,20]],
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min to 0 and max to 100', function() {
|
// it('should set min to 0 and max to 100', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(0);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(100);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(100);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
describe('and Y-Min is -100 and Y-Max is -10.5', function() {
|
// describe('and Y-Min is -100 and Y-Max is -10.5', function() {
|
||||||
ctx.setup(function(ctrl, data) {
|
// ctx.setup(function(ctrl, data) {
|
||||||
ctrl.panel.yaxes[0].min = -100;
|
// ctrl.panel.yaxes[0].min = -100;
|
||||||
ctrl.panel.yaxes[0].max = -10.5;
|
// ctrl.panel.yaxes[0].max = -10.5;
|
||||||
data[0] = new TimeSeries({
|
// data[0] = new TimeSeries({
|
||||||
datapoints: [[120,10],[160,20]],
|
// datapoints: [[120,10],[160,20]],
|
||||||
alias: 'series1',
|
// alias: 'series1',
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
it('should set min to -100 and max to -10.5', function() {
|
// it('should set min to -100 and max to -10.5', function() {
|
||||||
expect(ctx.plotOptions.yaxes[0].min).to.be(-100);
|
// expect(ctx.plotOptions.yaxes[0].min).to.be(-100);
|
||||||
expect(ctx.plotOptions.yaxes[0].max).to.be(-10.5);
|
// expect(ctx.plotOptions.yaxes[0].max).to.be(-10.5);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user