function d3waffle() {
var margin = {top: 10, right: 10, bottom: 10, left: 10},
icon = "■",
scale = 1,
rows = 10,
adjust = 0.8,
colorscale = d3.scale.category20(),
appearancetimes = function(d, i){ return 500; },
height = 200,
magic_padding = 5;
function chart(selection) {
selection.each(function(data) {
selection.selectAll("*").remove();
/* setting parameters and data */
var idcontainer = selection[0][0].id; // I need to change thiz plz
var total = d3.sum(data, function(d) { return d.value; });
/* updating data */
data.forEach(function(d, i){
data[i].class = slugify(d.name);
data[i].scalevalue = Math.round(data[i].value*scale);
data[i].percent = data[i].value/total;
});
var totalscales = d3.sum(data, function(d){ return d.scalevalue; })
var cols = Math.ceil(totalscales/rows);
var griddata = cartesianprod(d3.range(cols), d3.range(rows));
var detaildata = [];
data.forEach(function(d){
d3.range(d.scalevalue).forEach(function(e){
detaildata.push({ name: d.name, class: d.class })
});
});
detaildata.forEach(function(d, i){
detaildata[i].col = griddata[i][0];
detaildata[i].row = griddata[i][1];
})
/*console.log("detail data length: ", detaildata.length)*/
var gridSize = ((height - margin.top - margin.bottom) / rows)
/* setting the container */
var svg = selection.append("svg")
.attr("width", "100%")
.attr("height", height + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.style("cursor", "default");
var tooltip = d3.select("body")
.append("div")
.attr("class", "waffle-tooltip")
.style("position", "absolute")
.style("text-align", "right")
.style("background", "#333")
.style("margin", "3px")
.style("color","white")
.style("padding","3px")
.style("border","0px")
.style("border-radius","3px") // 3px rule
.style("opacity",0)
.style("cursor", "default");
var nodes = svg.selectAll(".node")
.data(detaildata)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + (d.col)*gridSize + "," + (rows - d.row - 1)*gridSize + ")"; });
/* this is necesary, when the icons are small/thin activate mouseout */
nodes.append("text")
.style("opacity", 0)
.html(icon)
.attr('class', function(d){ return d.class; })
.attr('font-family', 'FontAwesome')
.attr("transform", function(d) { return "translate(" + gridSize/2 + "," + 5/6*gridSize + ")"; })
.style("text-anchor", "middle")
.style('fill', function(d){ return colorscale(d.class); })
.style("font-size", function(d) {
val = 9;
val2 = 2.5;
textsize = Math.min(val2 * gridSize, (val2 * gridSize - val) / this.getComputedTextLength() * val);
return textsize * adjust + "px";
})
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("mousemove", mousemove)
.transition()
.duration(appearancetimes)
.style("opacity", 1);
nodes.append("rect")
.style("fill", "white")
.attr('class', function(d){ return d.class; })
.style("stroke", "gray")
.attr("width", gridSize)
.attr("height", gridSize)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("mousemove", mousemove)
.style("opacity", 0)
var legend = svg.selectAll('.legend')
.data(data)
.enter().append('g')
.attr('class', function(d){ return "legend" + " " + d.class; })
.attr("transform", function(d) { return "translate(" + (cols*gridSize + magic_padding) + "," + magic_padding + ")"; })
legend.append('text')
.attr('x', gridSize)
.attr('y', function(d, i){ return i * gridSize + i * magic_padding / 2;})
.style("opacity", 1)
.html(function(d){ return icon; })
.attr('class', function(d){ return d.class; })
.attr('font-family', 'FontAwesome')
.attr("transform", function(d) { return "translate(" + gridSize/2 + "," + 5/6*gridSize + ")"; })
.style('fill', function(d){ return colorscale(d.class); })
/*.style("font-size", function(d) {
val = 9;
val2 = 2.5;
textsize = Math.min(val2 * gridSize, (val2 * gridSize - val) / this.getComputedTextLength() * val);
return textsize * adjust + "px";
});*/
legend.append('text')
.attr('x', 1.5*gridSize + magic_padding)
.attr('y', function(d, i){ return i * gridSize + i * magic_padding / 2;})
.style("opacity", 1)
.html(function(d){ return d.name; })
.attr('class', function(d){ return "waffle-legend-text" + " " + d.class; })
.attr("transform", function(d) { return "translate(" + gridSize/2 + "," + 5/6*gridSize + ")"; })
function mouseover(d){
tooltip.transition().duration(100).style("opacity", .9);
el = data.filter(function(e){ return e.name == d.name})[0]
txt = "" +el.name + "
" + d3.format(',')(el.value) + "
(" + d3.format(".0%")(el.percent) + ")"
tooltip.html(txt);
d3.select("#" + idcontainer).selectAll("text").transition().duration(100).style("opacity", 0.2);
d3.select("#" + idcontainer).selectAll("text." + d.class).transition().duration(100).style("opacity", 1);
}
function mouseout(d){
tooltip.transition().duration(100).style("opacity", 0);
d3.select("#" + idcontainer).selectAll("text").transition().duration(100).style("opacity", 1);
}
function mousemove(d){
tooltip
.style("left", (d3.event.pageX + 0 ) + "px")
.style("top", (d3.event.pageY + - 70) + "px");
}
});
}
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.rows = function(_) {
if (!arguments.length) return rows;
rows = _;
return chart;
};
chart.icon = function(_) {
if (!arguments.length) return icon;
icon = _;
return chart;
};
chart.scale = function(_) {
if (!arguments.length) return scale;
scale = _;
return chart;
};
chart.colorscale = function(_) {
if (!arguments.length) return colorscale;
colorscale = _;
return chart;
};
chart.appearancetimes = function(_) {
if (!arguments.length) return appearancetimes;
appearancetimes = _;
return chart;
};
chart.adjust = function(_) {
if (!arguments.length) return adjust;
adjust = _;
return chart;
};
return chart;
}
function slugify(text){
return text.toString().toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.trim(); // Trim - from end of text
}
/* http://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript */
function cartesianprod(paramArray) {
function addTo(curr, args) {
var i, copy,
rest = args.slice(1),
last = !rest.length,
result = [];
for (i = 0; i < args[0].length; i++) {
copy = curr.slice();
copy.push(args[0][i]);
if (last) {
result.push(copy);
} else {
result = result.concat(addTo(copy, rest));
}
}
return result;
}
return addTo([], Array.prototype.slice.call(arguments));
}