var WaffleChart = function() { var $_selector, $_data, $_label, $_cellSize, $_cellGap, $_rows, $_columns, $_rounded, $_keys, $_useWidth; var defaults = { size: 6, rows: 50, columns: 100, rounded: false, gap: 2 }; function generatedWaffleChart() { $_keys = d3.keys($_data[0]); var obj = { selector: $_selector, data: $_data, label: $_label, size: $_cellSize, gap: $_cellGap, rows: $_rows, columns: $_columns, rounded: $_rounded }; drawWaffleChart(obj); } function drawWaffleChart(_obj) { if (!_obj.size) { _obj.size = defaults.size; } if (!_obj.rows) { _obj.rows = defaults.rows; } if (!_obj.columns) { _obj.columns = defaults.columns; } if (_obj.gap === undefined) { _obj.gap = defaults.gap; } if (_obj.rounded === undefined) { _obj.columns = defaults.rounded; } var formattedData = []; var domain = []; var value = $_keys[$_keys.length - 1]; var total = d3.sum(_obj.data, function(d) { return d[value]; }); if ($_useWidth) { var forcedWidth = d3.select(_obj.selector).node().getBoundingClientRect().width; _obj.columns = Math.floor(forcedWidth / (_obj.size + _obj.gap)); } var squareVal = total / (_obj.rows * _obj.columns); _obj.data.forEach(function(d, i) { d[value] = +d[value]; d.units = Math.floor(d[value] / squareVal); Array(d.units + 1).join(1).split('').map(function() { formattedData.push({ squareVal: squareVal, units: d.units, value: d[value], groupIndex: i }); }); domain.push(d[$_keys[0]]); }); var red = "#CE2A23"; var color = d3.scale.linear() .domain([1, _obj.data.length - 1]) .interpolate(d3.interpolateRgb) .range(["#555", "#EEE"]); // add label if (_obj.label) { d3.select(_obj.selector) .append("div") .attr("class", "label") .text(_obj.label); } // add legend var legend = d3.select($_selector) .append("div") .attr("class", "legend"); var legendItem = legend.selectAll("div") .data(_obj.data); legendItem.enter() .append("div") .attr("class", function(d, i) { return "legend_item legend_item_" + (i + 1); }); var legendIcon = legendItem.append("div") .attr("class", "legend_item_icon") .style("background-color", function(d, i) { if (i === 0) { return red; } else { return color(i); } }); if (_obj.rounded) { legendIcon.style("border-radius", "50%"); } legendItem.append("span") .attr("class", "legend_item_text") .text(function(d) { return d[$_keys[0]]; }); // set up the dimensions var width = (_obj.size * _obj.columns) + (_obj.columns * _obj.gap) - _obj.gap; var height = (_obj.size * _obj.rows) + (_obj.rows * _obj.gap) - _obj.gap; if ($_useWidth) { width = d3.select(_obj.selector).node().getBoundingClientRect().width; } var svg = d3.select(_obj.selector) .append("svg") .attr("class", "waffle") .attr("width", width) .attr("height", height); var g = svg.append("g") .attr("transform", "translate(0,0)"); // insert dem items var item = g.selectAll(".unit") .data(formattedData); item.enter() .append("rect") .attr("class", "unit") .attr("width", _obj.size) .attr("height", _obj.size) .attr("fill", function(d) { if (d.groupIndex === 0) { return red; } else { return color(d.groupIndex); } }) .attr("x", function(d, i) { var col = Math.floor(i / _obj.rows); return (col * (_obj.size)) + (col * _obj.gap); }) .attr("y", function(d, i) { var row = i % _obj.rows; return (_obj.rows * (_obj.size + _obj.gap)) - ((row * _obj.size) + (row * _obj.gap)) - _obj.size - _obj.gap; }) .append("title") .text(function (d, i) { return _obj.data[d.groupIndex][$_keys[0]] + ": " + Math.round((d.units / formattedData.length) * 100) + "%"; }); if (_obj.rounded) { item .attr("rx", (_obj.size / 2)) .attr("ry", (_obj.size / 2)); } } generatedWaffleChart.selector = function(value){ if (!arguments.length) { return $_selector; } $_selector = value; return generatedWaffleChart; } generatedWaffleChart.data = function(value){ if (!arguments.length) { return $_data; } $_data = value; return generatedWaffleChart; } generatedWaffleChart.useWidth = function(value){ if (!arguments.length) { return $_useWidth; } $_useWidth = value; return generatedWaffleChart; } generatedWaffleChart.label = function(value){ if (!arguments.length) { return $_label; } $_label = value; return generatedWaffleChart; } generatedWaffleChart.size = function(value){ if (!arguments.length) { return $_cellSize; } $_cellSize = value; return generatedWaffleChart; } generatedWaffleChart.gap = function(value){ if (!arguments.length) { return $_cellGap; } $_cellGap = value; return generatedWaffleChart; } generatedWaffleChart.rows = function(value){ if (!arguments.length) { return $_rows; } $_rows = value; return generatedWaffleChart; } generatedWaffleChart.columns = function(value){ if (!arguments.length) { return $_columns; } $_columns = value; return generatedWaffleChart; } generatedWaffleChart.rounded = function(value){ if (!arguments.length) { return $_rounded; } $_rounded = value; return generatedWaffleChart; } return generatedWaffleChart; };