Skip to content

Instantly share code, notes, and snippets.

@toni-moreno
Created September 13, 2014 05:49
Show Gist options
  • Save toni-moreno/c87d55144d7320b6fd7a to your computer and use it in GitHub Desktop.
Save toni-moreno/c87d55144d7320b6fd7a to your computer and use it in GitHub Desktop.
highcharts.graphite.js ( Graphite viewer core based on highcharts )
//http://jsfiddle.net/Gv7Tg/27/
var dashCtrl = {
onChart:[], // ch: , id: //
//graphite vars
data_mode: 0, //1=hicharts //0= graphite original
graphite_url: null, //null = current host
//zoom synchronization vars
controllingChart:null,
defaultTickInterval:5,
currentTickInterval:5,
//time refresh values
dashboard_refresh_time:0,
dashboard_refresh_margin:60, // seconds
rf_time:0,
/*
* update time
*/
updateIncrementTime: function() {
var rf_time_ms=new Date().getTime() - this.dashboard_refresh_time;
this.rf_time=parseInt((rf_time_ms+500)/1000)+ this.dashboard_refresh_margin;
},
/*
* update time
*/
updateLastRefreshTime: function () {
this.dashboard_refresh_time=new Date().getTime();
},
/*
* Initialize highcharts
*/
init: function(graphite_url,mode) {
this.graphite_url = graphite_url || null;
this.data_mode = mode || 0;
Highcharts.setOptions({
global: { useUTC: false },
chart: {
style: {
fontFamily: 'monospace'
}
}
});
},
/*
* syncronizeCrossHairs
*/
resetCharts:function() {
this.onChart = [];
},
/*
* syncronizeCrossHairs
*/
unzoom: function() {
for (var i=0; i < this.onChart.length; i++) {
this.onChart[i].ch.options.chart.isZoomed = false;
this.onChart[i].ch.xAxis[0].setExtremes(null, null);
}
},
/*
* syncronizeCrossHairs
*/
syncronizeCrossHairs: function (chart) {
var container = $(chart.container),
offset = container.offset(),
x, y, isInside, report;
container.mousemove(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
y = evt.clientY - chart.plotTop - offset.top;
var xAxis = chart.xAxis[0];
//remove old plot line and draw new plot line (crosshair) for this chart
for (var i=0; i < dashCtrl.onChart.length; i++) {
var xAxis1 = dashCtrl.onChart[i].ch.xAxis[0];
xAxis1.removePlotLine("myPlotLineId");
xAxis1.addPlotLine({
value: chart.xAxis[0].translate(x, true),
width: 1,
color: 'red',
//dashStyle: 'dash',
id: "myPlotLineId"
});
}
//if you have other charts that need to be syncronized - update their crosshair (plot line) in the same way in this function.
});
},
/*compute a reasonable tick interval given the zoom range -
*have to compute this since we set the tickIntervals in order
*to get predictable synchronization between 3 charts with
*different data.
*/
computeTickInterval:function (xMin, xMax) {
var zoomRange = xMax - xMin;
if (zoomRange <= 2)
this.currentTickInterval = 0.5;
if (zoomRange < 20)
currentTickInterval = 1;
else if (zoomRange < 100)
this.currentTickInterval = 5;
},
/*
*explicitly set the tickInterval for the all charts - based on
*selected range
*/
setTickInterval: function (event) {
var xMin = event.xAxis[0].min;
var xMax = event.xAxis[0].max;
this.computeTickInterval(xMin, xMax);
for (var i=0; i < this.onChart.length; i++) {
this.onChart[i].ch.xAxis[0].options.tickInterval = this.currentTickInterval;
this.onChart[i].ch.xAxis[0].isDirty = true;
}
},
/*
* hc_init_graph
http://graphite.readthedocs.org/en/latest/render_api.html
* addGraph(id,params) {
- title
- url
- areaAlpha
- areaMode
- bgcolor
- colorList
- fgcolor
- fontBold
- fontItalic
- fontName
- fontSize
- graphOnly ( no grid , axes, legend)
- graphType ( line, pie)
- hideLegend
- hideAxes
- hideYAxis
- hideGrid
- leftColor(Dual Y axis)
- leftDashed(Dual Y axix)
- leftWidth
- lineMode (slope,staircase,connected)
- lineWidth
- logBase
- margin
- yMax
- yMin
-minorGridLineColor
}
*/
addChart: function (id,title,type) {
var char_type= type || 'line';
var c_chart=new Highcharts.Chart({
title:{
text: title,
x: -20
},
chart:{
renderTo: id,
type: char_type,
zoomType: 'x',
events: {
load: function() {
}
}
},
xAxis:{
minorTickInterval: 'auto',
type: 'datetime',
maxZoom: 1 * 1800000, //30 min
events:{
afterSetExtremes:function(){
if (!this.chart.options.chart.isZoomed) {
var xMin = this.chart.xAxis[0].min;
var xMax = this.chart.xAxis[0].max;
var zmRange = dashCtrl.computeTickInterval(xMin, xMax);
for(var i=0;i< dashCtrl.onChart.length;i++) {
dashCtrl.onChart[i].ch.xAxis[0].options.tickInterval =zmRange;
dashCtrl.onChart[i].ch.xAxis[0].isDirty = true;
if(dashCtrl.onChart[i].ch != this.chart ) {
dashCtrl.onChart[i].ch.options.chart.isZoomed = true;
dashCtrl.onChart[i].ch.xAxis[0].setExtremes(xMin, xMax, true);
dashCtrl.onChart[i].ch.options.chart.isZoomed = false;
}
}
}
}
}
},
yAxis:{
minorTickInterval: 'auto',
//min:0,
title: {
text: null
},
labels: {
}
},
tooltip:{
crosshairs: true,
shared:true,
formatter: function() {
d1=new Date(this.x);
var s= '<b>'+ d1.toLocaleString() +'</b>';
if(char_type == 'area') {
var sum = 0;
$.each(this.points, function(i, point) {
var v=parseFloat(point.y).toFixed(2);
var p=parseFloat(point.percentage).toFixed(2);
//Parse CactiStile
var cur_name=point.series.name;
var parts = cur_name.split(' ');
var real_name=parts[0];
s += '<br/><span style="color:'+ point.series.color+'">\u25CF </span>'+ real_name +': <b>'+ v +':('+p+'%)</b>';
sum = sum + point.y;
});
s += '<br/>Total: '+parseFloat(sum).toFixed(2);
} else {
$.each(this.points, function(i, point) {
var v=parseFloat(point.y).toFixed(2);
//Parse CactiStile
var cur_name=point.series.name;
var parts = cur_name.split(' ');
var real_name=parts[0];
s += '<br/><span style="color:'+ point.series.color+'">\u25CF </span>'+ real_name +': <b>'+ v +'</b>';
});
}
return s;
}
},
legend: {
},
plotOptions: {
series: {
marker: {
enabled: false
}
},
area: {
stacking: 'normal',
}
},
scrollbar: {
enabled: true
}/*,
exporting: {
buttons: {
customButton: {
x: -62,
onclick: function () {
alert('Clicked');
},
symbol: 'circle'
}
}
}*/
},function(chart){
//this function needs to be added to each syncronized chart
dashCtrl.syncronizeCrossHairs(chart);
});
this.onChart.push({
ch:c_chart,
id:id,
url: null
});
},
getIdFromIndex:function(index) {
return this.onChart[index].id;
},
getIndexFromId:function(id) {
for(var i=0; i < this.onChart.length; i++) {
if(this.onChart[i].id == id ) return i;
}
},
/*
* setChartData(index,)
*/
setChartData: function(id,from,until,urlreq) {
var i;
if (typeof id === "string") {
i=this.getIndexFromId(id);
} else {
i=id;
}
var l_from = from || "-12h";
if(until == "-" ) {
var l_until = '-'+this.dashboard_refresh_margin+'s';
} else {
var l_until = until;
}
var chart=this.onChart[i].ch;
this.onChart[i].url=urlreq;
if(this.data_mode) subformat="&subformat=highcharts";
else subformat="";
var full_url='/render?format=json'+subformat+'&from='+l_from+'&until='+l_until+'&'+urlreq;
if(this.graphite_url != null ) full_url=this.graphite_url+full_url;
$.ajax({
url: full_url,
type: 'GET',
dataType: 'json',
timeout: 60000,
async: false,
//async: true,
error: function (data, textStatus, jqXHR){
alert('ERROR2:'+textStatus);
},
success: function (data, textStatus, jqXHR){
$.each(data,function(index){
var title =this.target;
var timedata = this.datapoints;
if(dashCtrl.data_mode){
//highcharts formatted
var timedata2=timedata;
} else {
//original graphite formatted
var timedata2= new Array();
var n=this.datapoints.length;
var i=0;
for(i=0;i<n;i++){
timedata2.push([timedata[i][1]*1000,timedata[i][0]]);
}
}
chart.addSeries({
name: title,
lineWidth: 1,
radius:0,
data: timedata2
});
});
}
});
},
/*
* updateData(id,urlreq)
* updateData(index)
*/
updateChartData: function(id) {
var i;
if (typeof id === "string") {
i=this.getIndexFromId(id);
} else {
i=id;
}
if(this.data_mode) subformat="&subformat=highcharts";
else subformat="";
var chart=this.onChart[i].ch;
var urlreq=this.onChart[i].url;
var full_url='/render?format=json'+subformat+'&from=-'+this.rf_time+'s&until=-'+this.dashboard_refresh_margin+'s&'+urlreq;
if(this.graphite_url != null) full_url=this.graphite_url+full_url;
$.ajax({
url: full_url,
type: 'GET',
dataType: 'json',
timeout: 60000,
/*async: false,*/
async: true,
error: function (data, textStatus, jqXHR){
alert('ERROR2:'+textStatus);
},
success: function (data, textStatus, jqXHR){
$.each(data,function(index){
var title =this.target;
var timedata = this.datapoints;
//create new array to swap time,value order (value,timestamp)-> (timestamp*1000,value)
if(dashCtrl.data_mode){
//highcharts formatted
var timedata2=timedata;
} else {
//original graphite formatted
var timedata2= new Array();
var n=this.datapoints.length;
var i=0;
for(i=0;i<n;i++){
timedata2.push([timedata[i][1]*1000,timedata[i][0]]);
}
}
for(var i=0;i<timedata2.length;i++) {
chart.series[index].addPoint(timedata2[i],false,true);
}
});
chart.redraw();
}
});
},
updateAllCharts: function() {
this.updateIncrementTime();
for(var i=0; i < this.onChart.length; i++) {
this.updateChartData(i);
}
this.updateLastRefreshTime();
},
addLinkToChart: function(id,link,image) {
img=image || '/content/img/link.png'
var i;
if (typeof id === "string") {
i=this.getIndexFromId(id);
} else {
i=id;
}
var chart=this.onChart[i].ch;
chart.renderer.image(img, 5, 5, 20, 20)
.on('click', function() {
window.location.href = link
//location.href=xxx
})
.css({
cursor: 'pointer'
})
.css({
position: 'relative',
"margin-left": "-90px",
opacity: 0.75
})
.attr({
zIndex: -100
zIndex: -100
})
.add();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment