lib/visage-app/public/javascripts/graph.js in visage-app-1.0.0 vs lib/visage-app/public/javascripts/graph.js in visage-app-2.0.0
- old
+ new
@@ -119,18 +119,16 @@
var label = value,
unit = '';
break
}
- var rounded = label.round(precision)
-
- return rounded + unit
+ return label.format({decimals: precision, suffix: unit})
}
function formatDate(d) {
- var datetime = new Date(d * 1000)
- return datetime.format("%Y-%m-%d %H:%M:%S UTC%T")
+ var datetime = new Date(d)
+ return datetime.format("%Y-%m-%d %H:%M:%S UTC%z")
}
function formatPluginName(name) {
if (name.test(/^curl_json/)) {
name = name.split('-')[1].replace(/(-|_)/, ' ');
@@ -138,48 +136,46 @@
return name
}
/*
- * visageBase()
+ * VisageBase()
*
* Base class for fetching data and setting graph options.
* Should be used by other classes to build specialised graphing behaviour.
*
*/
-var visageBase = new Class({
+var VisageBase = new Class({
Implements: [ Options, Events ],
options: {
secureJSON: false,
httpMethod: 'get',
live: false
},
initialize: function(element, host, plugin, options) {
- this.parentElement = element
- this.options.host = host
- this.options.plugin = plugin
- this.setOptions(options)
+ this.parentElement = element;
+ this.options.host = host;
+ this.options.plugin = plugin;
+ this.query = window.location.search.slice(1).parseQueryString();
+ this.options = Object.merge(this.options, this.query);
- var data = new Hash()
- if($chk(this.options.start)) {
- data.set('start', this.options.start)
- }
- if($chk(this.options.finish)) {
- data.set('finish', this.options.finish)
- }
+ this.setOptions(options);
- this.requestData = data;
+ this.requestData = new Object();
+ this.requestData.start = this.options.start;
+ this.requestData.finish = this.options.finish;
+
this.getData(); // calls graphData
},
dataURL: function() {
- var url = ['data', this.options.host, this.options.plugin]
+ var url = ['data', this.options.host, this.options.plugin];
// if the data exists on another host (useful for embedding)
- if ($defined(this.options.baseurl)) {
+ if (this.options.baseurl) {
url.unshift(this.options.baseurl.replace(/\/$/, ''))
}
// for specific plugin instances
- if ($chk(this.options.pluginInstance)) {
+ if (this.options.pluginInstance) {
url.push(this.options.pluginInstance)
}
// if no url is specified
if (!url[0].match(/http\:\/\//)) {
url[0] = '/' + url[0]
@@ -200,34 +196,34 @@
}.bind(this)
});
this.request.send();
},
- graphName: function() {
+ title: function() {
if ($chk(this.options.name)) {
- var name = this.options.name
+ var title = this.options.name
} else {
- var name = [ formatPluginName(this.options.plugin),
- 'on',
- this.options.host ].join(' ')
+ var title = [ formatPluginName(this.options.plugin),
+ 'on',
+ this.options.host ].join(' ')
}
- return name
+ return title
},
});
/*
- * visageGraph()
+ * VisageGraph()
*
* General purpose graph for rendering data from a single plugin
* with multiple plugin instances.
*
- * Builds upon visageBase().
+ * Builds upon VisageBase().
*
*/
-var visageGraph = new Class({
- Extends: visageBase,
+var VisageGraph = new Class({
+ Extends: VisageBase,
Implements: Chain,
// assemble data to graph, then draw it
graphData: function(data) {
this.response = data
this.buildDataStructures()
@@ -264,24 +260,25 @@
var series = this.series = []
var host = this.options.host
var plugin = this.options.plugin
var data = data ? data : this.response
- $each(data[host][plugin], function(instance, iname) {
- $each(instance, function(metric, mname) {
+ $each(data[host][plugin], function(instance, instanceName) {
+ $each(instance, function(metric, metricName) {
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,
+ var x = (start + index * interval) * 1000,
y = value;
+
return [ x, y ];
});
var set = {
- name: [ host, plugin, iname, mname ],
+ name: [ host, plugin, instanceName, metricName ],
data: data,
};
series.push(set)
}, this);
@@ -316,11 +313,11 @@
return {'min': min, 'max': max};
},
drawChart: function() {
var series = this.series,
- title = this.graphName(),
+ title = this.title(),
element = this.parentElement,
ytitle = formatPluginName(this.options.plugin),
min,
max;
@@ -329,129 +326,145 @@
meta = this.getSeriesMinMax(series);
var min = meta.min,
max = meta.max;
this.chart = new Highcharts.Chart({
+ series: series,
chart: {
- renderTo: element,
- defaultSeriesType: 'line',
- marginRight: 200,
- marginBottom: 25,
- zoomType: 'xy',
- height: 300,
+ renderTo: element,
+ type: 'line',
+ marginRight: 0,
+ marginBottom: 60,
+ zoomType: 'xy',
+ height: 300,
events: {
load: function(e) {
setInterval(function() {
if (this.options.live) {
- var data = { 'start': this.lastFinish,
- 'finish': this.lastFinish + 10,
+ var data = { 'start': this.lastFinish / 1000,
+ 'finish': this.lastFinish / 1000 + 10,
'live': true };
this.requestData = data;
this.getData()
}
}.bind(this), 10000);
}.bind(this)
}
},
title: {
- text: title,
- style: {
- fontSize: '20px',
- fontWeight: 'bold',
- color: "#333333"
- }
+ text: title,
+ style: {
+ 'fontSize': '18px',
+ 'fontWeight': 'bold',
+ 'color': '#333333',
+ 'font-family': 'Bitstream Vera Sans, Helvetica Neue, sans-serif',
+ }
},
+/*
+ colors: [
+'#204a87', '#4e9a06', '#cc0000', '#5c3566', '#f57900', '#e9b96e', '#ad7fa8', '#888a85', '#8ae234', '#75507b', '#c17d11', '#729fcf', '#73d216', '#ef2929', '#edd400', '#8f5902', '#555753', '#fce94f', '#2e3436', '#babdb6', '#3465a4', '#a40000', '#c4a000', '#ce5c00', '#d3d7cf', '#fcaf3e', '#eeeeec',
+ ],
+*/
xAxis: {
- type: 'datetime',
- labels: {
- y: 20,
- formatter: function() {
- var d = new Date(this.value * 1000)
- return d.format("%H:%M")
- }
- },
title: {
text: null
+ },
+ lineColor: "#aaa",
+ tickColor: "#aaa",
+ type: 'datetime',
+ dateTimeLabelFormats: {
+ second: '%H:%M:%S',
+ minute: '%H:%M',
+ hour: '%H:%M',
+ day: '%d/%m',
+ week: '%d/%m',
+ month: '%m/%Y',
+ year: '%Y'
}
+
},
yAxis: {
- title: {
- text: ytitle
- },
- startOnTick: false,
- minPadding: 0.065,
- max: max,
- endOnTick: true,
- labels: {
+ title: {
+ text: null
+ },
+ startOnTick: false,
+ minPadding: 0.065,
+ endOnTick: false,
+ gridLineColor: "#dddddd",
+ labels: {
formatter: function() {
- var precision = min - max < 1000 ? 2 : 0,
+ var precision = 1,
value = formatValue(this.value, {
'precision': precision,
'min': min,
'max': max
});
return value
}
- }
+ }
},
plotOptions: {
- series: {
- shadow: false,
- marker: {
- enabled: false,
- stacking: 'normal',
- states: {
- hover: {
- enabled: true
+ series: {
+ shadow: false,
+ lineWidth: 1,
+ marker: {
+ enabled: false,
+ states: {
+ hover: {
+ enabled: true,
+ radius: 4,
+ },
+ },
+ },
+ states: {
+ hover: {
+ enabled: true,
+ lineWidth: 1,
+ },
}
- }
}
- }
},
tooltip: {
- formatter: function() {
- var tip;
- tip = '<strong>'
- tip += formatSeriesLabel(this.series.name).trim()
- tip += '</strong>' + ' -> '
- tip += '<span style="font-family: monospace; font-size: 14px;">'
- tip += formatValue(this.y, { 'precision': 2, 'min': min, 'max': max })
- tip += '<span style="font-size: 9px; color: #777">'
- tip += ' (' + this.y + ')'
- tip += '</span>'
- tip += '</span>'
- tip += '<br/>'
- tip += '<span style="font-family: monospace">'
- tip += formatDate(this.x)
- tip += '</span>'
+ formatter: function() {
+ var tip;
+ tip = '<strong>'
+ tip += formatSeriesLabel(this.series.name).trim()
+ tip += '</strong>' + ' -> '
+ tip += '<span style="font-family: monospace; font-size: 14px;">'
+ tip += formatValue(this.y, { 'precision': 2, 'min': min, 'max': max })
+ tip += '<span style="font-size: 9px; color: #777">'
+ tip += ' (' + this.y + ')'
+ tip += '</span>'
+ tip += '</span>'
+ tip += '<br/>'
+ tip += '<span style="font-family: monospace">'
+ tip += formatDate(this.x)
+ tip += '</span>'
- return tip
- }
+ return tip
+ }
},
legend: {
- layout: 'vertical',
- align: 'right',
+ layout: 'horizontal',
+ align: 'center',
verticalAlign: 'top',
- x: -10,
- y: 60,
+ y: 275,
borderWidth: 0,
- itemWidth: 186,
labelFormatter: function() {
return formatSeriesLabel(this.name)
},
itemStyle: {
cursor: 'pointer',
color: '#333333'
},
itemHoverStyle: {
- color: '#777777'
+ color: '#888'
}
},
- series: series,
credits: {
- enabled: false
+ enabled: false
}
});
this.buildDateSelector();
},
@@ -460,57 +473,75 @@
* container
* \
* - form
* \
* - select
- * | \
- * | - option
- * | |
- * | - option
- * |
- * - submit
+ * \
+ * - option
+ * |
+ * - option
*/
var currentDate = new Date;
var currentUnixTime = parseInt(currentDate.getTime() / 1000);
var container = $(this.parentElement);
- var form = new Element('form', {
+ var form = this.form = new Element('form', {
'method': 'get',
+ 'styles': {
+ 'text-align': 'right',
+ },
'events': {
- 'submit': function(e, foo) {
- e.stop();
- e.target.getElement('select').getSelected().each(function(option) {
- value = parseInt(option.value.split('=')[1])
- data = { 'start': value }
- });
- this.requestData = data;
-
+ 'submit': function(e) {
+ this.requestData = this.form.getElement('select').getSelected()[0].value.parseQueryString()
/* Draw everything again. */
this.getData();
}.bind(this)
}
});
- var select = new Element('select', { 'class': 'date timescale' });
- var timescales = new Hash({ 'hour': 1, '2 hours': 2, '6 hours': 6, '12 hours': 12,
- 'day': 24, '2 days': 48, '3 days': 72,
- 'week': 168, '2 weeks': 336, 'month': 672 });
+ /* Select dropdown */
+ var select = this.select = new Element('select', {
+ 'class': 'date timescale',
+ 'styles': {
+ 'margin-bottom': '3px',
+ 'border': '1px solid #aaa',
+ },
+ 'events': {
+ 'change': function(e) {
+ e.target.form.fireEvent('submit', e)
+ }
+ }
+ });
+
+ /* Timescales available in the dropdown */
+ var timescales = new Hash({ '1 hour': 1,
+ '2 hours': 2,
+ '6 hours': 6,
+ '12 hours': 12,
+ '24 hours': 24,
+ '3 days': 72,
+ '7 days': 168,
+ '2 weeks': 336,
+ '1 month': 774,
+ '3 month': 2322,
+ '6 months': 4368,
+ '1 year': 8760,
+ '2 years': 17520 });
+
timescales.each(function(hour, label) {
var current = this.currentTimePeriod == 'last {label}'.substitute({'label': label });
- var value = "start={start}".substitute({'start': currentUnixTime - (hour * 3600)});
- var html = 'last {label}'.substitute({'label': label });
+ var value = "start={start}".substitute({'start': currentUnixTime - (hour * 3600)});
+ var html = 'last {label}'.substitute({'label': label });
var option = new Element('option', {
- html: html,
- value: value,
- selected: (current ? 'selected' : '')
+ 'html': html,
+ 'value': value,
+ 'selected': (current ? 'selected' : ''),
});
select.grab(option)
});
- var submit = new Element('input', { 'type': 'submit', 'value': 'show' });
-
var liveToggler = new Element('input', {
'type': 'checkbox',
'id': this.parentElement + '-live',
'name': 'live',
'checked': this.options.live,
@@ -518,33 +549,34 @@
'click': function() {
this.options.live = !this.options.live
}.bind(this)
},
'styles': {
- 'margin-left': '4px',
+ 'margin-right': '4px',
'cursor': 'pointer'
}
});
var liveLabel = new Element('label', {
'for': this.parentElement + '-live',
'html': 'Live',
'styles': {
'font-family': 'sans-serif',
'font-size': '11px',
- 'margin-left': '8px',
+ 'margin-right': '8px',
'cursor': 'pointer'
}
});
var exportLink = new Element('a', {
'href': this.dataURL(),
'html': 'Export data',
'styles': {
- 'font-family': 'sans-serif',
- 'font-size': '11px',
- 'margin-left': '8px',
+ 'font-family': 'sans-serif',
+ 'font-size': '11px',
+ 'margin-right': '8px',
+ 'color': '#2F5A92',
},
'events': {
'mouseover': function(e) {
e.stop();
var url = e.target.get('href'),
@@ -559,20 +591,25 @@
e.target.set('href', url)
}.bind(this)
}
});
- form.grab(select)
- form.grab(submit)
+ form.grab(exportLink)
form.grab(liveToggler)
form.grab(liveLabel)
- form.grab(exportLink)
+ form.grab(select)
container.grab(form, 'top')
},
+ setTimePeriodTo: function(selected) {
+ var option = this.select.getElements('option').filter(function(opt) {
+ return opt.text == selected.text
+ })[0];
+ option.set('selected', 'selected');
-
+ this.form.fireEvent('submit')
+ }
});
// buildEmbedder: function() {
// var pre = new Element('textarea', {
// 'id': 'embedder',
@@ -608,10 +645,10 @@
// },
// embedCode: function() {
// baseurl = "{protocol}//{host}".substitute({'host': window.location.host, 'protocol': window.location.protocol});
// code = "<script src='{baseurl}/javascripts/visage.js' type='text/javascript'></script>".substitute({'baseurl': baseurl});
// code += "<div id='graph'></div>"
-// code += "<script type='text/javascript'>window.addEvent('domready', function() { var graph = new visageGraph('graph', '{host}', '{plugin}', ".substitute({'host': this.options.host, 'plugin': this.options.plugin});
+// code += "<script type='text/javascript'>window.addEvent('domready', function() { var graph = new VisageGraph('graph', '{host}', '{plugin}', ".substitute({'host': this.options.host, 'plugin': this.options.plugin});
// code += "{"
// code += "width: 900, height: 220, gridWidth: 800, gridHeight: 200, baseurl: '{baseurl}'".substitute({'baseurl': baseurl});
// code += "}); });</script>"
// return code.replace('<', '<').replace('>', '>')
// },