lib/visage-app/public/javascripts/graph.js in visage-app-2.0.5 vs lib/visage-app/public/javascripts/graph.js in visage-app-2.1.0
- old
+ new
@@ -119,10 +119,11 @@
var label = value,
unit = '';
break
}
+ if (!(label)) { label = 0; }
return label.format({decimals: precision, suffix: unit})
}
function formatDate(d) {
var datetime = new Date(d)
@@ -166,10 +167,11 @@
this.getData(); // calls graphData
},
dataURL: function() {
var url = ['data', this.options.host, this.options.plugin];
+
// if the data exists on another host (useful for embedding)
if (this.options.baseurl) {
url.unshift(this.options.baseurl.replace(/\/$/, ''))
}
// for specific plugin instances
@@ -178,11 +180,18 @@
}
// if no url is specified
if (!url[0].match(/http\:\/\//)) {
url[0] = '/' + url[0]
}
- return url.join('/')
+ var options = '';
+ if (this.options.percentiles) {
+ if (this.options.percentiles.length > 0) {
+ options = '?percentiles=true';
+ this.options.percentile95 = true;
+ }
+ }
+ return url.join('/') + options;
},
getData: function() {
this.request = new Request.JSONP({
url: this.dataURL(),
data: this.requestData,
@@ -248,13 +257,15 @@
this.chart.xAxis.concat(this.chart.yAxis).each(function(axis) {
axis.setExtremes(null,null,false);
});
this.chart.redraw();
+ this.drawPercentiles(this.chart)
break;
default:
this.drawChart()
+ this.drawPercentiles(this.chart)
break;
}
},
buildDataStructures: function (data) {
var series = this.series = []
@@ -262,12 +273,12 @@
var plugin = this.options.plugin
var data = data ? data : this.response
$each(data[host][plugin], function(instance, instanceName) {
$each(instance, function(metric, metricName) {
- var start = metric.start,
- finish = metric.finish,
+ var start = metric.start,
+ finish = metric.finish,
interval = (finish - start) / metric.data.length;
var data = metric.data.map(function(value, index) {
var x = (start + index * interval) * 1000,
y = value;
@@ -276,16 +287,16 @@
});
var set = {
name: [ host, plugin, instanceName, metricName ],
data: data,
+ percentile95: metric.percentile_95
};
series.push(set)
}, this);
}, this);
-
return series
},
getSeriesMinMax: function(series) {
var min, max;
@@ -311,11 +322,44 @@
}
});
return {'min': min, 'max': max};
},
+ removePercentiles: function(chart) {
+
+ var series = this.series;
+
+ series.each(function(set) {
+ chart.yAxis[0].removePlotLine('95e_' + set.name[2] + set.name[3]);
+ });
+ },
+ drawPercentiles: function(chart) {
+ var series = this.series;
+
+ /* Get the maximum value across all sets.
+ * Used later on to determine the decimal place in the label. */
+ meta = this.getSeriesMinMax(series);
+ var min = meta.min,
+ max = meta.max;
+
+ series.each(function(set) {
+ formattedValue = formatValue(set.percentile95, { 'precision': 2, 'min': min, 'max': max });
+ chart.yAxis[0].removePlotLine('95e_' + set.name[2] + set.name[3]);
+ chart.yAxis[0].addPlotLine({
+ id: '95e_' + set.name[2] + set.name[3],
+ value: set.percentile95,
+ color: '#ff0000',
+ width: 1,
+ zIndex: 5,
+ label: {
+ text: '95e ' + set.name[3] + ": " + formattedValue
+ }
+ })
+ });
+ },
drawChart: function() {
+
var series = this.series,
title = this.title(),
element = this.parentElement,
ytitle = formatPluginName(this.options.plugin),
min,
@@ -342,10 +386,13 @@
if (this.options.live) {
var data = { 'start': this.lastFinish / 1000,
'finish': this.lastFinish / 1000 + 10,
'live': true };
this.requestData = data;
+ // FIXME: for 95e plotLines - need to update them each data retrieval
+ // but perhaps 'live' just sends incremental data and so won't cause
+ // a recalculation of the 95e figures on the server side?
this.getData()
}
}.bind(this), 10000);
}.bind(this)
}
@@ -398,11 +445,11 @@
'min': min,
'max': max
});
return value
}
- }
+ },
},
plotOptions: {
series: {
shadow: false,
lineWidth: 1,
@@ -445,12 +492,13 @@
},
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'top',
- y: 275,
+ y: 255,
borderWidth: 0,
+ floating: true,
labelFormatter: function() {
return formatSeriesLabel(this.name)
},
itemStyle: {
cursor: 'pointer',
@@ -520,11 +568,11 @@
'24 hours': 24,
'3 days': 72,
'7 days': 168,
'2 weeks': 336,
'1 month': 774,
- '3 month': 2322,
+ '3 months': 2322,
'6 months': 4368,
'1 year': 8760,
'2 years': 17520 });
timescales.each(function(hour, label) {
@@ -538,18 +586,49 @@
'selected': (current ? 'selected' : ''),
});
select.grab(option)
});
- var liveToggler = new Element('input', {
+ /* Calendar month timescales dropdown */
+ var monthlyTimescales = new Hash({ 'current month': 0,
+ 'previous month': 1,
+ 'two months ago': 2,
+ 'three months ago': 3});
+
+ monthlyTimescales.each(function(monthsAgo, label) {
+ var current = this.currentTimePeriod == label;
+ var value = "start=" + (new Date().decrement('month', monthsAgo).set('date', 1).set('hr', 0).set('min', 0).set('sec', 0).getTime() / 1000);
+ value += '&finish=' + (new Date().decrement('month', monthsAgo - 1).set('date', 1).set('hr', 0).set('min', 0).set('sec', 0).getTime() / 1000);
+
+ var option = new Element('option', {
+ 'html': label,
+ 'value': value,
+ 'selected': (current ? 'selected' : ''),
+ });
+ select.grab(option)
+ });
+
+ var liveToggler = this.liveToggler = new Element('input', {
'type': 'checkbox',
'id': this.parentElement + '-live',
'name': 'live',
'checked': this.options.live,
+ 'disabled': this.options.percentile95,
'events': {
- 'click': function() {
+ 'click': function(e) {
this.options.live = !this.options.live
+ if (this.options.live) {
+ // tell percentiles95Toggler to be unchecked
+ if (this.options.percentile95) {
+ this.percentile95Toggler.fireEvent('click');
+ }
+ this.percentile95Toggler.set('disabled', true);
+ } else {
+ this.requestData.live = false;
+ this.percentile95Toggler.set('disabled', false);
+ e.target.form.fireEvent('submit', e)
+ }
}.bind(this)
},
'styles': {
'margin-right': '4px',
'cursor': 'pointer'
@@ -565,10 +644,64 @@
'margin-right': '8px',
'cursor': 'pointer'
}
});
+ var percentile95Toggler = this.percentile95Toggler = new Element('input', {
+ 'type': 'checkbox',
+ 'id': this.parentElement + '-percentile95',
+ 'name': 'percentile95',
+ 'checked': this.options.percentile95,
+ 'events': {
+ 'click': function() {
+ this.options.percentile95 = !this.options.percentile95;
+ if (!(this.options.percentiles)) {
+ this.options.percentiles = [];
+ }
+ if (this.options.percentile95) {
+ this.options.percentiles.push('95');
+ if ((this.chart) && (this.series[0].percentile95)) {
+ this.drawPercentiles(this.chart);
+ } else {
+ this.getData();
+ }
+ // tell liveToggler to be unchecked
+ if (this.options.live) {
+ this.liveToggler.fireEvent('click');
+ }
+ this.liveToggler.set('disabled', true);
+ } else {
+ // FIXME - when adding support for 5th and 50th percentiles
+ // etc we'll need to switch the options.percentiles array
+ // to a hash for easy enabling / disabling of percentiles
+ this.options.percentiles = [];
+ if (this.chart) {
+ this.removePercentiles(this.chart);
+ } else {
+ this.getData();
+ }
+ this.liveToggler.set('disabled', false);
+ }
+ }.bind(this)
+ },
+ 'styles': {
+ 'margin-right': '4px',
+ 'cursor': 'pointer'
+ }
+ });
+
+ var percentile95Label = new Element('label', {
+ 'for': this.parentElement + '-percentile95',
+ 'html': '95th Percentile',
+ 'styles': {
+ 'font-family': 'sans-serif',
+ 'font-size': '11px',
+ 'margin-right': '8px',
+ 'cursor': 'pointer'
+ }
+ });
+
var exportLink = new Element('a', {
'href': this.dataURL(),
'html': 'Export data',
'styles': {
'font-family': 'sans-serif',
@@ -592,9 +725,11 @@
}.bind(this)
}
});
form.grab(exportLink)
+ form.grab(percentile95Toggler)
+ form.grab(percentile95Label)
form.grab(liveToggler)
form.grab(liveLabel)
form.grab(select)
container.grab(form, 'top')
},