1066 lines
46 KiB
JavaScript
1066 lines
46 KiB
JavaScript
// dygraph
|
|
|
|
// Codacy declarations
|
|
/* global smoothPlotter */
|
|
/* global Dygraph */
|
|
|
|
NETDATA.dygraph = {
|
|
smooth: false
|
|
};
|
|
|
|
NETDATA.dygraphToolboxPanAndZoom = function (state, after, before) {
|
|
if (after < state.netdata_first) {
|
|
after = state.netdata_first;
|
|
}
|
|
|
|
if (before > state.netdata_last) {
|
|
before = state.netdata_last;
|
|
}
|
|
|
|
state.setMode('zoom');
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
state.tmp.dygraph_user_action = true;
|
|
state.tmp.dygraph_force_zoom = true;
|
|
// state.log('toolboxPanAndZoom');
|
|
state.updateChartPanOrZoom(after, before);
|
|
NETDATA.globalPanAndZoom.setMaster(state, after, before);
|
|
};
|
|
|
|
NETDATA.dygraphSetSelection = function (state, t) {
|
|
if (typeof state.tmp.dygraph_instance !== 'undefined') {
|
|
let r = state.calculateRowForTime(t);
|
|
if (r !== -1) {
|
|
state.tmp.dygraph_instance.setSelection(r);
|
|
return true;
|
|
} else {
|
|
state.tmp.dygraph_instance.clearSelection();
|
|
state.legendShowUndefined();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
NETDATA.dygraphClearSelection = function (state) {
|
|
if (typeof state.tmp.dygraph_instance !== 'undefined') {
|
|
state.tmp.dygraph_instance.clearSelection();
|
|
}
|
|
return true;
|
|
};
|
|
|
|
NETDATA.dygraphSmoothInitialize = function (callback) {
|
|
$.ajax({
|
|
url: NETDATA.dygraph_smooth_js,
|
|
cache: true,
|
|
dataType: "script",
|
|
xhrFields: {withCredentials: true} // required for the cookie
|
|
})
|
|
.done(function () {
|
|
NETDATA.dygraph.smooth = true;
|
|
smoothPlotter.smoothing = 0.3;
|
|
})
|
|
.fail(function () {
|
|
NETDATA.dygraph.smooth = false;
|
|
})
|
|
.always(function () {
|
|
if (typeof callback === "function") {
|
|
return callback();
|
|
}
|
|
});
|
|
};
|
|
|
|
NETDATA.dygraphInitialize = function (callback) {
|
|
if (typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
|
|
$.ajax({
|
|
url: NETDATA.dygraph_js,
|
|
cache: true,
|
|
dataType: "script",
|
|
xhrFields: {withCredentials: true} // required for the cookie
|
|
})
|
|
.done(function () {
|
|
NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
|
|
})
|
|
.fail(function () {
|
|
NETDATA.chartLibraries.dygraph.enabled = false;
|
|
NETDATA.error(100, NETDATA.dygraph_js);
|
|
})
|
|
.always(function () {
|
|
if (NETDATA.chartLibraries.dygraph.enabled && NETDATA.options.current.smooth_plot) {
|
|
NETDATA.dygraphSmoothInitialize(callback);
|
|
} else if (typeof callback === "function") {
|
|
return callback();
|
|
}
|
|
});
|
|
} else {
|
|
NETDATA.chartLibraries.dygraph.enabled = false;
|
|
if (typeof callback === "function") {
|
|
return callback();
|
|
}
|
|
}
|
|
};
|
|
|
|
NETDATA.dygraphChartUpdate = function (state, data) {
|
|
let dygraph = state.tmp.dygraph_instance;
|
|
|
|
if (typeof dygraph === 'undefined') {
|
|
return NETDATA.dygraphChartCreate(state, data);
|
|
}
|
|
|
|
// when the chart is not visible, and hidden
|
|
// if there is a window resize, dygraph detects
|
|
// its element size as 0x0.
|
|
// this will make it re-appear properly
|
|
|
|
if (state.tm.last_unhidden > state.tmp.dygraph_last_rendered) {
|
|
dygraph.resize();
|
|
}
|
|
|
|
let options = {
|
|
file: data.result.data,
|
|
colors: state.chartColors(),
|
|
labels: data.result.labels,
|
|
//labelsDivWidth: state.chartWidth() - 70,
|
|
includeZero: state.tmp.dygraph_include_zero,
|
|
visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
|
|
};
|
|
|
|
if (state.tmp.dygraph_chart_type === 'stacked') {
|
|
if (options.includeZero && state.dimensions_visibility.countSelected() < options.visibility.length) {
|
|
options.includeZero = 0;
|
|
}
|
|
}
|
|
|
|
if (!NETDATA.chartLibraries.dygraph.isSparkline(state)) {
|
|
options.ylabel = state.units_current; // (state.units_desired === 'auto')?"":state.units_current;
|
|
}
|
|
|
|
if (state.tmp.dygraph_force_zoom) {
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('dygraphChartUpdate() forced zoom update');
|
|
}
|
|
|
|
options.dateWindow = (state.requested_padding !== null) ? [state.view_after, state.view_before] : null;
|
|
//options.isZoomedIgnoreProgrammaticZoom = true;
|
|
state.tmp.dygraph_force_zoom = false;
|
|
} else if (state.current.name !== 'auto') {
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('dygraphChartUpdate() loose update');
|
|
}
|
|
} else {
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('dygraphChartUpdate() strict update');
|
|
}
|
|
|
|
options.dateWindow = (state.requested_padding !== null) ? [state.view_after, state.view_before] : null;
|
|
//options.isZoomedIgnoreProgrammaticZoom = true;
|
|
}
|
|
|
|
options.valueRange = state.tmp.dygraph_options.valueRange;
|
|
|
|
let oldMax = null, oldMin = null;
|
|
if (state.tmp.__commonMin !== null) {
|
|
state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0];
|
|
oldMin = options.valueRange[0] = NETDATA.commonMin.get(state);
|
|
}
|
|
if (state.tmp.__commonMax !== null) {
|
|
state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1];
|
|
oldMax = options.valueRange[1] = NETDATA.commonMax.get(state);
|
|
}
|
|
|
|
if (state.tmp.dygraph_smooth_eligible) {
|
|
if ((NETDATA.options.current.smooth_plot && state.tmp.dygraph_options.plotter !== smoothPlotter)
|
|
|| (NETDATA.options.current.smooth_plot === false && state.tmp.dygraph_options.plotter === smoothPlotter)) {
|
|
NETDATA.dygraphChartCreate(state, data);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (netdataSnapshotData !== null && NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(state) === false) {
|
|
// pan and zoom on snapshots
|
|
options.dateWindow = [NETDATA.globalPanAndZoom.force_after_ms, NETDATA.globalPanAndZoom.force_before_ms];
|
|
//options.isZoomedIgnoreProgrammaticZoom = true;
|
|
}
|
|
|
|
if (NETDATA.chartLibraries.dygraph.isLogScale(state)) {
|
|
if (Array.isArray(options.valueRange) && options.valueRange[0] <= 0) {
|
|
options.valueRange[0] = null;
|
|
}
|
|
}
|
|
|
|
dygraph.updateOptions(options);
|
|
|
|
let redraw = false;
|
|
if (oldMin !== null && oldMin > state.tmp.dygraph_instance.axes_[0].extremeRange[0]) {
|
|
state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0];
|
|
options.valueRange[0] = NETDATA.commonMin.get(state);
|
|
redraw = true;
|
|
}
|
|
if (oldMax !== null && oldMax < state.tmp.dygraph_instance.axes_[0].extremeRange[1]) {
|
|
state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1];
|
|
options.valueRange[1] = NETDATA.commonMax.get(state);
|
|
redraw = true;
|
|
}
|
|
|
|
if (redraw) {
|
|
// state.log('forcing redraw to adapt to common- min/max');
|
|
dygraph.updateOptions(options);
|
|
}
|
|
|
|
state.tmp.dygraph_last_rendered = Date.now();
|
|
return true;
|
|
};
|
|
|
|
NETDATA.dygraphChartCreate = function (state, data) {
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('dygraphChartCreate()');
|
|
}
|
|
|
|
state.tmp.dygraph_chart_type = NETDATA.dataAttribute(state.element, 'dygraph-type', state.chart.chart_type);
|
|
if (state.tmp.dygraph_chart_type === 'stacked' && data.dimensions === 1) {
|
|
state.tmp.dygraph_chart_type = 'area';
|
|
}
|
|
if (state.tmp.dygraph_chart_type === 'stacked' && NETDATA.chartLibraries.dygraph.isLogScale(state)) {
|
|
state.tmp.dygraph_chart_type = 'area';
|
|
}
|
|
|
|
let highlightCircleSize = NETDATA.chartLibraries.dygraph.isSparkline(state) ? 3 : 4;
|
|
|
|
let smooth = NETDATA.dygraph.smooth
|
|
? (NETDATA.dataAttributeBoolean(state.element, 'dygraph-smooth', (state.tmp.dygraph_chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false)))
|
|
: false;
|
|
|
|
state.tmp.dygraph_include_zero = NETDATA.dataAttribute(state.element, 'dygraph-includezero', (state.tmp.dygraph_chart_type === 'stacked'));
|
|
let drawAxis = NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawaxis', true);
|
|
|
|
state.tmp.dygraph_options = {
|
|
colors: NETDATA.dataAttribute(state.element, 'dygraph-colors', state.chartColors()),
|
|
|
|
// leave a few pixels empty on the right of the chart
|
|
rightGap: NETDATA.dataAttribute(state.element, 'dygraph-rightgap', 5),
|
|
showRangeSelector: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showrangeselector', false),
|
|
showRoller: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showroller', false),
|
|
title: NETDATA.dataAttribute(state.element, 'dygraph-title', state.title),
|
|
titleHeight: NETDATA.dataAttribute(state.element, 'dygraph-titleheight', 19),
|
|
legend: NETDATA.dataAttribute(state.element, 'dygraph-legend', 'always'), // we need this to get selection events
|
|
labels: data.result.labels,
|
|
labelsDiv: NETDATA.dataAttribute(state.element, 'dygraph-labelsdiv', state.element_legend_childs.hidden),
|
|
//labelsDivStyles: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivstyles', { 'fontSize':'1px' }),
|
|
//labelsDivWidth: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivwidth', state.chartWidth() - 70),
|
|
labelsSeparateLines: NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsseparatelines', true),
|
|
labelsShowZeroValues: NETDATA.chartLibraries.dygraph.isLogScale(state) ? false : NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsshowzerovalues', true),
|
|
labelsKMB: false,
|
|
labelsKMG2: false,
|
|
showLabelsOnHighlight: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showlabelsonhighlight', true),
|
|
hideOverlayOnMouseOut: NETDATA.dataAttributeBoolean(state.element, 'dygraph-hideoverlayonmouseout', true),
|
|
includeZero: state.tmp.dygraph_include_zero,
|
|
xRangePad: NETDATA.dataAttribute(state.element, 'dygraph-xrangepad', 0),
|
|
yRangePad: NETDATA.dataAttribute(state.element, 'dygraph-yrangepad', 1),
|
|
valueRange: NETDATA.dataAttribute(state.element, 'dygraph-valuerange', [null, null]),
|
|
ylabel: state.units_current, // (state.units_desired === 'auto')?"":state.units_current,
|
|
yLabelWidth: NETDATA.dataAttribute(state.element, 'dygraph-ylabelwidth', 12),
|
|
|
|
// the function to plot the chart
|
|
plotter: null,
|
|
|
|
// The width of the lines connecting data points.
|
|
// This can be used to increase the contrast or some graphs.
|
|
strokeWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokewidth', ((state.tmp.dygraph_chart_type === 'stacked') ? 0.1 : ((smooth === true) ? 1.5 : 0.7))),
|
|
strokePattern: NETDATA.dataAttribute(state.element, 'dygraph-strokepattern', undefined),
|
|
|
|
// The size of the dot to draw on each point in pixels (see drawPoints).
|
|
// A dot is always drawn when a point is "isolated",
|
|
// i.e. there is a missing point on either side of it.
|
|
// This also controls the size of those dots.
|
|
drawPoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawpoints', false),
|
|
|
|
// Draw points at the edges of gaps in the data.
|
|
// This improves visibility of small data segments or other data irregularities.
|
|
drawGapEdgePoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgapedgepoints', true),
|
|
connectSeparatedPoints: NETDATA.chartLibraries.dygraph.isLogScale(state) ? false : NETDATA.dataAttributeBoolean(state.element, 'dygraph-connectseparatedpoints', false),
|
|
pointSize: NETDATA.dataAttribute(state.element, 'dygraph-pointsize', 1),
|
|
|
|
// enabling this makes the chart with little square lines
|
|
stepPlot: NETDATA.dataAttributeBoolean(state.element, 'dygraph-stepplot', false),
|
|
|
|
// Draw a border around graph lines to make crossing lines more easily
|
|
// distinguishable. Useful for graphs with many lines.
|
|
strokeBorderColor: NETDATA.dataAttribute(state.element, 'dygraph-strokebordercolor', NETDATA.themes.current.background),
|
|
strokeBorderWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokeborderwidth', (state.tmp.dygraph_chart_type === 'stacked') ? 0.0 : 0.0),
|
|
fillGraph: NETDATA.dataAttribute(state.element, 'dygraph-fillgraph', (state.tmp.dygraph_chart_type === 'area' || state.tmp.dygraph_chart_type === 'stacked')),
|
|
fillAlpha: NETDATA.dataAttribute(state.element, 'dygraph-fillalpha',
|
|
((state.tmp.dygraph_chart_type === 'stacked')
|
|
? NETDATA.options.current.color_fill_opacity_stacked
|
|
: NETDATA.options.current.color_fill_opacity_area)
|
|
),
|
|
stackedGraph: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraph', (state.tmp.dygraph_chart_type === 'stacked')),
|
|
stackedGraphNaNFill: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraphnanfill', 'none'),
|
|
drawAxis: drawAxis,
|
|
axisLabelFontSize: NETDATA.dataAttribute(state.element, 'dygraph-axislabelfontsize', 10),
|
|
axisLineColor: NETDATA.dataAttribute(state.element, 'dygraph-axislinecolor', NETDATA.themes.current.axis),
|
|
axisLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-axislinewidth', 1.0),
|
|
drawGrid: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgrid', true),
|
|
gridLinePattern: NETDATA.dataAttribute(state.element, 'dygraph-gridlinepattern', null),
|
|
gridLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-gridlinewidth', 1.0),
|
|
gridLineColor: NETDATA.dataAttribute(state.element, 'dygraph-gridlinecolor', NETDATA.themes.current.grid),
|
|
maxNumberWidth: NETDATA.dataAttribute(state.element, 'dygraph-maxnumberwidth', 8),
|
|
sigFigs: NETDATA.dataAttribute(state.element, 'dygraph-sigfigs', null),
|
|
digitsAfterDecimal: NETDATA.dataAttribute(state.element, 'dygraph-digitsafterdecimal', 2),
|
|
valueFormatter: NETDATA.dataAttribute(state.element, 'dygraph-valueformatter', undefined),
|
|
highlightCircleSize: NETDATA.dataAttribute(state.element, 'dygraph-highlightcirclesize', highlightCircleSize),
|
|
highlightSeriesOpts: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesopts', null), // TOO SLOW: { strokeWidth: 1.5 },
|
|
highlightSeriesBackgroundAlpha: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesbackgroundalpha', null), // TOO SLOW: (state.tmp.dygraph_chart_type === 'stacked')?0.7:0.5,
|
|
pointClickCallback: NETDATA.dataAttribute(state.element, 'dygraph-pointclickcallback', undefined),
|
|
visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names),
|
|
logscale: NETDATA.chartLibraries.dygraph.isLogScale(state) ? 'y' : undefined,
|
|
|
|
// Expects a string in the format "<series name>: <style>" where each series is separated by a |
|
|
perSeriesStyle: NETDATA.dataAttribute(state.element, 'dygraph-per-series-style', ''),
|
|
|
|
axes: {
|
|
x: {
|
|
pixelsPerLabel: NETDATA.dataAttribute(state.element, 'dygraph-xpixelsperlabel', 50),
|
|
ticker: Dygraph.dateTicker,
|
|
axisLabelWidth: NETDATA.dataAttribute(state.element, 'dygraph-xaxislabelwidth', 60),
|
|
drawAxis: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawxaxis', drawAxis),
|
|
axisLabelFormatter: function (d, gran) {
|
|
void(gran);
|
|
return NETDATA.dateTime.xAxisTimeString(d);
|
|
}
|
|
},
|
|
y: {
|
|
logscale: NETDATA.chartLibraries.dygraph.isLogScale(state) ? true : undefined,
|
|
pixelsPerLabel: NETDATA.dataAttribute(state.element, 'dygraph-ypixelsperlabel', 15),
|
|
axisLabelWidth: NETDATA.dataAttribute(state.element, 'dygraph-yaxislabelwidth', 50),
|
|
drawAxis: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawyaxis', drawAxis),
|
|
axisLabelFormatter: function (y) {
|
|
|
|
// unfortunately, we have to call this every single time
|
|
state.legendFormatValueDecimalsFromMinMax(
|
|
this.axes_[0].extremeRange[0],
|
|
this.axes_[0].extremeRange[1]
|
|
);
|
|
|
|
let old_units = this.user_attrs_.ylabel;
|
|
let v = state.legendFormatValue(y);
|
|
let new_units = state.units_current;
|
|
|
|
if (state.units_desired === 'auto' && typeof old_units !== 'undefined' && new_units !== old_units && !NETDATA.chartLibraries.dygraph.isSparkline(state)) {
|
|
// console.log(this);
|
|
// state.log('units discrepancy: old = ' + old_units + ', new = ' + new_units);
|
|
let len = this.plugins_.length;
|
|
while (len--) {
|
|
// console.log(this.plugins_[len]);
|
|
if (typeof this.plugins_[len].plugin.ylabel_div_ !== 'undefined'
|
|
&& this.plugins_[len].plugin.ylabel_div_ !== null
|
|
&& typeof this.plugins_[len].plugin.ylabel_div_.children !== 'undefined'
|
|
&& this.plugins_[len].plugin.ylabel_div_.children !== null
|
|
&& typeof this.plugins_[len].plugin.ylabel_div_.children[0].children !== 'undefined'
|
|
&& this.plugins_[len].plugin.ylabel_div_.children[0].children !== null
|
|
) {
|
|
this.plugins_[len].plugin.ylabel_div_.children[0].children[0].innerHTML = new_units;
|
|
this.user_attrs_.ylabel = new_units;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (len < 0) {
|
|
state.log('units discrepancy, but cannot find dygraphs div to change: old = ' + old_units + ', new = ' + new_units);
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
}
|
|
},
|
|
legendFormatter: function (data) {
|
|
if (state.tmp.dygraph_mouse_down) {
|
|
return;
|
|
}
|
|
|
|
let elements = state.element_legend_childs;
|
|
|
|
// if the hidden div is not there
|
|
// we are not managing the legend
|
|
if (elements.hidden === null) {
|
|
return;
|
|
}
|
|
|
|
if (typeof data.x !== 'undefined') {
|
|
state.legendSetDate(data.x);
|
|
let i = data.series.length;
|
|
while (i--) {
|
|
let series = data.series[i];
|
|
if (series.isVisible) {
|
|
state.legendSetLabelValue(series.label, series.y);
|
|
} else {
|
|
state.legendSetLabelValue(series.label, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
return '';
|
|
},
|
|
drawCallback: function (dygraph, is_initial) {
|
|
|
|
// the user has panned the chart and this is called to re-draw the chart
|
|
// 1. refresh this chart by adding data to it
|
|
// 2. notify all the other charts about the update they need
|
|
|
|
// to prevent an infinite loop (feedback), we use
|
|
// state.tmp.dygraph_user_action
|
|
// - when true, this is initiated by a user
|
|
// - when false, this is feedback
|
|
|
|
if (state.current.name !== 'auto' && state.tmp.dygraph_user_action) {
|
|
state.tmp.dygraph_user_action = false;
|
|
|
|
let x_range = dygraph.xAxisRange();
|
|
let after = Math.round(x_range[0]);
|
|
let before = Math.round(x_range[1]);
|
|
|
|
if (NETDATA.options.debug.dygraph) {
|
|
state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): mode ' + state.current.name + ' ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
|
|
//console.log(state);
|
|
}
|
|
|
|
if (before <= state.netdata_last && after >= state.netdata_first) {
|
|
// update only when we are within the data limits
|
|
state.updateChartPanOrZoom(after, before);
|
|
}
|
|
}
|
|
},
|
|
zoomCallback: function (minDate, maxDate, yRanges) {
|
|
|
|
// the user has selected a range on the chart
|
|
// 1. refresh this chart by adding data to it
|
|
// 2. notify all the other charts about the update they need
|
|
|
|
void(yRanges);
|
|
|
|
if (NETDATA.options.debug.dygraph) {
|
|
state.log('dygraphZoomCallback(): ' + state.current.name);
|
|
}
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
state.setMode('zoom');
|
|
|
|
// refresh it to the greatest possible zoom level
|
|
state.tmp.dygraph_user_action = true;
|
|
state.tmp.dygraph_force_zoom = true;
|
|
state.updateChartPanOrZoom(minDate, maxDate);
|
|
},
|
|
highlightCallback: function (event, x, points, row, seriesName) {
|
|
void(seriesName);
|
|
|
|
state.pauseChart();
|
|
|
|
// there is a bug in dygraph when the chart is zoomed enough
|
|
// the time it thinks is selected is wrong
|
|
// here we calculate the time t based on the row number selected
|
|
// which is ok
|
|
// let t = state.data_after + row * state.data_update_every;
|
|
// console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t === x)?'SAME':(Math.abs(x-t)<=state.data_update_every)?'SIMILAR':'DIFFERENT') + ', rows in db: ' + state.data_points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.data_after + ' - ' + state.data_before + ' real: ' + state.data.after + ' - ' + state.data.before + ' every: ' + state.data_update_every);
|
|
|
|
if (state.tmp.dygraph_mouse_down !== true) {
|
|
NETDATA.globalSelectionSync.sync(state, x);
|
|
}
|
|
|
|
// fix legend zIndex using the internal structures of dygraph legend module
|
|
// this works, but it is a hack!
|
|
// state.tmp.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000;
|
|
},
|
|
unhighlightCallback: function (event) {
|
|
void(event);
|
|
|
|
if (state.tmp.dygraph_mouse_down) {
|
|
return;
|
|
}
|
|
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('dygraphUnhighlightCallback()');
|
|
}
|
|
|
|
state.unpauseChart();
|
|
NETDATA.globalSelectionSync.stop();
|
|
},
|
|
underlayCallback: function (canvas, area, g) {
|
|
// the chart is about to be drawn
|
|
|
|
// update history_tip_element
|
|
if (state.tmp.dygraph_history_tip_element) {
|
|
const xHookRightSide = g.toDomXCoord(state.netdata_first);
|
|
if (xHookRightSide > area.x) {
|
|
state.tmp.dygraph_history_tip_element_displayed = true;
|
|
// group the styles for possible better performance
|
|
state.tmp.dygraph_history_tip_element.setAttribute(
|
|
'style',
|
|
`display: block; left: ${area.x}px; right: calc(100% - ${xHookRightSide}px);`
|
|
)
|
|
} else {
|
|
if (state.tmp.dygraph_history_tip_element_displayed) {
|
|
// additional check just for performance
|
|
// don't update the DOM when it's not needed
|
|
state.tmp.dygraph_history_tip_element.style.display = 'none';
|
|
state.tmp.dygraph_history_tip_element_displayed = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// this function renders global highlighted time-frame
|
|
|
|
if (NETDATA.globalChartUnderlay.isActive()) {
|
|
let after = NETDATA.globalChartUnderlay.after;
|
|
let before = NETDATA.globalChartUnderlay.before;
|
|
|
|
if (after < state.view_after) {
|
|
after = state.view_after;
|
|
}
|
|
|
|
if (before > state.view_before) {
|
|
before = state.view_before;
|
|
}
|
|
|
|
if (after < before) {
|
|
let bottom_left = g.toDomCoords(after, -20);
|
|
let top_right = g.toDomCoords(before, +20);
|
|
|
|
let left = bottom_left[0];
|
|
let right = top_right[0];
|
|
|
|
canvas.fillStyle = NETDATA.themes.current.highlight;
|
|
canvas.fillRect(left, area.y, right - left, area.h);
|
|
}
|
|
}
|
|
},
|
|
interactionModel: {
|
|
mousedown: function (event, dygraph, context) {
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.mousedown()');
|
|
}
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
|
|
if (NETDATA.options.debug.dygraph) {
|
|
state.log('dygraphMouseDown()');
|
|
}
|
|
|
|
// Right-click should not initiate anything.
|
|
if (event.button && event.button === 2) {
|
|
return;
|
|
}
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
state.tmp.dygraph_mouse_down = true;
|
|
context.initializeMouseDown(event, dygraph, context);
|
|
|
|
//console.log(event);
|
|
if (event.button && event.button === 1) {
|
|
if (event.shiftKey) {
|
|
//console.log('middle mouse button dragging (PAN)');
|
|
|
|
state.setMode('pan');
|
|
// NETDATA.globalSelectionSync.delay();
|
|
state.tmp.dygraph_highlight_after = null;
|
|
Dygraph.startPan(event, dygraph, context);
|
|
} else if (event.altKey || event.ctrlKey || event.metaKey) {
|
|
//console.log('middle mouse button highlight');
|
|
|
|
if (!(event.offsetX && event.offsetY)) {
|
|
event.offsetX = event.layerX - event.target.offsetLeft;
|
|
event.offsetY = event.layerY - event.target.offsetTop;
|
|
}
|
|
state.tmp.dygraph_highlight_after = dygraph.toDataXCoord(event.offsetX);
|
|
Dygraph.startZoom(event, dygraph, context);
|
|
} else {
|
|
//console.log('middle mouse button selection for zoom (ZOOM)');
|
|
|
|
state.setMode('zoom');
|
|
// NETDATA.globalSelectionSync.delay();
|
|
state.tmp.dygraph_highlight_after = null;
|
|
Dygraph.startZoom(event, dygraph, context);
|
|
}
|
|
} else {
|
|
if (event.shiftKey) {
|
|
//console.log('left mouse button selection for zoom (ZOOM)');
|
|
|
|
state.setMode('zoom');
|
|
// NETDATA.globalSelectionSync.delay();
|
|
state.tmp.dygraph_highlight_after = null;
|
|
Dygraph.startZoom(event, dygraph, context);
|
|
} else if (event.altKey || event.ctrlKey || event.metaKey) {
|
|
//console.log('left mouse button highlight');
|
|
|
|
if (!(event.offsetX && event.offsetY)) {
|
|
event.offsetX = event.layerX - event.target.offsetLeft;
|
|
event.offsetY = event.layerY - event.target.offsetTop;
|
|
}
|
|
state.tmp.dygraph_highlight_after = dygraph.toDataXCoord(event.offsetX);
|
|
Dygraph.startZoom(event, dygraph, context);
|
|
} else {
|
|
//console.log('left mouse button dragging (PAN)');
|
|
|
|
state.setMode('pan');
|
|
// NETDATA.globalSelectionSync.delay();
|
|
state.tmp.dygraph_highlight_after = null;
|
|
Dygraph.startPan(event, dygraph, context);
|
|
}
|
|
}
|
|
},
|
|
mousemove: function (event, dygraph, context) {
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.mousemove()');
|
|
}
|
|
|
|
if (state.tmp.dygraph_highlight_after !== null) {
|
|
//console.log('highlight selection...');
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
Dygraph.moveZoom(event, dygraph, context);
|
|
event.preventDefault();
|
|
} else if (context.isPanning) {
|
|
//console.log('panning...');
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
//NETDATA.globalSelectionSync.stop();
|
|
//NETDATA.globalSelectionSync.delay();
|
|
state.setMode('pan');
|
|
context.is2DPan = false;
|
|
Dygraph.movePan(event, dygraph, context);
|
|
} else if (context.isZooming) {
|
|
//console.log('zooming...');
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
//NETDATA.globalSelectionSync.stop();
|
|
//NETDATA.globalSelectionSync.delay();
|
|
state.setMode('zoom');
|
|
Dygraph.moveZoom(event, dygraph, context);
|
|
}
|
|
},
|
|
mouseup: function (event, dygraph, context) {
|
|
state.tmp.dygraph_mouse_down = false;
|
|
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.mouseup()');
|
|
}
|
|
|
|
if (state.tmp.dygraph_highlight_after !== null) {
|
|
//console.log('done highlight selection');
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
if (!(event.offsetX && event.offsetY)) {
|
|
event.offsetX = event.layerX - event.target.offsetLeft;
|
|
event.offsetY = event.layerY - event.target.offsetTop;
|
|
}
|
|
|
|
NETDATA.globalChartUnderlay.set(state
|
|
, state.tmp.dygraph_highlight_after
|
|
, dygraph.toDataXCoord(event.offsetX)
|
|
, state.view_after
|
|
, state.view_before
|
|
);
|
|
|
|
state.tmp.dygraph_highlight_after = null;
|
|
|
|
context.isZooming = false;
|
|
dygraph.clearZoomRect_();
|
|
dygraph.drawGraph_(false);
|
|
|
|
// refresh all the charts immediately
|
|
NETDATA.options.auto_refresher_stop_until = 0;
|
|
} else if (context.isPanning) {
|
|
//console.log('done panning');
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
Dygraph.endPan(event, dygraph, context);
|
|
|
|
// refresh all the charts immediately
|
|
NETDATA.options.auto_refresher_stop_until = 0;
|
|
} else if (context.isZooming) {
|
|
//console.log('done zomming');
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
Dygraph.endZoom(event, dygraph, context);
|
|
|
|
// refresh all the charts immediately
|
|
NETDATA.options.auto_refresher_stop_until = 0;
|
|
}
|
|
},
|
|
click: function (event, dygraph, context) {
|
|
void(dygraph);
|
|
void(context);
|
|
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.click()');
|
|
}
|
|
|
|
event.preventDefault();
|
|
},
|
|
dblclick: function (event, dygraph, context) {
|
|
void(event);
|
|
void(dygraph);
|
|
void(context);
|
|
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.dblclick()');
|
|
}
|
|
NETDATA.resetAllCharts(state);
|
|
},
|
|
wheel: function (event, dygraph, context) {
|
|
void(context);
|
|
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.wheel()');
|
|
}
|
|
|
|
// Take the offset of a mouse event on the dygraph canvas and
|
|
// convert it to a pair of percentages from the bottom left.
|
|
// (Not top left, bottom is where the lower value is.)
|
|
function offsetToPercentage(g, offsetX, offsetY) {
|
|
// This is calculating the pixel offset of the leftmost date.
|
|
let xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
|
|
let yar0 = g.yAxisRange(0);
|
|
|
|
// This is calculating the pixel of the highest value. (Top pixel)
|
|
let yOffset = g.toDomCoords(null, yar0[1])[1];
|
|
|
|
// x y w and h are relative to the corner of the drawing area,
|
|
// so that the upper corner of the drawing area is (0, 0).
|
|
let x = offsetX - xOffset;
|
|
let y = offsetY - yOffset;
|
|
|
|
// This is computing the rightmost pixel, effectively defining the
|
|
// width.
|
|
let w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
|
|
|
|
// This is computing the lowest pixel, effectively defining the height.
|
|
let h = g.toDomCoords(null, yar0[0])[1] - yOffset;
|
|
|
|
// Percentage from the left.
|
|
let xPct = w === 0 ? 0 : (x / w);
|
|
// Percentage from the top.
|
|
let yPct = h === 0 ? 0 : (y / h);
|
|
|
|
// The (1-) part below changes it from "% distance down from the top"
|
|
// to "% distance up from the bottom".
|
|
return [xPct, (1 - yPct)];
|
|
}
|
|
|
|
// Adjusts [x, y] toward each other by zoomInPercentage%
|
|
// Split it so the left/bottom axis gets xBias/yBias of that change and
|
|
// tight/top gets (1-xBias)/(1-yBias) of that change.
|
|
//
|
|
// If a bias is missing it splits it down the middle.
|
|
function zoomRange(g, zoomInPercentage, xBias, yBias) {
|
|
xBias = xBias || 0.5;
|
|
yBias = yBias || 0.5;
|
|
|
|
function adjustAxis(axis, zoomInPercentage, bias) {
|
|
let delta = axis[1] - axis[0];
|
|
let increment = delta * zoomInPercentage;
|
|
let foo = [increment * bias, increment * (1 - bias)];
|
|
|
|
return [axis[0] + foo[0], axis[1] - foo[1]];
|
|
}
|
|
|
|
let yAxes = g.yAxisRanges();
|
|
let newYAxes = [];
|
|
for (let i = 0; i < yAxes.length; i++) {
|
|
newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
|
|
}
|
|
|
|
return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
|
|
}
|
|
|
|
if (event.altKey || event.shiftKey) {
|
|
state.tmp.dygraph_user_action = true;
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
// http://dygraphs.com/gallery/interaction-api.js
|
|
let normal_def;
|
|
if (typeof event.wheelDelta === 'number' && !isNaN(event.wheelDelta))
|
|
// chrome
|
|
{
|
|
normal_def = event.wheelDelta / 40;
|
|
} else
|
|
// firefox
|
|
{
|
|
normal_def = event.deltaY * -1.2;
|
|
}
|
|
|
|
let normal = (event.detail) ? event.detail * -1 : normal_def;
|
|
let percentage = normal / 50;
|
|
|
|
if (!(event.offsetX && event.offsetY)) {
|
|
event.offsetX = event.layerX - event.target.offsetLeft;
|
|
event.offsetY = event.layerY - event.target.offsetTop;
|
|
}
|
|
|
|
let percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
|
|
let xPct = percentages[0];
|
|
let yPct = percentages[1];
|
|
|
|
let new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
|
|
let after = new_x_range[0];
|
|
let before = new_x_range[1];
|
|
|
|
let first = state.netdata_first + state.data_update_every;
|
|
let last = state.netdata_last + state.data_update_every;
|
|
|
|
if (before > last) {
|
|
after -= (before - last);
|
|
before = last;
|
|
}
|
|
if (after < first) {
|
|
after = first;
|
|
}
|
|
|
|
state.setMode('zoom');
|
|
state.updateChartPanOrZoom(after, before, function () {
|
|
dygraph.updateOptions({dateWindow: [after, before]});
|
|
});
|
|
|
|
event.preventDefault();
|
|
}
|
|
},
|
|
touchstart: function (event, dygraph, context) {
|
|
state.tmp.dygraph_mouse_down = true;
|
|
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.touchstart()');
|
|
}
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
state.setMode('zoom');
|
|
state.pauseChart();
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
|
|
|
|
// we overwrite the touch directions at the end, to overwrite
|
|
// the internal default of dygraph
|
|
context.touchDirections = {x: true, y: false};
|
|
|
|
state.dygraph_last_touch_start = Date.now();
|
|
state.dygraph_last_touch_move = 0;
|
|
|
|
if (typeof event.touches[0].pageX === 'number') {
|
|
state.dygraph_last_touch_page_x = event.touches[0].pageX;
|
|
} else {
|
|
state.dygraph_last_touch_page_x = 0;
|
|
}
|
|
},
|
|
touchmove: function (event, dygraph, context) {
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.touchmove()');
|
|
}
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
|
|
|
|
state.dygraph_last_touch_move = Date.now();
|
|
},
|
|
touchend: function (event, dygraph, context) {
|
|
state.tmp.dygraph_mouse_down = false;
|
|
|
|
if (NETDATA.options.debug.dygraph || state.debug) {
|
|
state.log('interactionModel.touchend()');
|
|
}
|
|
|
|
NETDATA.globalSelectionSync.stop();
|
|
NETDATA.globalSelectionSync.delay();
|
|
|
|
state.tmp.dygraph_user_action = true;
|
|
Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
|
|
|
|
// if it didn't move, it is a selection
|
|
if (state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) {
|
|
NETDATA.globalSelectionSync.dontSyncBefore = 0;
|
|
NETDATA.globalSelectionSync.setMaster(state);
|
|
|
|
// internal api of dygraph
|
|
let pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w;
|
|
console.log('pct: ' + pct.toString());
|
|
|
|
let t = Math.round(state.view_after + (state.view_before - state.view_after) * pct);
|
|
if (NETDATA.dygraphSetSelection(state, t)) {
|
|
NETDATA.globalSelectionSync.sync(state, t);
|
|
}
|
|
}
|
|
|
|
// if it was double tap within double click time, reset the charts
|
|
let now = Date.now();
|
|
if (typeof state.dygraph_last_touch_end !== 'undefined') {
|
|
if (state.dygraph_last_touch_move === 0) {
|
|
let dt = now - state.dygraph_last_touch_end;
|
|
if (dt <= NETDATA.options.current.double_click_speed) {
|
|
NETDATA.resetAllCharts(state);
|
|
}
|
|
}
|
|
}
|
|
|
|
// remember the timestamp of the last touch end
|
|
state.dygraph_last_touch_end = now;
|
|
|
|
// refresh all the charts immediately
|
|
NETDATA.options.auto_refresher_stop_until = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
if (NETDATA.chartLibraries.dygraph.isLogScale(state)) {
|
|
if (Array.isArray(state.tmp.dygraph_options.valueRange) && state.tmp.dygraph_options.valueRange[0] <= 0) {
|
|
state.tmp.dygraph_options.valueRange[0] = null;
|
|
}
|
|
}
|
|
|
|
if (NETDATA.chartLibraries.dygraph.isSparkline(state)) {
|
|
state.tmp.dygraph_options.drawGrid = false;
|
|
state.tmp.dygraph_options.drawAxis = false;
|
|
state.tmp.dygraph_options.title = undefined;
|
|
state.tmp.dygraph_options.ylabel = undefined;
|
|
state.tmp.dygraph_options.yLabelWidth = 0;
|
|
//state.tmp.dygraph_options.labelsDivWidth = 120;
|
|
//state.tmp.dygraph_options.labelsDivStyles.width = '120px';
|
|
state.tmp.dygraph_options.labelsSeparateLines = true;
|
|
state.tmp.dygraph_options.rightGap = 0;
|
|
state.tmp.dygraph_options.yRangePad = 1;
|
|
state.tmp.dygraph_options.axes.x.drawAxis = false;
|
|
state.tmp.dygraph_options.axes.y.drawAxis = false;
|
|
}
|
|
|
|
if (smooth) {
|
|
state.tmp.dygraph_smooth_eligible = true;
|
|
|
|
if (NETDATA.options.current.smooth_plot) {
|
|
state.tmp.dygraph_options.plotter = smoothPlotter;
|
|
}
|
|
}
|
|
else {
|
|
state.tmp.dygraph_smooth_eligible = false;
|
|
}
|
|
|
|
if (netdataSnapshotData !== null && NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(state) === false) {
|
|
// pan and zoom on snapshots
|
|
state.tmp.dygraph_options.dateWindow = [NETDATA.globalPanAndZoom.force_after_ms, NETDATA.globalPanAndZoom.force_before_ms];
|
|
//state.tmp.dygraph_options.isZoomedIgnoreProgrammaticZoom = true;
|
|
}
|
|
|
|
let seriesStyles = NETDATA.dygraphGetSeriesStyle(state.tmp.dygraph_options);
|
|
state.tmp.dygraph_options.series = seriesStyles;
|
|
|
|
state.tmp.dygraph_instance = new Dygraph(
|
|
state.element_chart,
|
|
data.result.data,
|
|
state.tmp.dygraph_options
|
|
);
|
|
|
|
state.tmp.dygraph_history_tip_element = document.createElement('div');
|
|
state.tmp.dygraph_history_tip_element.innerHTML = `
|
|
<span class="dygraph__history-tip-content">
|
|
Want to extend your history of real-time metrics?
|
|
<br />
|
|
<a href="https://docs.netdata.cloud/docs/configuration-guide/#increase-the-metrics-retention-period" target=_blank>
|
|
Configure Netdata's <b>history</b></a>
|
|
or use the <a href="https://docs.netdata.cloud/database/engine/" target=_blank>DB engine</a>.
|
|
</span>
|
|
`;
|
|
state.tmp.dygraph_history_tip_element.className = 'dygraph__history-tip';
|
|
state.element_chart.appendChild(state.tmp.dygraph_history_tip_element);
|
|
|
|
|
|
state.tmp.dygraph_force_zoom = false;
|
|
state.tmp.dygraph_user_action = false;
|
|
state.tmp.dygraph_last_rendered = Date.now();
|
|
state.tmp.dygraph_highlight_after = null;
|
|
|
|
if (state.tmp.dygraph_options.valueRange[0] === null && state.tmp.dygraph_options.valueRange[1] === null) {
|
|
if (typeof state.tmp.dygraph_instance.axes_[0].extremeRange !== 'undefined') {
|
|
state.tmp.__commonMin = NETDATA.dataAttribute(state.element, 'common-min', null);
|
|
state.tmp.__commonMax = NETDATA.dataAttribute(state.element, 'common-max', null);
|
|
} else {
|
|
state.log('incompatible version of Dygraph detected');
|
|
state.tmp.__commonMin = null;
|
|
state.tmp.__commonMax = null;
|
|
}
|
|
} else {
|
|
// if the user gave a valueRange, respect it
|
|
state.tmp.__commonMin = null;
|
|
state.tmp.__commonMax = null;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
NETDATA.dygraphGetSeriesStyle = function(dygraphOptions) {
|
|
const seriesStyleStr = dygraphOptions.perSeriesStyle;
|
|
let formattedStyles = {};
|
|
|
|
if (seriesStyleStr === '') {
|
|
return formattedStyles;
|
|
}
|
|
|
|
// Parse the config string into a JSON object
|
|
let styles = seriesStyleStr.replace(' ', '').split('|');
|
|
|
|
styles.forEach(style => {
|
|
const keys = style.split(':');
|
|
formattedStyles[keys[0]] = keys[1];
|
|
});
|
|
|
|
for (let key in formattedStyles) {
|
|
if (formattedStyles.hasOwnProperty(key)) {
|
|
let settings;
|
|
|
|
switch (formattedStyles[key]) {
|
|
case 'line':
|
|
settings = { fillGraph: false };
|
|
break;
|
|
case 'area':
|
|
settings = { fillGraph: true };
|
|
break;
|
|
case 'dot':
|
|
settings = {
|
|
fillGraph: false,
|
|
drawPoints: true,
|
|
pointSize: dygraphOptions.pointSize
|
|
};
|
|
break;
|
|
default:
|
|
settings = undefined;
|
|
}
|
|
|
|
formattedStyles[key] = settings;
|
|
}
|
|
}
|
|
|
|
return formattedStyles;
|
|
};
|