nodejs - how to keep an status at top of stdout - node.js

I'm trying to output something like these:
counter is: 10 <= fixed line and auto updating
console.logs, etc... <= other console.logs, errors, defaul outputs
console.logs, etc...
console.logs, etc...
console.logs, etc...
Is this possible?
I have tried with process.stdout.write() but it is not working.
var counter = 0;
setInterval(function(){
counter++;
process.stdout.write("counter is " + counter + " \r");
}, 500);
setInterval(function(){
console.log('some output');
}, 1500);

Here's an example using blessed:
var blessed = require('blessed');
var screen = blessed.screen(),
body = blessed.box({
top: 1,
left: 0,
width: '100%',
height: '99%'
}),
statusbar = blessed.box({
top: 0,
left: 0,
width: '100%',
height: 1,
style: {
fg: 'white',
bg: 'blue'
}
});
screen.append(statusbar);
screen.append(body);
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
function status(text) { statusbar.setContent(text); screen.render(); }
function log(text) { body.insertLine(0, text); screen.render(); }
var c = 1;
setInterval(function() {
status((new Date()).toISOString());
log('This is line #' + (c++));
}, 100);
Here's a simpler example that has almost the same effect (the status bar doesn't fill in extra space with background color):
var screen = blessed.screen(),
body = blessed.box({
top: 0,
left: 0,
width: '100%',
height: '100%',
tags: true
});
screen.append(body);
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
function status(text) {
body.setLine(0, '{blue-bg}' + text + '{/blue-bg}');
screen.render();
}
function log(text) {
body.insertLine(1, text);
screen.render();
}
var c = 1;
setInterval(function() {
status((new Date()).toISOString());
log('This is line #' + (c++));
}, 100);

Aside there are a lot of node modules that can help you do this,(blessed, ncurses, ansi, termhelper), for educational purposes you can also do it with vanilla node easily using process.stdout.moveCursor:
var logs = [];
function log(text) {
logs.push(text);
console.log(text);
}
function changeCounter(n) {
process.stdout.moveCursor(0, -logs.length - 1);
printCounter(n);
logs.forEach(function (log) { console.log(log) });
}
function printCounter(n) {
console.log('Counter is:', n);
}
// Now lets test
printCounter(0);
var i = 1;
setInterval(function () {
log('meoww');
changeCounter(i++);
});
Though you have to write to extra code to prevent overflowing terminal.

A traditional library for doing that sort of thing (drawing text at other than the bottom of the screen) is "curses"...there are bindings for Node.js but there is also "blessed" (ha ha) which looks easier to use: https://github.com/chjj/blessed

Related

Phaser 3 Examples Text Entry cant detect Z key typed?

This code can't detect the letter "Z" when typed. I got it from Phaser 3 Examples - Text Entry.
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: {
create: create,
update: update
}
};
var keySpace;
var keyBackspace;
var textEntry;
var game = new Phaser.Game(config);
function create ()
{
this.add.text(10, 10, 'Enter your name:', { font: '32px Courier', fill: '#ffffff' });
textEntry = this.add.text(10, 50, '', { font: '32px Courier', fill: '#ffff00' });
// keys = this.input.keyboard.addKeys('A,B,C');
keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
keyBackspace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.BACKSPACE);
this.input.keyboard.on('keydown', function (event) {
if (event.keyCode === 8 && textEntry.text.length > 0)
{
textEntry.text = textEntry.text.substr(0, textEntry.text.length - 1);
}
else if (event.keyCode === 32 || (event.keyCode >= 48 && event.keyCode < 90))
{
textEntry.text += event.key;
}
console.log(event);
});
}
function update ()
{
}
I managed to add this code to do something just fine. I am trying to capture the keyboard typed it and register it on a custom UI. The code won't work if he or she typed "Captain Zulu" which would only register "Captain ulu" because it didn't detect it. Same goes if they typed "Zuzuki" and it would appear "uuki" instead.
else if (event.keyCode === 38 )
{
dosomethingthirtyeight();
showPanel();
}
else if (event.keyCode === 40 )
{
dosomethingfortyeight();
showPanel();
}
this fix the problem. I found the keycodes here https://github.com/photonstorm/phaser/blob/v3.20.0/src/input/keyboard/keys/KeyCodes.js#L764
(event.keyCode === 190 || event.keyCode === 90) // the period isnt working too
{
textEntry.text += event.key;
}

Fabric.js: src attribute missing on grouping subclassed object

I tried to use subclassing as shown in the example at http://fabricjs.com/polaroid.
The PolaroidPhoto subclass just adds a border on the image as shown on the following fiddle: https://jsfiddle.net/gusy54rr/6/
canvas = this.__canvas = new fabric.Canvas('c', {
backgroundColor: '#333',
HOVER_CURSOR: 'pointer'
});
var PolaroidPhoto = fabric.util.createClass(fabric.Image, {
H_PADDING: 20,
V_PADDING: 20,
originX: 'center',
originY: 'center',
initialize: function(src, options) {
this.callSuper('initialize', options);
this.image = new Image();
this.image.src = src;
console.log("In initialize, src is:" + src);
this.image.onload = (function() {
this.width = this.image.width;
this.height = this.image.height;
this.loaded = true;
this.setCoords();
this.fire('image:loaded');
}).bind(this);
},
_render: function(ctx) {
if (this.loaded) {
ctx.fillStyle = '#fff';
ctx.fillRect(
-(this.width / 2) - this.H_PADDING,
-(this.height / 2) - this.H_PADDING,
this.width + this.H_PADDING * 2,
this.height + this.V_PADDING * 2);
ctx.drawImage(this.image, -this.width / 2, -this.height / 2);
}
}
});
var photo = new PolaroidPhoto('https://i.stack.imgur.com/cqmQ9.png', { });
photo.on('image:loaded', canvas.renderAll.bind(canvas));
photo.set('scaleX', 1);
photo.set('scaleY', 1);
photo.set('top', 180);
photo.set('left', 150);
console.log("photo,src is :" + photo.get('src'));
// forcing src value (but ineffective)
photo.set('src', 'https://i.stack.imgur.com/cqmQ9.png');
canvas.add(photo);
canvas.add(
rect= new fabric.Rect({ top: 50, left: 100, width: 50, height: 50, fill: '#f55' }),
circle = new fabric.Circle({ top: 140, left: 230, radius: 75, fill: 'green' }),
triangle = new fabric.Triangle({ top: 300, left: 210, width: 100, height: 100, fill: 'blue' })
);
$("#group").on('click', function() {
var activegroup = canvas.getActiveGroup();
var objectsInGroup = activegroup.getObjects();
activegroup.clone(function(newgroup) {
canvas.discardActiveGroup();
objectsInGroup.forEach(function(object) {
canvas.remove(object);
});
canvas.add(newgroup);
});
});
$("#ungroup").click(function(){
var activeObject = canvas.getActiveObject();
if(activeObject.type=="group"){
var items = activeObject._objects;
alert(items);
activeObject._restoreObjectsState();
canvas.remove(activeObject);
for(var i = 0; i < items.length; i++) {
canvas.add(items[i]);
items[i].dirty = true;
canvas.item(canvas.size()-1).hasControls = true;
}
canvas.renderAll();
}
});
It works fine until I want to stringify or make some grouping with a subclassed object.
In the fiddle, I completed the Fabric demo's example by adding a few basis objects (a rectangle, a circle and a triangle).
If I select the subclassed image and any other object and then click on the group button:
The image disappears.
The scr property of the photo is not set (as shown by the alert on "ungroup" for the former group).
A stringification of the canvas also shows that "src" is missing.
Even if I force (see the fiddle) a src value using "photo.set('src',...)" :
- the grouping still makes the picture to disappear.
- The stringification still lacks the "src" attribute. (I tried to extend toObjects to no avail)
How to get grouping and stringification to work with subclassed objects?
Thanks for your help.
Here is a new jsfiddle showing correct grouping and JSON load with a sub-classed (PolaroidPhoto) image. Fabric version is 1.7.19
https://jsfiddle.net/rpzk7wL6/2/
I put some comments in the code to show my modifications.
The main problem in the former script was the absence of fromObjects() method.
I also added a handler listening to "image:loaded" to the sub-class instance created
by fromObjects, in order to render it after loading .
fabric.Object.prototype.transparentCorners = false;
canvas = this.__canvas = new fabric.Canvas('c', {
backgroundColor: '#333',
HOVER_CURSOR: 'pointer'
});
fabric.Polaroidphoto = fabric.util.createClass(fabric.Image, {
type: 'polaroidphoto',
H_PADDING: 20,
V_PADDING: 20,
originX: 'center',
originY: 'center',
initialize: function(src, options) {
this.image = new Image();
this.image.src = src;
this.callSuper('initialize',src, options);
console.log("initialize, src:" + src);
this.image.onload = (function() {
this.width = this.image.width;
console.log("initialize, scaleX:" + this.image.scaleX);
this.height = this.image.height;
this.src= this.image.src;
console.log("initialize image.onload, src:" + src);
this.loaded = true;
this.setCoords();
this.fire('image:loaded');
}).bind(this);
},
_render: function(ctx) {
if (this.loaded) {
console.log("_render:is_loaded");
ctx.fillStyle = '#fff';
ctx.fillRect(
-(this.width / 2) - this.H_PADDING,
-(this.height / 2) - this.H_PADDING,
this.width + this.H_PADDING * 2,
this.height + this.V_PADDING * 2);
ctx.drawImage(this.image, -this.width / 2, -this.height / 2);
} else {
console.log("_render:is_NOT__loaded");
}
}
});
// Added fromObject function for sub-class
fabric.Polaroidphoto.async = true;
fabric.Polaroidphoto.fromObject = function (object, callback) {
console.log("fabric.Polaroidphoto.fromObject object.src) :" + object.src);
var instance = new fabric.Polaroidphoto(object.src, object);
callback && callback(instance);
// added handler to render instance
instance.on('image:loaded', when_loaded );
};
var photo = new fabric.Polaroidphoto('https://i.stack.imgur.com/cqmQ9.png', { });
photo.on('image:loaded', when_loaded );
photo.set('scaleX', 1);
photo.set('scaleY', 1);
photo.set('top', 180);
photo.set('left', 150);
canvas.add(photo);
canvas.add(
rect= new fabric.Rect({ top: 50, left: 100, width: 50, height: 50, fill: '#f55' }),
circle = new fabric.Circle({ top: 140, left: 230, radius: 75, fill: 'green' }),
triangle = new fabric.Triangle({ top: 300, left: 210, width: 100, height: 100, fill: 'blue' })
);
// required at load to render sub-classed image in group
function when_loaded() {
console.log("when_loaded");
dirty(); // to set dirty : true
canvas.renderAll();
}
// required at load to display sub-classed image in group,
// set dirty:true for groups
function dirty() {
$.each(canvas._objects, function( index, obj ) {
if( typeof obj.type !== 'undefined' && obj.type == 'group') {
obj.dirty= true;
}
});
}
$("#group").on('click', function() {
var activegroup = canvas.getActiveGroup();
var objectsInGroup = activegroup.getObjects();
activegroup.clone(function(newgroup) {
canvas.discardActiveGroup();
objectsInGroup.forEach(function(object) {
canvas.remove(object);
});
canvas.add(newgroup);
newgroup.dirty = true;
});
});
$("#ungroup").click(function(){
var activeObject = canvas.getActiveObject();
if(activeObject.type=="group"){
var items = activeObject._objects;
alert(items);
activeObject._restoreObjectsState();
canvas.remove(activeObject);
for(var i = 0; i < items.length; i++) {
canvas.add(items[i]);
items[i].dirty = true;
canvas.item(canvas.size()-1).hasControls = true;
}
canvas.renderAll();
}
});
Thanks

How to add text via chart.renderer.text in Highcharts?

I have succeeded adding additional text to my charts, like here. This, I achieved through adding a "function(chart)" to my "var chart = new Highcharts.Chart"
$.get('xxx.csv', function(data)
{
var chart = new Highcharts.Chart({
...
plotOptions:
{
series:
{
....
}
}
}, function(chart)
{
chart.renderer.text('The dotted line represents ...', 100, 86)
.css({
fontSize: '13px',
color: '#666666'
})
.add();
}
)};
My current format is however different:
$(function () {
var options =
{
chart:
{
...
},
...
};
$.get('xxx.csv', function(data)
{
var series = {
data: []
};
var temp = []
// Split the lines
var lines = data.split('\n');
// For each line, split the record into seperate attributes
$.each(lines, function(lineNo, line) {
var items = line.split(',');
if (lineNo !== 0) {
var xValue = +items[0],
kwh = parseFloat(items[2] / 1000);
if (!isNaN(kwh)) {
series.data.push({x:xValue,y: kwh, extra:items[1]});
}
}
});
// Push the completed series
options.series.push(series);
new Highcharts.Chart(options);
});
});
Now, I wonder how I can add a chart.renderer.text to the graph. I have tried different things, but didn't succeed. Where and how am I supposed to add the code for the text now? Here is a fiddle for this. Thanks for any hints!
You can add your text inside load event callback function of your chart. You can add this function inside your options - options.chart.events object.
http://api.highcharts.com/highcharts#chart.events.load
options.chart.events = {
load: function() {
var chart = this;
chart.renderer.text('The dotted line represents ...', 100, 186)
.css({
fontSize: '13px',
color: '#666666'
})
.add();
}
}
Here you can see an example how it can work:
http://jsfiddle.net/17ed42pa/1/
Best regards.

How to switch states in phaser frame work?

I am trying to switch to the Main Menu state in phaser through a function but couldn't get it to work. Below is my code snippet. The end function is called from the update function in game.js file.
end: function(){
player.kill();
ltext.setText("Over!!");
this.state.start('Menu');
},
You'd better not to call this function from the update function.
Instead, you can call it from a sprite or button event handler etc.
I tested game.state.start('xx') in update function and it worked as expected.
window.addEventListener('load', function(){
var game = new Phaser.Game(500, 190, Phaser.CANVAS, '', {
create : function (game) {
var textStyle = { font: "14px Arial", fill: "#ffcc00" };
game.add.text(60, 40, 'Phaser HTML5 Game Engine', textStyle);
game.add.text(60, 70, 'This is state 1', textStyle);
var textStyle = { font: "14px Arial", fill: "#00ff00"};
game.add.text(200, 130, 'Pointer here to enter state2', textStyle);
var graphics = game.add.graphics(0, 0);
graphics.beginFill(0x00ff00);
graphics.drawRect(200, 80, 50, 50);
graphics.endFill();
},
update : function (game) {
var x = game.input.x, y = game.input.y;
if(x > 200 && x < 250 && y > 80 && y < 130){
game.state.start('state2');
}
}
});
game.state.add('state2', {
create: function (game){
var textStyle = { font: "14px Arial", fill: "#00ff00"};
game.add.text(60, 40, 'Phaser HTML5 Game Engine', textStyle);
game.add.text(60, 70, 'Welcome to state 2', textStyle);
}
});
}, false);
body{text-align:center;margin:4px;padding:0;}
canvas{vertical-align:middle; background-color:#000;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/phaser/2.3.0/phaser.min.js"></script>

Atlasboard and Rickshaw

Am trying to create a dashboard using the atlasboard and rickshaw. Now what happens is that atlasboard by default has a black background and am unable to see the x-axis and y-axis when creating graphs (e.g WIKI markdown analytics).
If you look at the example screenshot below from the official atlasboard page it appears that atlassian devs have managed to show the x-axis as a white color, so am wondering :
how they did it as am unable to achieve this with rickshaw
apparently.
Is there any css that needs to be overridden or is there any kind of
settings to be setup in nodejs?
or even another graph framework should be used other than rickshaw
(and how to do that)?
Can someone please explain?
(source: bitbucket.org)
Update1 following answer:
I have added the following within the widget:
widget = {
//runs when we receive data from the job
onData: function(el, data) {
function drawDashLine(val, max, min) {
var container = $('.content', el),
viewport = {
height: container.height(),
width: container.width()
},
topPosition = (viewport.height - Math.ceil(val/max * viewport.height));
var dashedLineLine = $('<hr />')
.attr('class', 'dashedLine')
.css({
width: el.width() - 40,
top: topPosition + 'px'
});
var lineLabel = $("<span />")
.attr('class', 'lineLabel')
.css({
top: topPosition + 'px'
})
.text(val);
container.append(dashedLineLine, lineLabel);
}
function paintMinMax(series) {
var mergedData = [];
for (var i = series.length - 1; i >= 0; i--) {
mergedData = mergedData.concat(series[i].data);
};
var min = _.min(_.pluck(mergedData, 'y')),
max = _.max(_.pluck(mergedData, 'y')),
mid = Math.round(max / 2);
drawDashLine(min, max, min);
drawDashLine(mid, max, min);
drawDashLine(max, max, min);
}
function paintTimeMark(chartWidth, chartHeight, startDate, endDate, timeMarkData) {
if (!timeMarkData || !timeMarkData.length) {
return;
}
var chartLengthMs = endDate - startDate;
for (var i = 0, l = timeMarkData.length; i < l; i++) {
var timeMark = timeMarkData[i];
// are we in the boundaries?
if ((timeMark.epoch < startDate) || (timeMark.epoch > endDate)){
continue; // out of boundaries
}
var lengthOfTimemark = timeMark.epoch - startDate;
var percentage = (lengthOfTimemark / chartLengthMs);
var leftPosition = Math.round(chartWidth * percentage);
var top = timeMark.top || 0;
var markHeight = chartHeight - top;
var color = timeMark.color || 'orange';
var mark = $('<div>')
.attr('class', 'mark')
.css({
left: leftPosition + 'px',
'margin-top': top + 'px',
'background-color' : color,
'height': markHeight + 'px'
});
var legend = $('<span>')
.attr('class', 'legend')
.css({
left: leftPosition + 'px',
'margin-top': top + 'px',
'height': markHeight + 'px'
})
.hide()
.text(timeMark.name);
$('.content', el).append(mark, legend);
var center = Math.round((legend.width() - (mark.width())) / 2);
// center align legend
(function(legend, center){
setTimeout(function(){
legend.css({
'margin-left': (-(center)) + 'px'
}).fadeIn();
},600);
}(legend, center));
}
}
function paintChart(width, height, series, color) {
for (var i = series.length - 1; i >= 0; i--) {
series[i].data = formatData(series[i].data);
}
var graph = new Rickshaw.Graph({
element: $('.graph', el)[0],
width: width,
height: height,
renderer: 'line',
offset: 'expand',
series: series
});
var xAxis = new Rickshaw.Graph.Axis.Time({
graph: graph,
timeFixture: new Rickshaw.Fixtures.Time.Local()
});
xAxis.render();
graph.render();
}
function formatData(rawData) {
var sortedData = _.sortBy(rawData, function(num) {
return num.date;
});
return _.map(sortedData, function(e) {
var ret = {
x: e.date,
y: e.sum
};
return ret;
});
}
var init = function() {
if (data.title) {
$('h2', el).text(data.title);
}
if ($('.graph', el).hasClass('rickshaw_graph')) {
$('.graph', el).empty();
}
if (!data.series.length) {
console.error('There is no results to paint the chart');
return;
}
// paint chart
var width = el.width() - 50;
var height = el.closest('li').height() - 80;
paintChart(width, height, data.series, data.color || 'yellow');
// paint min max dash
paintMinMax(data.series);
// paint time marks
paintTimeMark(width, height, data.startDate, data.endDate, data.timeMarks);
}();
}
};
Then within the job I have added :
module.exports = function(config, dependencies, job_callback) {
var text = "Hello World!";
var date1 = (new Date(2014, 4, 2, 1, 30, 0, 0))/1000;
var date2 = (new Date(2014, 5, 3, 2, 30, 0, 0))/1000;
var date3 = (new Date(2014, 6, 4, 3, 30, 0, 0))/1000;
var date4 = (new Date(2014, 7, 6, 4, 30, 0, 0))/1000;
var totalBacklogDefects = [{ x: date1, y : 40},
{ x: date2, y : 30},
{ x: date3, y : 23}, { x: date4, y : 10} ] ;
var blockedDefects = [{ x: date1, y : 32}, { x: date2, y : 22},
{ x: date3, y : 3}, { x: date4, y : 5} ] ;
var fixedDefects = [{ x: date1, y : 2}, { x: date2, y : 12},
{ x: date3 ,y : 20}, { x: date4, y : 25} ] ;
var series = [
{
data: totalBacklogDefects ,
color: 'steelblue',
name: 'Total Backlog'
},
{
data: blockedDefects ,
color: 'red',
name: 'Blocked Defects'
},
{
data: fixedDefects,
color: 'green',
name: 'Fixed'
}
];
var timeMarks = [
{
top: 0,
color: 'red',
name: 'test2',
epoch: date2
},
{
top: 0,
color: 'blue',
name: 'test1',
epoch: date3
}
];
job_callback(null, {title: "Graph Sandbox", series: series, startDate: date1 ,
endDate : date4 , timeMarks: timeMarks});
};
As for my html page I have added the following:
<h2>graphsandbox</h2>
<div class="content">
<div class="graph rickshaw_graph"></div>
</div>
Can you please let me know whether am using the right approach as currently this renders only an empty box?
The chart widget that you see in the screenshot belongs to an internal package, but I can share it with you :)
Rickshaw is available globally anyway, so you can create your own chart widgets check Rickshaw's examples.
widget = {
onData: function(el, data) {
function drawDashLine(val, max, min) {
var container = $('.content', el),
viewport = {
height: container.height(),
width: container.width()
},
topPosition = (viewport.height - Math.ceil(val/max * viewport.height));
var dashedLineLine = $('<hr />')
.attr('class', 'dashedLine')
.css({
width: el.width() - 40,
top: topPosition + 'px'
});
var lineLabel = $("<span />")
.attr('class', 'lineLabel')
.css({
top: topPosition + 'px'
})
.text(val);
container.append(dashedLineLine, lineLabel);
}
function paintMinMax(series) {
var mergedData = [];
for (var i = series.length - 1; i >= 0; i--) {
mergedData = mergedData.concat(series[i].data);
};
var min = _.min(_.pluck(mergedData, 'y')),
max = _.max(_.pluck(mergedData, 'y')),
mid = Math.round(max / 2);
drawDashLine(min, max, min);
drawDashLine(mid, max, min);
drawDashLine(max, max, min);
}
function paintTimeMark(chartWidth, chartHeight, startDate, endDate, timeMarkData) {
if (!timeMarkData || !timeMarkData.length) {
return;
}
var chartLengthMs = endDate - startDate;
for (var i = 0, l = timeMarkData.length; i < l; i++) {
var timeMark = timeMarkData[i];
// are we in the boundaries?
if ((timeMark.epoch < startDate) || (timeMark.epoch > endDate)){
continue; // out of boundaries
}
var lengthOfTimemark = timeMark.epoch - startDate;
var percentage = (lengthOfTimemark / chartLengthMs);
var leftPosition = Math.round(chartWidth * percentage);
var top = timeMark.top || 0;
var markHeight = chartHeight - top;
var color = timeMark.color || 'orange';
var mark = $('<div>')
.attr('class', 'mark')
.css({
left: leftPosition + 'px',
'margin-top': top + 'px',
'background-color' : color,
'height': markHeight + 'px'
});
var legend = $('<span>')
.attr('class', 'legend')
.css({
left: leftPosition + 'px',
'margin-top': top + 'px',
'height': markHeight + 'px'
})
.hide()
.text(timeMark.name);
$('.content', el).append(mark, legend);
var center = Math.round((legend.width() - (mark.width())) / 2);
// center align legend
(function(legend, center){
setTimeout(function(){
legend.css({
'margin-left': (-(center)) + 'px'
}).fadeIn();
},600);
}(legend, center));
}
}
function paintChart(width, height, series, color) {
for (var i = series.length - 1; i >= 0; i--) {
series[i].data = formatData(series[i].data);
}
var graph = new Rickshaw.Graph({
element: $('.graph', el)[0],
width: width,
height: height,
renderer: 'line',
offset: 'expand',
series: series
});
var xAxis = new Rickshaw.Graph.Axis.Time({
graph: graph,
timeFixture: new Rickshaw.Fixtures.Time.Local()
});
xAxis.render();
graph.render();
}
function formatData(rawData) {
var sortedData = _.sortBy(rawData, function(num) {
return num.date;
});
return _.map(sortedData, function(e) {
var ret = {
x: e.date,
y: e.sum
};
return ret;
});
}
var init = function() {
if (data.title) {
$('h2', el).text(data.title);
}
if ($('.graph', el).hasClass('rickshaw_graph')) {
$('.graph', el).empty();
}
if (!data.series.length) {
console.error('There is no results to paint the chart');
return;
}
// paint chart
var width = el.width() - 50;
var height = el.closest('li').height() - 80;
paintChart(width, height, data.series, data.color || 'yellow');
// paint min max dash
paintMinMax(data.series);
// paint time marks
paintTimeMark(width, height, data.startDate, data.endDate, data.timeMarks);
}();
}
};

Resources