diff --git a/client/plugins/radar.coffee b/client/plugins/radar.coffee
new file mode 100644
index 00000000..37fa9b9a
--- /dev/null
+++ b/client/plugins/radar.coffee
@@ -0,0 +1,115 @@
+window.plugins.radar =
+ bind: (div, item) ->
+ emit: (div, item) ->
+ wiki.getScript '/js/d3/d3.js', ->
+ wiki.getScript '/js/d3/d3.time.js', ->
+ div.append '''
+
+ '''
+
+ who = $('.chart,.data,.calculator').last()
+ data = who.data('item').data
+
+ # Adapted from https://gist.github.com/1630683
+
+ w = 400
+ h = 400
+ vizPadding =
+ top: 10
+ right: 0
+ bottom: 15
+ left: 0
+
+ dimension = 7
+ ruleColor = "#CCC"
+
+ randomFromTo = randomFromTo = (from, to) ->
+ Math.floor Math.random() * (to - from + 1) + from
+
+ series = [ [], [] ]
+ hours = []
+ i = 0
+ while i < dimension
+ series[0][i] = randomFromTo(-10, 20)
+ series[1][i] = randomFromTo(5, 20)
+ hours[i] = i
+ i += 1
+ mergedArr = series[0].concat(series[1])
+ minVal = d3.min(mergedArr)
+ maxVal = d3.max(mergedArr)
+ maxVal = maxVal + ((maxVal - minVal) * 0.15)
+ i = 0
+ while i < series.length
+ series[i].push series[i][0]
+ i += 1
+
+ viz = d3.select(div.get(0))
+ .append("svg:svg")
+ .attr("width", w)
+ .attr("height", h)
+ .attr("class", "vizSvg")
+ vizBody = viz.append("svg:g")
+ .attr("id", "body")
+
+ heightCircleConstraint = h - vizPadding.top - vizPadding.bottom
+ widthCircleConstraint = w - vizPadding.left - vizPadding.right
+ circleConstraint = d3.min([ heightCircleConstraint, widthCircleConstraint ])
+ radius = d3.scale.linear()
+ .domain([ minVal, maxVal ])
+ .range([ 0, (circleConstraint / 2) ])
+ radiusLength = radius(maxVal)
+ centerXPos = widthCircleConstraint / 2 + vizPadding.left
+ centerYPos = heightCircleConstraint / 2 + vizPadding.top
+ vizBody.attr "transform", "translate(" + centerXPos + ", " + centerYPos + ")"
+
+ radialTicks = radius.ticks(5)
+ circleAxes = vizBody.selectAll(".circle-ticks")
+ .data(radialTicks)
+ .enter()
+ .append("svg:g")
+ .attr("class", "circle-ticks")
+ circleAxes.append("svg:circle")
+ .attr("r", (d, i) -> radius d )
+ .attr("class", "circle")
+ .style("stroke", ruleColor)
+ .style "fill", "none"
+ circleAxes.append("svg:text")
+ .attr("text-anchor", "middle")
+ .style("stroke", ruleColor)
+ .attr("dy", (d) -> -1 * radius(d) )
+ .text String
+ lineAxes = vizBody.selectAll(".line-ticks")
+ .data(hours)
+ .enter()
+ .append("svg:g")
+ .attr("transform", (d, i) -> "rotate(" + ((i / hours.length * 360) - 90) + ")translate(" + radius(maxVal) + ")" )
+ .attr("class", "line-ticks")
+ lineAxes.append("svg:line")
+ .attr("x2", -1 * radius(maxVal))
+ .style("stroke", ruleColor)
+ .style "fill", "none"
+ lineAxes.append("svg:text")
+ .text(String)
+ .attr("text-anchor", "middle")
+ .style("stroke", ruleColor)
+ .attr "transform", (d, i) -> (if (i / hours.length * 360) < 180 then null else "rotate(180)")
+
+ colorSelector = (d, i) -> if i is 0 then "green" else "blue"
+ vizBody.selectAll(".series")
+ .data(series)
+ .enter()
+ .append("svg:g")
+ .attr("class", "series")
+ .append("svg:path")
+ .attr("class", "line")
+ .style("fill", colorSelector)
+ .style("stroke", colorSelector)
+ .style("stroke-width", 3)
+ .style("fill-opacity", .1)
+ .style("fill", colorSelector)
+ .attr("d", d3.svg.line.radial()
+ .radius((d) -> radius d )
+ .angle((d, i) -> (i / dimension) * 2 * Math.PI ))
+ .append("svg:title").text((d,i) -> "Material #{i+1}")
diff --git a/client/plugins/radar.js b/client/plugins/radar.js
new file mode 100644
index 00000000..35438c87
--- /dev/null
+++ b/client/plugins/radar.js
@@ -0,0 +1,91 @@
+(function() {
+
+ window.plugins.radar = {
+ bind: function(div, item) {},
+ emit: function(div, item) {
+ return wiki.getScript('/js/d3/d3.js', function() {
+ return wiki.getScript('/js/d3/d3.time.js', function() {
+ var centerXPos, centerYPos, circleAxes, circleConstraint, colorSelector, data, dimension, h, heightCircleConstraint, hours, i, lineAxes, maxVal, mergedArr, minVal, radialTicks, radius, radiusLength, randomFromTo, ruleColor, series, viz, vizBody, vizPadding, w, who, widthCircleConstraint;
+ div.append(' ');
+ who = $('.chart,.data,.calculator').last();
+ data = who.data('item').data;
+ w = 400;
+ h = 400;
+ vizPadding = {
+ top: 10,
+ right: 0,
+ bottom: 15,
+ left: 0
+ };
+ dimension = 7;
+ ruleColor = "#CCC";
+ randomFromTo = randomFromTo = function(from, to) {
+ return Math.floor(Math.random() * (to - from + 1) + from);
+ };
+ series = [[], []];
+ hours = [];
+ i = 0;
+ while (i < dimension) {
+ series[0][i] = randomFromTo(-10, 20);
+ series[1][i] = randomFromTo(5, 20);
+ hours[i] = i;
+ i += 1;
+ }
+ mergedArr = series[0].concat(series[1]);
+ minVal = d3.min(mergedArr);
+ maxVal = d3.max(mergedArr);
+ maxVal = maxVal + ((maxVal - minVal) * 0.15);
+ i = 0;
+ while (i < series.length) {
+ series[i].push(series[i][0]);
+ i += 1;
+ }
+ viz = d3.select(div.get(0)).append("svg:svg").attr("width", w).attr("height", h).attr("class", "vizSvg");
+ vizBody = viz.append("svg:g").attr("id", "body");
+ heightCircleConstraint = h - vizPadding.top - vizPadding.bottom;
+ widthCircleConstraint = w - vizPadding.left - vizPadding.right;
+ circleConstraint = d3.min([heightCircleConstraint, widthCircleConstraint]);
+ radius = d3.scale.linear().domain([minVal, maxVal]).range([0, circleConstraint / 2]);
+ radiusLength = radius(maxVal);
+ centerXPos = widthCircleConstraint / 2 + vizPadding.left;
+ centerYPos = heightCircleConstraint / 2 + vizPadding.top;
+ vizBody.attr("transform", "translate(" + centerXPos + ", " + centerYPos + ")");
+ radialTicks = radius.ticks(5);
+ circleAxes = vizBody.selectAll(".circle-ticks").data(radialTicks).enter().append("svg:g").attr("class", "circle-ticks");
+ circleAxes.append("svg:circle").attr("r", function(d, i) {
+ return radius(d);
+ }).attr("class", "circle").style("stroke", ruleColor).style("fill", "none");
+ circleAxes.append("svg:text").attr("text-anchor", "middle").style("stroke", ruleColor).attr("dy", function(d) {
+ return -1 * radius(d);
+ }).text(String);
+ lineAxes = vizBody.selectAll(".line-ticks").data(hours).enter().append("svg:g").attr("transform", function(d, i) {
+ return "rotate(" + ((i / hours.length * 360) - 90) + ")translate(" + radius(maxVal) + ")";
+ }).attr("class", "line-ticks");
+ lineAxes.append("svg:line").attr("x2", -1 * radius(maxVal)).style("stroke", ruleColor).style("fill", "none");
+ lineAxes.append("svg:text").text(String).attr("text-anchor", "middle").style("stroke", ruleColor).attr("transform", function(d, i) {
+ if ((i / hours.length * 360) < 180) {
+ return null;
+ } else {
+ return "rotate(180)";
+ }
+ });
+ colorSelector = function(d, i) {
+ if (i === 0) {
+ return "green";
+ } else {
+ return "blue";
+ }
+ };
+ return vizBody.selectAll(".series").data(series).enter().append("svg:g").attr("class", "series").append("svg:path").attr("class", "line").style("fill", colorSelector).style("stroke", colorSelector).style("stroke-width", 3).style("fill-opacity", .1).style("fill", colorSelector).attr("d", d3.svg.line.radial().radius(function(d) {
+ return radius(d);
+ }).angle(function(d, i) {
+ return (i / dimension) * 2 * Math.PI;
+ })).append("svg:title").text(function(d, i) {
+ return "Material " + (i + 1);
+ });
+ });
+ });
+ }
+ };
+
+}).call(this);