Source: Chart.js

/**
 * A chart (line / column etc.)
 * <pre><strong>options:</strong> {
 *  element : «Element | ID»,
 *  name : «String»,
 *  (TODO many more)
 * }
 * </pre>
 * @constructor
 * @param {Object} options The options
 */
hui.ui.Chart = function(options) {
  this.options = options = options || {};
  this.element = hui.get(options.element);
  this.body  = {
    width : undefined,
    height : undefined,
    paddingTop : 10,
    paddingBottom : 30,
    paddingLeft : 10,
    paddingRight : 10,
    innerPaddingVertical : 10,
    innerPaddingHorizontal : 10
  };
  this.style = {
    border : true,
    background : true,
    colors : ['#36a','#69d','#acf'],
    legends : { position: 'right' , left: 0, top: 0 },
    pie : { radiusFactor: 0.9 , valueInLegend: false , left: 0, top: 0 }
  };
  this.xAxis = { labels:[], grid:true, concentration: 0.8 , maxLabels: 12};
  this.yAxis = { min:0, max:0, steps:8, above:false , factor: 10};

  this.dataSets = [];
  this.data = null;

  hui.ui.extend(this);

  if (this.options.source) {
    this.options.source.listen(this);
  }
};

hui.ui.Chart.create = function(options) {
  options.element = hui.build('div',{
    'class' : 'hui_chart',
    parent : hui.get(options.parent),
    style : 'width: 100%; height: 100%;'
  });
  return new hui.ui.Chart(options);
};

hui.ui.Chart.prototype = {
  addDataSet : function(dataSet) {
    this.dataSets[this.dataSets.length] = dataSet;
  },
  setXaxisLabels : function(labels) {
    for (var i=0; i < labels.length; i++) {
      this.xAxis.labels[this.xAxis.labels.length] = {key:labels[i],label:labels[i]};
    }
  },
  setData : function(data) {
    if (!data.dataSets) {
      this.data = hui.ui.Chart.Util.convertData(data);
    } else {
      this.data = data;
    }
  },
  render : function() {
    var renderer = new hui.ui.Chart.Renderer(this);
    renderer.render();
  },
  $$layout : function() {
    this.render();
  },
  $objectsLoaded : function(data) {
    this.setData(data);
    this.render();
  }
};


//////////////////////// Data ////////////////////

hui.ui.Chart.Data = function(options) {
  this.xAxis = hui.override({ labels:[], grid:true, concentration:0.8 , maxLabels:12},options.xAxis);
  this.yAxis = hui.override({ min:0, max:0, steps:8, above:false , factor: 10},options.yAxis);
  this.dataSets = [];
};

hui.ui.Chart.Data.prototype = {
  addDataSet : function(set) {
    this.dataSets.push(set);
  }
};


///////////////////// Data set ////////////////////

hui.ui.Chart.DataSet = function(options) {
  options = options || {};
  this.dataSets = [];
  this.entries = options.entries || [];
  this.legend = null;
  this.style = {type:options.type || 'line'};
};

hui.ui.Chart.DataSet.prototype = {

  addDataSet : function(dataSet) {
    this.dataSets[this.dataSets.length] = dataSet;
  },
  setLegend : function(legend) {
    this.legend = legend;
  },
  isMultiDimensional : function() {
    return this.dataSets.length>0;
  },
  addEntry : function(key,value) {
    this.entries[this.entries.length] = {key:key,value:value};
  },
  setValues : function(graph,values) {
    for (var i=0; i < graph.xAxis.labels.length; i++) {
      if (values[i]) {
        this.addEntry(graph.xAxis.labels[i].key,values[i]);
      }
    }
  },
  getEntryValue : function(key) {
    var value = 0;
    for (var i=0;i<this.entries.length;i++) {
      if (this.entries[i].key==key) {
        return this.entries[i].value;
      }
    }
    return value;
  },
  getEntryValue2D : function(key) {
    var value = [];
    for (var i=0;i<this.dataSets.length;i++) {
      var set = this.dataSets[i];
      for (var j=0;j<set.entries.length;j++) {
        if (set.entries[j].key==key) {
          value[i] = set.entries[j].value;
        }
      }
      if (!value[i]) {
        value[i]=0;
      }
    }
    return value;
  },
  keysToValues : function(keys) {
    var values = [];
    for (var i = 0; i < keys.length; i++) {
      values[i] = this.getEntryValue(keys[i].key);
    }
    return values;
  },
  keysToValues2D : function(keys) {
    var values = [];
    for (var i = 0; i < keys.length; i++) {
      values[i] = this.getEntryValue2D(keys[i].key);
    }
    return values;
  },
  getValueRange : function(keys) {
    var vals = [];
    if (this.isMultiDimensional()) {
      var vals2D = this.keysToValues2D(keys);
      for (var i=0; i < vals2D.length; i++) {
        var sum = 0;
        for (var j=0; j < vals2D[i].length; j++) {
          sum+=vals2D[i][j];
        }
        vals[i] = sum;
      }
    } else {
      vals = this.keysToValues(keys);
    }
    var min = Number.MAX_VALUE,
      max = Number.MIN_VALUE;
    for (var k=0; k < vals.length; k++) {
      min = Math.min(min, vals[k]);
      max = Math.max(max, vals[k]);
    }
    return {min:min,max:max};
  },
  getSubLegends : function() {
    var value = [];
    for (var i = 0; i < this.dataSets.length; i++) {
      value[i] = this.dataSets[i].legend;
    }
    return value;
  }
};

/*********************************************************************/
/*                             Renderer                              */
/*********************************************************************/

hui.ui.Chart.Renderer = function(chart) {
  this.chart = chart;
  this.crisp = false;
  this.legends = [];
  this.state = { numColumns:0, currColumn:0, xLabels:[], yLabels:[], body:{left:0}, innerBody:{}, coordinateSystem: false, currColor:0 };
  this.width = null;
  this.height = null;
};

hui.ui.Chart.Renderer.prototype = {
  _registerLegend : function(color,label) {
    this.legends[this.legends.length] = {color:color,label:label};
  },
  _buildInnerBody : function() {
    var body = this.chart.body;
    var xLabels = this.state.xLabels;
    var space = 0;
    if (this.state.numColumns>0) {
      space = ( this.width - 2 * body.innerPaddingHorizontal - body.paddingLeft - body.paddingRight ) / xLabels.length;
    }
    var innerBody = {
      left : (body.innerPaddingHorizontal + this.state.body.left + space/2),
      top : (body.paddingTop + body.innerPaddingVertical),
      width : (this.state.body.width-2 * body.innerPaddingHorizontal - space),
      height : (this.state.body.height - body.innerPaddingVertical * 2 )
    };
    return innerBody;
  },
  _buildBody : function() {
    var body = this.chart.body,
    left = body.paddingLeft + this.state.yLabelWidth;
    return {
      left : left,
      top : body.paddingTop,
      width : this.width - left - body.paddingRight,
      height : this.height - body.paddingTop - body.paddingBottom,
      right : this.width - body.paddingRight,
      bottom : this.height - body.paddingBottom
    };
  }
};

hui.ui.Chart.Renderer.prototype.render = function() {

  this.width = this.chart.body.width || this.chart.element.clientWidth;
  this.height = this.chart.body.height || this.chart.element.clientHeight;

  hui.dom.clear(this.chart.element);
  this.canvas = hui.build('canvas',{parent:this.chart.element,width:this.width,height:this.height});
  if (!this.canvas.getContext) {
    return;
  }
  this.ctx = this.canvas.getContext("2d");

  if (!hui.isDefined(this.chart.data)) {
    return;
  }

  var i, dataSet;

  // Extract basic info about the chart
  for (i=0;i<this.chart.data.dataSets.length;i++) {
    dataSet = this.chart.data.dataSets[i];
    if (dataSet.style.type=='line' || dataSet.style.type=='column') {
      this.state.coordinateSystem = true;
    }
    if (dataSet.style.type=='column') {
      this.state.numColumns++;
    }
  }

  this.state.xLabels = this.chart.data.xAxis.labels;
  this.state.yLabels = hui.ui.Chart.Util.generateYLabels(this.chart);
  this.state.yLabelWidth = 0;
  for (i = 0; i < this.state.yLabels.length; i++) {
    this.state.yLabelWidth = Math.max(this.state.yLabelWidth, String(this.state.yLabels[i]).length * 5);
  }
  this.state.yLabelWidth+=5;
  this.state.body = this._buildBody();
  this.state.innerBody = this._buildInnerBody();

  // Render the coordinate system (below)
  if (this.state.coordinateSystem) {
    this.renderBody();
  }

  // Loop through data sets and render them
  var xLabels = this.state.xLabels;
  for (i = 0; i < this.chart.data.dataSets.length; i++) {
    dataSet = this.chart.data.dataSets[i];
    var values, legend;
    if (dataSet.style.type=='line') {
      values = dataSet.keysToValues(xLabels);
      this.renderLineGraph( { values:values, style:dataSet.style , legend:dataSet.legend } );
    } else if (dataSet.style.type=='column') {
      if (dataSet.isMultiDimensional()) {
        values = dataSet.keysToValues2D(xLabels);
        legend = dataSet.getSubLegends();
      } else {
        values = dataSet.keysToValues(xLabels);
        legend = dataSet.legend;
      }
      this.renderColumnGraph( { values:values, style:dataSet.style , legend: legend} );
    } else if (dataSet.style.type=='pie') {
      values = dataSet.keysToValues(xLabels);
      this.renderPie( { values:values, style:dataSet.style } );
    }
  }

  // Render the coordinate system (above)
  if (this.shouldRenderCoordinateSystem) {
    this.renderPostBody();
  }

  // Render possible lengends
  this.renderLegends();
};

/**
 * Renders a legend box
 */
hui.ui.Chart.Renderer.prototype.renderLegends = function() {
  if (this.legends.length>0) {
    var position = this.chart.style.legends.position;
    var box = hui.build('div',{style:{position:'absolute',zIndex:5,width:this.width+'px'}});

    var html='<div class="hui_chart_legends" style="margin-right: '+(5-this.chart.style.legends.left)+'px; margin-top: '+(5+this.chart.style.legends.top)+'px;">';
    for (var i=0;i<this.legends.length;i++) {
      var style = '';
      if (position=='bottom') {
        style = 'padding: 2px; padding-right: 8px; float: left; white-space: nowrap;';
        if (i==this.legends.length-1) {
          style+='padding-right: 3px';
        }
      } else {
        style = 'padding: 2px;';
      }
      html+='<div class="hui_chart_legend" style="'+style+'"><em style="background: '+this.legends[i].color+';"></em><span>'+this.legends[i].label+'</span></div>';
    }
    html+='</div>';
    box.innerHTML = html;
    if (position=='right') {
      this.canvas.parentNode.insertBefore(box,this.canvas);
    } else if (position=='bottom') {
      this.canvas.parentNode.appendChild(box);
      var y = document.createElement('div');
      y.appendChild(box);
      this.canvas.parentNode.appendChild(y);
    }
  }
};

/**
 * Renders the body of the chart
 */
hui.ui.Chart.Renderer.prototype.renderBody = function() {

  var body = this.chart.body,
    stroke = 'rgb(255,255,255)',
    background = 'rgb(240,240,240)',
    state = this.state,
    innerBody = this.state.innerBody;

  stroke = '#eee'; // TODO Make this configurable
  background = '#fff';

  if (this.chart.style.background) {
    this.ctx.fillStyle = background;
    this.ctx.fillRect(
      state.body.left,
      state.body.top,
      state.body.width,
      state.body.height
    );
  }

  var mod = 1;
  /* Build X-axis*/
  var xLabels = this.state.xLabels;
  if (xLabels.length>this.chart.data.xAxis.maxLabels) {
    mod = Math.ceil(xLabels.length/this.chart.data.xAxis.maxLabels);
  }
  this.ctx.strokeStyle=stroke;
  for (var i = 0; i < xLabels.length; i++) {
    var left = i * ((innerBody.width) / (xLabels.length - 1)) + innerBody.left;
    left = Math.round(left);

    if (mod < 10 || (i % mod) === 0) {
      // Draw grid
      if (this.chart.data.xAxis.grid) {
        this.ctx.beginPath();
        this.ctx.moveTo(0.5 + left, state.body.top + 0.5);
        this.ctx.lineTo(0.5 + left, state.body.top + 0.5 + state.body.height);
        this.ctx.stroke();
        this.ctx.closePath();
      }
    }
    if ((i % mod) === 0) {
      // Draw label
      hui.build('span',{
        'class' : 'hui_chart_label',
        text : xLabels[i].label,
        before : this.canvas,
        style : {
          marginLeft : (left - 25) + 'px',
          marginTop : (state.body.bottom + 4) + 'px',
          color : '#999'
        }
      });
    }
  }
  this.ctx.strokeStyle=stroke;

  /* Build Y-axis*/
  var yLabels = this.state.yLabels.concat();
  yLabels.reverse();
  var top;
  for (i = 0; i < yLabels.length; i++) {
    // Draw grid
    top = i * ((state.body.height - body.innerPaddingVertical * 2) / (yLabels.length - 1)) + body.paddingTop + body.innerPaddingVertical;
    top = Math.round(top);
    if (!this.chart.data.yAxis.above) {
      this.ctx.beginPath();
      this.ctx.moveTo(0.5 + state.body.left, top + 0.5);
      this.ctx.lineTo(0.5 + state.body.right, top + 0.5);
      this.ctx.stroke();
      this.ctx.closePath();
    }
    // Draw label
    var label = hui.build('span',{text:yLabels[i],style:{
      position: 'absolute',
      textAlign : 'right',
      width : (this.state.yLabelWidth - 5) + 'px',
      font : '9px Tahoma',
      marginTop : (top - 5) +'px',
      marginLeft : body.paddingLeft+'px',
      color : '#999'
    }});
    this.canvas.parentNode.insertBefore(label,this.canvas);
  }

  // Draw a line at 0 if
  if (!this.chart.data.yAxis.above && yLabels[0] > 0 && yLabels[yLabels.length-1] < 0) {
    top = (state.body.height - body.innerPaddingVertical * 2) * yLabels[0] / (yLabels[0] - yLabels[yLabels.length - 1]) + body.paddingTop + body.innerPaddingVertical;
    top = Math.round(top);
    this.ctx.lineWidth = 2;
    this.ctx.strokeStyle=stroke;
    this.ctx.beginPath();
    this.ctx.moveTo(0.5 + state.body.left, top);
    this.ctx.lineTo(0.5 + state.body.right, top);
    this.ctx.stroke();
    this.ctx.closePath();
  }
};

hui.ui.Chart.Renderer.prototype.renderPostBody = function() {
  var body = this.chart.body;
  if (this.chart.data.yAxis.above) {

    this.ctx.strokeStyle='rgb(240,240,240)';
    var yLabels = this.state.yLabels.concat();
    yLabels.reverse();
    var top;
    for (var i = 0; i < yLabels.length; i++) {
      top = i * ((this.height - body.innerPaddingVertical * 2 - body.paddingTop - body.paddingBottom) / (yLabels.length - 1)) + body.paddingTop + body.innerPaddingVertical;
      top = Math.round(top);
      this.ctx.lineWidth = 1;
      this.ctx.beginPath();
      this.ctx.moveTo(0.5 + body.paddingLeft, top + 0.5);
      this.ctx.lineTo(0.5 + this.width - body.paddingRight, top + 0.5);
      this.ctx.stroke();
      this.ctx.closePath();
    }
    if (yLabels[0] > 0 && yLabels[yLabels.length - 1] < 0) {
      top = (this.height - body.innerPaddingVertical * 2 - body.paddingTop - body.paddingBottom) * yLabels[0] / (yLabels[0] - yLabels[yLabels.length - 1]) + body.paddingTop + body.innerPaddingVertical;
      top = Math.round(top);
      this.ctx.lineWidth = 2;
      this.ctx.strokeStyle = 'rgb(255,255,255)';
      this.ctx.beginPath();
      this.ctx.moveTo(0.5 + body.paddingLeft,top);
      this.ctx.lineTo(0.5 + this.width - body.paddingRight,top);
      this.ctx.stroke();
      this.ctx.closePath();
    }
  }
  if (this.chart.style.border) {
    this.ctx.lineWidth = 1;
    this.ctx.strokeStyle='rgb(230,230,230)';
    this.ctx.strokeRect(body.paddingLeft + 0.5, body.paddingTop + 0.5, this.width-body.paddingLeft - body.paddingRight, this.height - body.paddingTop - body.paddingBottom);
  }
};

hui.ui.Chart.Renderer.prototype.renderLineGraph = function(data) {
  var values = data.values;
  var xLabels = this.state.xLabels;
  var yLabels = this.state.yLabels;
  var yMin = yLabels[0];
  var yMax = yLabels[yLabels.length - 1];
  var body = this.chart.body;
  var innerBody = this.state.innerBody;
  var color;
  if (data.style.colors) {
    color = data.style.colors[0];
  } else {
    color = this.chart.style.colors[this.state.currColor];
    if (this.state.currColor + 2 > this.chart.style.colors.length) {
      this.state.currColor = 0;
    } else {
      this.state.currColor++;
    }
  }
  this.ctx.strokeStyle = color;
  this.ctx.lineWidth = data.width ? data.width : 3;
  this.ctx.lineCap = this.ctx.lineJoin = 'round';
  this.ctx.beginPath();
  for (var i = 0; i < xLabels.length; i++) {
    var amount = (values[i] === undefined ? 0 : values[i]);
    var value = (amount - yMin) / (yMax - yMin);
    var top = this.height - value * (innerBody.height) - body.innerPaddingVertical - body.paddingBottom;
    var left = i * (innerBody.width / (xLabels.length - 1)) + innerBody.left;
    if (i === 0) {
      this.ctx.moveTo(left + 0.5, top + 0.5);
    } else {
      this.ctx.lineTo(left + 0.5, top + 0.5);
    }
  }
  this.ctx.stroke();
  this.ctx.closePath();

  if (data.legend) {
    this._registerLegend(color,data.legend);
  }
};


hui.ui.Chart.Renderer.prototype.renderColumnGraph = function(data) {
  var values = data.values;
  var xLabels = this.state.xLabels;
  var yLabels = this.state.yLabels;
  var yMin = yLabels[0];
  var yMax = yLabels[yLabels.length-1];
  var body = this.chart.body;
  var colors = data.style.colors ? data.style.colors : this.chart.style.colors;
  this.state.currColumn++;
  var innerBody = this.state.innerBody;
  var space = (this.width - body.paddingLeft - body.paddingRight) / xLabels.length * this.chart.data.xAxis.concentration;
  var thickness = space / this.state.numColumns;
  this.ctx.lineCap = this.ctx.lineJoin = 'round';
  this.ctx.beginPath();
  for (var i = 0; i < xLabels.length; i++) {
    if (values[i]) {
      var colorIndex = 0;
      var currTop = 0;
      if (values[i] instanceof Array) {
        for (var j = 0; j < values[i].length; j++) {
          var val = values[i][j];
          currTop += this.renderOneColumn(val, colors[colorIndex], body, innerBody, yMin, yMax, currTop, i, xLabels, space, thickness);

          if (colorIndex+2>colors.length) {
            colorIndex = 0;
          } else {
            colorIndex++;
          }
        }
      } else {
        currTop += this.renderOneColumn(values[i], colors[colorIndex], body, innerBody, yMin, yMax, currTop, i, xLabels, space, thickness);
      }
    }
  }
  this.ctx.stroke();
  this.ctx.closePath();

  if (data.legend && data.legend instanceof Array) {
    for (var k = 0; k < data.legend.length; k++) {
      this._registerLegend(colors[k], data.legend[k]);
    }
  } else if (data.legend) {
    this._registerLegend(colors[0], data.legend);
  }
};

hui.ui.Chart.Renderer.prototype.renderOneColumn = function(val,color,body,innerBody,yMin,yMax,currTop,i,xLabels,space,thickness) {
  var value = (val - yMin) / (yMax - yMin);
  var height, top;
  if (yMin<=0 && val<=0) {
    top = innerBody.top + (innerBody.height) * yMax / (yMax - yMin) + currTop;
    height = innerBody.height * Math.abs(val) / (yMax - yMin);
  } else if (yMin <= 0) {
    top = this.height - body.innerPaddingVertical - body.paddingBottom - value * (innerBody.height) - currTop;
    height = (innerBody.height) * Math.abs(val) / (yMax - yMin);
  }
  else {
    top = this.height - value * (this.height - body.innerPaddingVertical * 2 - body.paddingTop - body.paddingBottom) - body.innerPaddingVertical - body.paddingBottom - currTop;
    height = (this.height - body.paddingBottom - top);
  }
  var left = i* ((innerBody.width) / (xLabels.length - 1)) + innerBody.left;

  this.ctx.fillStyle = color;
  if (this.crisp) {
    this.ctx.fillRect(Math.round(left - space / 2 + thickness * (this.state.currColumn - 1)), Math.floor(top), Math.ceil(thickness), Math.ceil(height));
  } else {
    this.ctx.fillRect(left - space / 2 + thickness * (this.state.currColumn - 1), top, thickness, height);
  }
  return height;
};

hui.ui.Chart.Renderer.prototype.renderPie = function(data) {

  var values = data.values;
  var colors = data.style.colors ? data.style.colors : this.chart.style.colors;
  var total = hui.ui.Chart.Util.arraySum(values);

  var colorIndex = 0;
  var current = Math.PI * 1.5;
  var cTop = this.height / 2 + this.chart.style.pie.top;
  var cLeft = this.width / 2 + this.chart.style.pie.left;
  var radius = this.height / 2 * this.chart.style.pie.radiusFactor;

  for (var i = 0; i < values.length; i++) {
    this.ctx.beginPath();
    var color = colors[colorIndex];
    this.ctx.fillStyle = color;
    var rads = values[i] / total * (Math.PI * 2);
    this.ctx.moveTo(cLeft, cTop);
    this.ctx.arc(cLeft, cTop, radius, current, current + rads, false);
    this.ctx.lineTo(cLeft, cTop);
    this.ctx.fill();
    this.ctx.closePath();
    current+=rads;

    if (!true) {
      this._registerLegend(color, this.state.xLabels[i].label);
    } else {
      this._registerLegend(color, values[i] + ' ' + this.state.xLabels[i].label);
    }
    if (colorIndex + 2 > colors.length) {
      colorIndex = 0;
    } else {
      colorIndex++;
    }
  }

};





/*********************************************************************/
/*                           Utitlities                              */
/*********************************************************************/

hui.ui.Chart.Util = function() {};

hui.ui.Chart.Util.generateYLabels = function(graph) {
  var range = hui.ui.Chart.Util.getYrange(graph);
  var labels = [];
  for (var i = 0; i <= graph.yAxis.steps; i++) {
    labels[labels.length] = Math.round(range.min + (range.max - range.min) / graph.yAxis.steps * i);
  }
  return labels;
};

hui.ui.Chart.Util.getYrange = function(graph) {
  var min = graph.yAxis.min,
    max = graph.yAxis.max,
    data = graph.data;
  for (var i = 0; i < data.dataSets.length; i++) {
    var range = data.dataSets[i].getValueRange(data.xAxis.labels);
    min = Math.min(min, range.min);
    max = Math.max(max, range.max);
  }
  var factor = max / graph.yAxis.steps;
  if (factor < graph.yAxis.factor) {
    factor = Math.ceil(factor);
  } else {
    factor = graph.yAxis.factor;
  }
  if (max != Number.MIN_VALUE) {
    max = Math.ceil(max / factor / graph.yAxis.steps) * factor * graph.yAxis.steps;
  } else {
    max = graph.yAxis.steps;
  }
  return {min: min, max : max};
};

hui.ui.Chart.Util.arraySum = function(values) {
  var total = 0;
  for (var i = 0; i < values.length; i++) {
    total += values[i];
  }
  return total;
};

/** Converts a simple data-representation into a class-based stucture */
hui.ui.Chart.Util.convertData = function(obj) {
  var labels = [],keys = [];
  var i;
  for (i = 0; i < obj.sets.length; i++) {
    var set = obj.sets[i];
    if (hui.isArray(set.entries)) {
      for (var j=0; j < set.entries.length; j++) {
        var entry = set.entries[j];
        if (!hui.array.contains(keys, entry.key)) {
          keys.push(entry.key);
          labels.push({key: entry.key, label: entry.label || entry.key});
        }
      }
    } else {
      for (var key in set.entries) {
        if (!hui.array.contains(keys, key)) {
          keys.push(key);
          labels.push({key: key, label: key});
        }
      }
    }
  }
  var options = {xAxis: {labels: labels}};
  if (obj.axis && obj.axis.x && obj.axis.x.time===true) {
    options.xAxis.resolution = 'time';
  }
  if (obj.axis && obj.axis.x && hui.isArray(obj.axis.x.labels)) {
    options.xAxis.labels = obj.axis.x.labels;
  }
  var data = new hui.ui.Chart.Data(options);

  for (i = 0; i < obj.sets.length; i++) {
    var setData = obj.sets[i];
    var dataSet = new hui.ui.Chart.DataSet({type : setData.type});
    if (hui.isArray(setData.entries)) {
      for (var k = 0; k < setData.entries.length; k++) {
        var dataEntry = setData.entries[k];
        dataSet.addEntry(dataEntry.key, dataEntry.value);
      }
    } else {
      for (var setKey in setData.entries) {
        dataSet.addEntry(setKey, setData.entries[setKey]);
      }
    }
    data.addDataSet(dataSet);
  }
  return data;
};