FnordMetric.widgets.timelineWidget = function(){ var widget_uid = FnordMetric.util.getNextWidgetUID(); var width, height, canvas, series, opts, xtick; var xticks = 100; var running_request = false; var series_paths = {}; var series_values = {}; var xpadding = 30; function render(_opts){ opts = _opts; //if(!silent){ $(opts.elem).css('opacity', 0.5); } if(opts.xticks){ xticks = opts.xticks; } if(!opts.draw_points){ opts.draw_points = true; } if(!opts.draw_path){ opts.draw_path = true; } if(!opts.draw_ygrid){ opts.draw_ygrid = true; } if(!opts.draw_xgrid){ opts.draw_xgrid = true; } drawLayout(opts); width = opts.elem.width() - (xpadding * 2) - 15; height = opts.height || 240; xtick = width / (xticks - 1); canvas = d3.select('#container-'+widget_uid) .append("svg:svg") .attr("width", width+(2*xpadding)) .attr("height", height+30); canvas.selectAll("*").remove(); for (ind in opts.series){ series_values[opts.series[ind]] = {}; } updateRange(); updateChart(); if(opts.autoupdate){ var secs = parseInt(opts.autoupdate); if(secs > 0){ var autoupdate_interval = window.setInterval(function(){ updateRange(true); updateChart(); // FIXPAUL: only update if not scrolled }, secs*1000); $('body').bind('fm_dashboard_close', function(){ window.clearInterval(autoupdate_interval); }); } } //canvas.drawGrid(0, 0, width+(2*xpadding), height, 1, 6, "#ececec"); } function announce(evt){ if(evt.widget_key == opts.widget_key){ if((evt.type == "widget_response") && (evt.cmd == "values_at")){ running_request = false; updateSeriesData(evt.tick, evt.values); updateChart(); } } } function requestValues(_tick, times){ if(times.length > 0){ if(!running_request){ running_request = (new Date).getTime(); requestValuesAsync(_tick, times); } } } function requestValuesAsync(_tick, times){ FnordMetric.publish({ "_class": "widget_request", "_channel": opts.channel, "cmd": "values_at", "tick": _tick, "ticks": times, "widget_key": opts.widget_key }) } function changeTick(){ opts.tick = parseInt($(this).attr('data-tick')); opts.start_timestamp = null; opts.end_timestamp = null; updateRange(); redrawDatepicker(); updateChart(); } function updateSeriesData(_tick, values){ for(_series in values){ for(_t in values[_series]){ series_values[_series][_tick+"+"+_t] = values[_series][_t]; } } } function updateChart(){ var _ticks = []; var _miss = []; var _max = []; var _rndr = []; for(sind in opts.series){ var _last = opts.start_timestamp + opts.tick; var _delta = (opts.end_timestamp - _last) / xticks; var _sdata = []; for(var n=0; n < xticks; n++){ var _t = (parseInt(_last / opts.tick) * opts.tick); var _v = series_values[opts.series[sind]][opts.tick+"+"+_t]; if((_v === undefined) && (_miss.indexOf(_t) == -1)){ _miss.push(_t); } _sdata.push(_v || 0); _last += _delta; } _max.push(Math.max.apply(Math, _sdata)); _rndr.push([opts.series[sind], _sdata]); } _max = Math.max.apply(Math, _max)*1.1; if(_max == 0){ _max = 1; } for(rind in _rndr){ drawSeries(_rndr[rind][0], _rndr[rind][1], _max); } if(_miss.length > 0) requestValues(opts.tick, _miss); redrawDatepicker(); } function refreshChart(){ for(_series in series_values){ series_values[_series] = {}; } updateRange(); updateChart(); } function drawSeries(series, series_data, _max){ //var path_string = "M0,"+height; var path_string = ""; var _color = '0066CC'; if (series_paths[series]){ for(ind in series_paths[series]){ series_paths[series][ind].remove(); } } series_paths[series] = []; if(!_max){ _max = Math.max.apply(Math, series_data)*1.1; } if(_max < 1){ _max = 1; } $(series_data).each(function(i,v){ var p_x = xpadding+(i*xtick); var p_y = (height-((v/_max)*height)); path_string += ( ( i == 0 ? "M" : "L" ) + p_x + ',' + p_y ); // if(i%label_mod==0){ // canvas.text(p_x, height+10, labels[i]).attr({ // font: '10px Helvetica, Arial', // fill: "#777" // }); // } if(opts.draw_points){ series_paths[series].push(canvas .append("svg:circle") .attr("cx", p_x) .attr("cy", p_y) .attr("stroke-width", "1px") .attr("stroke", "#fff") .attr("fill", _color) .attr("fill-opacity", 1) .attr("r", 4) ); } // var htrgt = canvas.rect(p_x - 20, p_y - 20, 40, 40).attr({ // stroke: "none", // fill: "#fff", // opacity: 0 // }).toFront(); //series_paths[series].push(htrgt); // (function(htrgt, series_paths){ // var t_y = p_y + 9; // var ttt = canvas.text(p_x, t_y+10, v).attr({ // font: '12px Helvetica, Arial', // fill: "#fff", // opacity: 0 // }); // var tttb = ttt.getBBox(); // var ttw = tttb.width+20; // var tt = canvas.rect(p_x-(ttw/2), t_y, ttw, 22, 5).attr({ // stroke: "none", // fill: "#000", // opacity: 0 // }).toBack(); // series_paths[series].push(tt); // series_paths[series].push(ttt); // $(htrgt[0]).hover(function(){ // tt.animate({ opacity: 0.8 }, 300); // ttt.animate({ opacity: 0.8 }, 300); // }, function(){ // tt.animate({ opacity: 0 }, 300); // ttt.animate({ opacity: 0 }, 300); // }); // })(htrgt, series_paths); }); if(opts.draw_path){ series_paths[series].push(canvas.append("svg:path") .attr("fill", "none") .attr("stroke", "steelblue") .attr("stroke-width", 3) .attr("d", path_string) ); } if(opts.draw_area){ // path_string += "L"+(width+xpadding)+","+height+" L"+xpadding+","+height+" Z"; // series_paths[series].push( // canvas.path(path_string).attr({ // stroke: "none", // fill: _color, // opacity: 0.1 // }).toBack() // ); } } function drawLayout(opts){ $(opts.elem).append( $('<div></div>').attr('class', 'headbar').append( $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('refresh')).click( function(){ refreshChart(); } ) ).append( $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('→')).click( function(){ moveRange(1); } ) ).append( $('<div></div>').attr('class', 'datepicker') ).append( $('<div></div>').attr('class', 'button ml').append($('<span></span>').html('←')).click( function(){ moveRange(-1); } ) ).append( $('<h2></h2>').html(opts.title) ) ).append( $('<div></div>').attr('id', 'container-'+widget_uid).css({ height: opts.height + 6, marginBottom: 20, overflow: 'hidden' }) ); if(opts.ticks){ $('.headbar', opts.elem).append('<div class="tick_btns btn_group"></div>'); for(__tick in opts.ticks){ var _tick = opts.ticks[__tick]; $('.tick_btns', opts.elem).append( $('<div></div>').attr('class', 'button tick').append($('<span></span>') .html(FnordMetric.util.formatTimeRange(_tick))) .attr('data-tick', _tick) .click(changeTick) ); } } } function redrawDatepicker(){ $('.datepicker', opts.elem).html( Highcharts.dateFormat('%d.%m.%y %H:%M', parseInt(opts.start_timestamp)*1000) + ' ‐ ' + Highcharts.dateFormat('%d.%m.%y %H:%M', parseInt(opts.end_timestamp)*1000) ); } function updateRange(force){ if(!opts.tick){ opts.tick = opts.ticks[0]; } if(!opts.start_timestamp || !opts.end_timestamp || !!force){ opts.end_timestamp = parseInt(new Date().getTime() / 1000); opts.start_timestamp = opts.end_timestamp - (opts.tick * xticks); } } function moveRange(direction){ v = opts.tick*direction*8; if(((opts.end_timestamp + v)*1000) < new Date().getTime()){ opts.start_timestamp += v; opts.end_timestamp += v; } updateChart(); } return { render: render, announce: announce } };