Skip to content

Instantly share code, notes, and snippets.

@hur1can3
Last active December 11, 2015 05:18
Show Gist options
  • Select an option

  • Save hur1can3/4550783 to your computer and use it in GitHub Desktop.

Select an option

Save hur1can3/4550783 to your computer and use it in GitHub Desktop.
a parallel coordinate gits
Country % GDP per capita Physicians Nurses Total hospital beds Discharges ALOS Life expectancy Infant Mortality Tobacco Alcohol Obesity
Australia 9.1 3670.2 3.08 10.06 3.73 15549.1 5.1 81.8 4.1 15.1 10.3 21.3
Austria 11 4394.8 4.78 7.67 7.63 26099.8 6.6 80.7 3.9 23.2 12.2 12.4
Belgium 10.5 3968.8 2.92 15.06 6.44 17840.7 8.1 80.3 3.5 20.5 10.8 13.8
Canada 11.4 4444.9 2.37 9.34 3.19 8260.4 7.7 80.8 5.1 16.3 8.2 17.5
Chile 8 1202.2 1.43 1.51 2.04 9936 5.6 79 7.9 29.8 8.6 12.1
Czech Republic 7.5 1883.5 3.58 8.06 7.01 19163 7.1 77.7 2.7 24.6 11.4 17.4
Denmark 11.1 4463.9 3.48 15.44 3.5 17153.7 4.6 79.3 3.4 20 10.3 13.4
Estonia 6.3 1293.8 3.24 6.13 5.33 16933.6 7 75.6 3.3 26.2 11.4 16.9
Finland 8.9 3250.9 3.27 9.58 5.85 18159.2 11.6 80.2 2.3 19 9.7 15.6
France 11.6 3974 3.27 8.45 6.42 16859.1 5.7 81.3 3.6 23.3 12 12.9
Germany 11.6 4338.4 3.73 11.27 8.25 23984.2 9.5 80.5 3.4 21.9 11.7 14.7
Greece 10.2 2913.7 6.13 3.31 4.85 19488.2 7 80.6 3.8 31.9 8.2 17.3
Hungary 7.8 1600.5 2.87 6.22 7.18 18501.6 5.1 74.3 5.3 26.5 11.5 19.5
Iceland 9.3 3309.3 3.6 14.54 5.79 14082.2 5.8 81.5 2.2 14.3 7.3 21
Ireland 9.2 3718.2 3.13 13.07 3.14 13155.5 6.1 81 3.8 29 11.9 15
Israel 7.5 2071.1 3.5 4.76 3.31 14638.5 4.5 81.7 3.7 18.6 2.4 16
Italy 9.3 2963.7 3.68 6.3 3.52 12751.2 6.7 82 3.4 23.1 6.9 10.3
Japan 9.5 3034.6 2.23 10.11 13.62 10708.9 18.2 83 2.3 19.5 7.3 ..
Korea 7.1 2035.4 1.99 4.63 8.76 16886 14.2 80.7 3.2 22.9 8.95 2
Luxembourg 7.9 4786 2.77 11.1 5.37 14943.8 7.5 80.7 3.4 18 15.3 ..
Mexico 6.2 915.7 2.03 2.48 1.64 5654.5 3.9 75.5 14.1 13.3 5.9 ..
Netherlands 12 5056.2 2.92 8.4 4.66 11583.8 5.8 80.8 3.8 20.9 9.4 11.4
New Zealand 10.1 3022.1 2.61 10.03 2.74 14695.8 8.1 81 5.2 18.1 9.6 ..
Norway 9.4 5387.6 4.07 14.39 3.3 17525.6 4.5 81.2 2.8 19 6.6 10
Poland 7 1388.7 2.18 5.26 6.59 20288.5 6 76.3 5 23.8 10.1 15.8
Portugal 10.7 2727.7 3.82 5.65 3.35 11247.3 5.9 79.8 2.5 18.6 11.4 15.4
Slovak Republic 9 2095.5 3.34 6.03 6.42 21196.7 7.3 75.2 5.7 19.5 10.7 15.1
Slovenia 9 2428.5 2.43 8.19 4.57 15606.1 6.3 79.5 2.5 18.9 10.3 16.4
Spain 9.6 3055.7 3.78 4.88 3.16 10246.2 6.8 82.2 3.2 26.2 11.4 16
Sweden 9.6 3757.7 3.8 11 2.73 16305.5 5.7 81.5 2.5 14 7.3 12.9
Switzerland 11.4 5269.6 3.81 16.03 4.97 16887 9.6 82.6 3.8 20.4 10 8.1
Turkey 6.1 913 1.69 1.6 2.52 14239.2 4.1 74.3 10.1 25.4 1.5 16.9
United Kingdom 9.6 3433.2 2.71 9.6 2.96 13595.9 7.7 80.6 4.2 21.5 10.2 ..
United States 17.6 8232.9 2.44 10.95 3.08 13100.1 4.9 78.7 6.1 15.1 8.7 28.1
// Parallel Coordinates
// Copyright (c) 2012, Kai Chang
// Released under the BSD License: http://opensource.org/licenses/BSD-3-Clause
//from ryb.js
var Points, RYB, display, generateColors, numberColors, __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for(var key in parent) {
if(__hasProp.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
RYB = {
white: [1, 1, 1],
red: [1, 0, 0],
yellow: [1, 1, 0],
blue: [0.163, 0.373, 0.6],
violet: [0.5, 0, 0.5],
green: [0, 0.66, 0.2],
orange: [1, 0.5, 0],
black: [0.2, 0.094, 0.0],
rgb: function(r, y, b) {
var i, _i, _results;
_results = [];
for(i = _i = 0; _i <= 2; i = ++_i) {
_results.push(RYB.white[i] * (1 - r) * (1 - b) * (1 - y) + RYB.red[i] * r * (1 - b) * (1 - y) + RYB.blue[i] * (1 - r) * b * (1 - y) + RYB.violet[i] * r * b * (1 - y) + RYB.yellow[i] * (1 - r) * (1 - b) * y + RYB.orange[i] * r * (1 - b) * y + RYB.green[i] * (1 - r) * b * y + RYB.black[i] * r * b * y);
}
return _results;
}
};
Points = (function(_super) {
__extends(Points, _super);
Points.name = 'Points';
function Points(number) {
var base, n, _i, _ref;
base = Math.ceil(Math.pow(number, 1 / 3));
console.log(base);
for(n = _i = 0, _ref = Math.pow(base, 3); 0 <= _ref ? _i < _ref : _i > _ref; n = 0 <= _ref ? ++_i : --_i) {
this.push([Math.floor(n / (base * base)) / (base - 1), Math.floor(n / base % base) / (base - 1), Math.floor(n % base) / (base - 1)]);
}
this.picked = null;
this.plength = 0;
}
Points.prototype.distance = function(p1) {
var _this = this;
return [0, 1, 2].map(function(i) {
return Math.pow(p1[i] - _this.picked[i], 2);
}).reduce(function(a, b) {
return a + b;
});
};
Points.prototype.pick = function() {
var index, pick, _, _ref, _this = this;
if(this.picked == null) {
pick = this.picked = this.shift();
this.plength = 1;
} else {
_ref = this.reduce(function(_arg, p2, i2) {
var d1, d2, i1;
i1 = _arg[0], d1 = _arg[1];
d2 = _this.distance(p2);
if(d1 < d2) {
return [i2, d2];
} else {
return [i1, d1];
}
}, [0, this.distance(this[0])]), index = _ref[0], _ = _ref[1];
pick = this.splice(index, 1)[0];
this.picked = [0, 1, 2].map(function(i) {
return(_this.plength * _this.picked[i] + pick[i]) / (_this.plength + 1);
});
this.plength++;
}
return pick;
};
return Points;
})(Array);
generateColors = function(numberColors) {
var b, color, el, g, i, number, point, points, r, _i, _ref, _results;
number = numberColors;
points = new Points(number);
point = null;
_results = [];
_results[0] = $.Color(302, 1, 0.6, 0.6).toHslaString();
_results[1] = $.Color(255, 0.6, 0.6, 0.6).toHslaString();
for(i = _i = 2; 1 <= number ? _i <= number : _i >= number; i = 1 <= number ? ++_i : --_i) {
point = points.pick(point);
_ref = RYB.rgb.apply(RYB, point).map(function(x) {
return Math.floor(255 * x);
}), r = _ref[0], g = _ref[1], b = _ref[2];
color = $.Color(r, g, b, 0.6).toHslaString();
_results[i] = color;
}
return _results;
};
var width = document.body.clientWidth,
height = d3.max([document.body.clientHeight - 440, 500]);
var m = [60, 0, 10, 0],
w = width - m[1] - m[3],
h = height - m[0] - m[2],
xscale = d3.scale.ordinal().rangePoints([0, w], 1),
yscale = {},
dragging = {},
line = d3.svg.line(),
axis = d3.svg.axis().orient("left").ticks(5, d3.format("s")),
data, foreground, background, highlighted, dimensions, legend, render_speed = 50,
brush_count = 0,
excluded_groups = [];
//Set the total number of unique markers/colours
var countOfColors = 36;
var colorMap = [];
colorMap = generateColors(countOfColors);
var colors = {
//NOT AN ACTUAL CATEOGRY, JUST FOR THE AVERAGE LINE
"Average": colorMap[0],
//USED FOR ANY MAPPING NOT DEFINED
"other": colorMap[1],
// USER defined color mappings here:
"Australia": colorMap[2],
"Austria": colorMap[3],
"Belgium": colorMap[4],
"Canada": colorMap[5],
"Chile": colorMap[6],
"Czech Republic": colorMap[7],
"Denmark": colorMap[8],
"Estonia": colorMap[9],
"Finland": colorMap[10],
"France": colorMap[11],
"Germany": colorMap[12],
"Ireland": colorMap[13],
"Japan": colorMap[14],
"Mexico": colorMap[15],
"New Zealand": colorMap[16],
"Norway": colorMap[17],
"Poland": colorMap[18],
"Spain": colorMap[19],
"Sweden": colorMap[20],
"Switzerland": colorMap[21],
"United Kingdom": colorMap[22],
"United States": colorMap[23],
"Greece": colorMap[24],
"Hungary": colorMap[25],
"Iceland": colorMap[26],
"Israel": colorMap[27],
"Italy": colorMap[28],
"Korea": colorMap[29],
"Luxembourg": colorMap[30],
"Netherlands": colorMap[31],
"Portugal": colorMap[32],
"Slovak Republic": colorMap[33],
"Slovenia": colorMap[34],
"Turkey": colorMap[35]
};
// Scale chart and canvas height
d3.select("#chart").style("height", (h + m[0] + m[2]) + "px")
d3.selectAll("canvas").attr("width", w).attr("height", h).style("padding", m.join("px ") + "px");
// Foreground canvas for primary view
foreground = document.getElementById('foreground').getContext('2d');
foreground.globalCompositeOperation = "destination-over";
foreground.strokeStyle = "rgba(0,100,160,0.1)";
foreground.lineWidth = 1.7;
foreground.fillText("Loading data for rendering...", w / 2.2, h / 2);
// Highlight canvas for temporary interactions
highlighted = document.getElementById('highlight').getContext('2d');
highlighted.strokeStyle = "rgba(0,100,160,1)";
highlighted.lineWidth = 4;
// Background canvas
background = document.getElementById('background').getContext('2d');
background.strokeStyle = "rgba(0,100,160,0.1)";
background.lineWidth = 1.7;
// SVG for ticks, labels, and interactions
var svg = d3.select("svg").attr("width", w + m[1] + m[3]).attr("height", h + m[0] + m[2]).append("svg:g").attr("transform", "translate(" + m[3] + "," + m[0] + ")");
// Load the data and visualization
d3.csv("2012oecd.csv", function(raw_data) {
// Convert quantitative scales to floats
data = [];
for(i = 0; i < raw_data.length; i++) {
d = raw_data[i];
for(var k in d) {
//exclude name and idcolumn
if(!_.isNaN(raw_data[0][k] - 0) && k != 'id' && k != 'Country') {
d[k] = parseFloat(d[k]) || 0;
}
};
show_ticks();
data[i] = d;
};
// Extract the list of numerical dimensions and create a scale for each.
xscale.domain(dimensions = d3.keys(data[0]).filter(function(k) {
if(k == "Est. Downloads*" || k == "Reviews") {
return(_.isNumber(data[0][k])) && (yscale[k] = d3.scale.log().domain(d3.extent(data, function(d) {
return +d[k];
})).range([h, 0]));
}
return(_.isNumber(data[0][k])) && (yscale[k] = d3.scale.linear().domain(d3.extent(data, function(d) {
return +d[k];
})).range([h, 0]));
}));
// Add a group element for each dimension.
var g = svg.selectAll(".dimension").data(dimensions).enter().append("svg:g").attr("class", "dimension").attr("transform", function(d) {
return "translate(" + xscale(d) + ")";
})
// Add an axis and title.
each = g.append("svg:g").attr("class", "axis").attr("transform", "translate(0,0)").each(function(d) {
d3.select(this).call(axis.scale(yscale[d]));
})
each.append("svg:text").attr("text-anchor", "middle").attr("y", -20).attr("transform", "rotate(0) translate(-6,-8)").attr("x", 3).attr("class", "label").text(String)
each.append("svg:text").attr("id", function(d) {
return "column-" + dimensions.indexOf(d)
}).attr("text-anchor", "middle").attr("y", -4).attr("x", 3).attr("transform", "rotate(0) translate(-6,-8)").attr("class", "label-average")
// Add and store a brush for each axis.
g.append("svg:g").attr("class", "brush").each(function(d) {
d3.select(this).call(yscale[d].brush = d3.svg.brush().y(yscale[d]).on("brush", brush));
}).selectAll("rect").style("visibility", null).attr("x", -15).attr("width", 30).append("title").text("Drag up or down to brush along this axis");
g.selectAll(".extent").append("title").text("Drag or resize this filter");
legend = create_legend(colors, brush);
// Render full foreground
brush();
});
// copy one canvas to another, grayscale
function gray_copy(source, target) {
var pixels = source.getImageData(0, 0, w, h);
target.putImageData(grayscale(pixels), 0, 0);
}
// http://www.html5rocks.com/en/tutorials/canvas/imagefilters/
function grayscale(pixels, args) {
var d = pixels.data;
for(var i = 0; i < d.length; i += 4) {
var r = d[i];
var g = d[i + 1];
var b = d[i + 2];
// CIE luminance for the RGB
// The human eye is bad at seeing red and blue, so we de-emphasize them.
var v = 0.2126 * r + 0.7152 * g + 0.0722 * b;
d[i] = d[i + 1] = d[i + 2] = v
}
return pixels;
};
function create_legend(colors, brush) {
// create legend
var legend_data = d3.select("#legend").html("").selectAll(".row").data(_.difference(_.keys(colors), ["Average"]).sort())
// filter by group
var legend = legend_data.enter().append("div").attr("title", "Hide group").on("click", function(d) {
// toggle category
if(_.contains(excluded_groups, d)) {
excluded_groups = _.difference(excluded_groups, [d]);
brush();
} else {
excluded_groups.push(d);
brush();
}
});
legend.append("span").style("background", function(d, i) {
return color(d, 0.85)
}).attr("class", "color-bar");
legend.append("span").attr("class", "tally").text(function(d, i) {
return 0
});
legend.append("span").text(function(d, i) {
return " " + d
});
return legend;
}
// render polylines i to i+render_speed
function render_range(selection, i, max, opacity) {
range = selection.slice(i, max)
for(i = 0; i < range.length; i++) {
d = range[i]
path(d, foreground, color(d.Country, opacity));
}
};
// simple data table
function data_table(sample) {
// sort by first column
var sample = sample.sort(function(a, b) {
var col = d3.keys(a)[0];
return a[col] < b[col] ? -1 : 1;
});
var table = d3.select("#category-list").html("").selectAll(".row").data(sample).enter().append("div").attr("class", "data-row").on("mouseover", highlight).on("mouseout", unhighlight);
table.append("span").attr("class", "color-block").style("background", function(d) {
return color(d.Country, 0.85)
})
table.append("span").text(function(d) {
return d.Country;
})
}
// Adjusts rendering speed
function optimize(timer) {
var delta = (new Date()).getTime() - timer;
render_speed = Math.max(Math.ceil(render_speed * 40 / delta), 15);
render_speed = Math.min(render_speed, 400);
return(new Date()).getTime();
}
// Feedback on rendering progress
function render_stats(i, n, render_speed) {
d3.select("#rendered-count").text(i);
d3.select("#rendered-bar").style("width", (100 * i / n) + "%");
d3.select("#render-speed").text(render_speed);
}
// Feedback on selection
function selection_stats(opacity, n, total) {
d3.select("#data-count").text(total);
d3.select("#selected-count").text(n);
d3.select("#selected-bar").style("width", (100 * n / total) + "%");
d3.select("#opacity").text(("" + (opacity * 100)).slice(0, 4) + "%");
}
// Highlight single polyline
function highlight(d) {
d3.select("#foreground").style("opacity", "0.25");
d3.selectAll(".row").style("font-weight", function(p) {
return(d.Country == p) ? "bold" : null
}).style("opacity", function(p) {
return(d.Country == p) ? null : "0.3"
});
path(d, highlighted, color(d.Country, 1));
}
// Remove highlight
function unhighlight() {
d3.select("#foreground").style("opacity", null);
d3.selectAll(".row").style("font-weight", null).style("opacity", null);
highlighted.clearRect(0, 0, w, h);
}
// Draw a single polyline
function path(d, ctx, color) {
if(color) ctx.strokeStyle = color;
var x = xscale(0) - 15;
y = yscale[dimensions[0]](d[dimensions[0]]); // left edge
x_orig = -1
ctx.beginPath();
for(i = 0; i < dimensions.length; i++) {
p = dimensions[i];
x = xscale(p), y = yscale[p](d[p]);
if(x_orig != -1) {
ctx.bezierCurveTo(x_orig + 65, y_orig, x - 65, y, x, y);
} else {
ctx.moveTo(x, y);
}
x_orig = x
y_orig = y
};
ctx.stroke();
}
function color(d, a) {
var c = colors[d];
return $.Color(c).alpha(a).toHslaString();
}
function position(d) {
var v = dragging[d];
return v == null ? xscale(d) : v;
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
brush_count++;
var actives = dimensions.filter(function(p) {
return !yscale[p].brush.empty();
}),
extents = new Array(actives.length);
for(i = 0; i < extents.length; i++) {
extents[i] = yscale[actives[i]].brush.extent()
}
// hack to hide ticks beyond extent
var b = d3.selectAll('.dimension')[0];
for(i = 0; i < b.length; i++) {
element = b[i];
var dimension = d3.select(element).data()[0];
d3.select(element).selectAll('text').style('font-size', null).style('font-weight', null).style('display', null);
d3.select(element).selectAll('.label').style('display', null);
};
// bold dimensions with label
d3.selectAll('.label').style("font-weight", function(dimension) {
if(_.include(actives, dimension)) return "bold";
return null;
});
// Get lines within extents
var selected = [];
filtered_data = data.filter(function(d) {
return !_.contains(excluded_groups, d.Country);
})
for(i = 0; i < filtered_data.length; i++) {
d = filtered_data[i];
actives.every(function(p, dimension) {
return extents[dimension][0] <= d[p] && d[p] <= extents[dimension][1];
}) ? selected.push(d) : null;
}
averageLine = {};
var formatter = d3.format("0.3s")
var currencyFormatter = d3.format(",.2f")
_.each(dimensions, function(d, i) {
average = _.reduce(selected, function(memo, num) {
return memo + num[d];
}, 0) / selected.length
averageLine[d] = average
displayedAverage = average
if(d == "Price") {
d3.select("#column-" + i).text("$" + currencyFormatter(average))
} else {
d3.select("#column-" + i).text(formatter(average))
}
});
window.averageLine = averageLine
// free text search
var query = d3.select("#search")[0][0].value;
if(query.length > 0) {
selected = search(selected, query);
}
// total by categories
var tallies = _(selected).groupBy(function(d) {
return d.Country;
})
// include empty groups
_(colors).each(function(v, k) {
tallies[k] = tallies[k] || [];
});
legend.style("text-decoration", function(d) {
return _.contains(excluded_groups, d) ? "line-through" : null;
}).attr("class", function(d) {
return(tallies[d].length > 0) ? "row" : "row off";
});
maximumLength = d3.max(_.map(tallies, function(d) {
return d.length
}));
legend.selectAll(".color-bar").style("width", function(d) {
return Math.ceil(300 * tallies[d].length / (maximumLength + 1)) + "px"
});
legend.selectAll(".tally").text(function(d, i) {
return tallies[d].length
});
// Render selected lines
paths(selected, foreground, brush_count, true);
path(averageLine, foreground, color("Average", 1))
show_ticks()
}
// render a set of polylines on a canvas
function paths(selected, ctx, count) {
var n = selected.length,
i = 0,
opacity = d3.min([2 / Math.pow(n, 0.3), 1]),
timer = (new Date()).getTime();
selection_stats(opacity, n, data.length)
shuffled_data = _.shuffle(selected);
data_table(shuffled_data.slice(0, 25));
ctx.clearRect(0, 0, w + 1, h + 1);
// render all lines until finished or a new brush event
function animloop() {
if(i >= n || count < brush_count) return true;
var max = d3.min([i + render_speed, n]);
render_range(shuffled_data, i, max, opacity);
render_stats(max, n, render_speed);
i = max;
timer = optimize(timer); // adjusts render_speed
path(averageLine, foreground, color("Average", 1))
};
d3.timer(animloop);
}
// transition ticks for reordering, rescaling and inverting
function update_ticks(d, extent) {
// update brushes
if(d) {
var brush_el = d3.selectAll(".brush").filter(function(key) {
return key == d;
});
// single tick
if(extent) {
// restore previous extent
brush_el.call(yscale[d].brush = d3.svg.brush().y(yscale[d]).extent(extent).on("brush", brush));
} else {
brush_el.call(yscale[d].brush = d3.svg.brush().y(yscale[d]).on("brush", brush));
}
} else {
// all ticks
d3.selectAll(".brush").each(function(d) {
d3.select(this).call(yscale[d].brush = d3.svg.brush().y(yscale[d]).on("brush", brush));
})
}
brush_count++;
// update axes
d3.selectAll(".axis").each(function(d, i) {
// transition axis numbers
d3.select(this).transition().duration(720).call(axis.scale(yscale[d]));
// bring lines back
d3.select(this).selectAll('line').transition().delay(800).style("display", null);
d3.select(this).selectAll('text').style('font-weight', null).style('font-size', null).style('display', null);
});
}
// Rescale to new dataset domain
function rescale() {
// Render selected data
averageLine = {};
_.each(dimensions, function(d, i) {
average = _.reduce(selected, function(memo, num) {
return memo + num[d];
}, 0) / selected.length
averageLine[d] = average
});
paths(data, foreground, brush_count);
path(averageLine, foreground, color("Average", 1))
update_ticks();
}
// Get polylines within extents
function actives() {
var actives = dimensions.filter(function(p) {
return !yscale[p].brush.empty();
}),
extents = new Array(actives.length);
for(i = 0; i < extents.length; i++) {
extents[i] = yscale[actives[i]].brush.extent()
}
// filter extents and excluded groups
var selected = [];
data.filter(function(d) {
return !_.contains(excluded_groups, d.Country);
})
for(i = 0; i < data.length; i++) {
d = data[i]
actives.every(function(p, i) {
return extents[i][0] <= d[p] && d[p] <= extents[i][1];
}) ? selected.push(d) : null;
}
// free text search
var query = d3.select("#search")[0][0].value;
if(query > 0) {
selected = search(selected, query);
}
return selected;
}
// scale to window size
window.onresize = function() {
width = document.body.clientWidth, height = d3.max([document.body.clientHeight - 500, 500]);
w = width - m[1] - m[3], h = height - m[0] - m[2];
d3.select("#chart").style("height", (h + m[0] + m[2]) + "px")
d3.selectAll("canvas").attr("width", w).attr("height", h).style("padding", m.join("px ") + "px");
d3.select("svg").attr("width", w + m[1] + m[3]).attr("height", h + m[0] + m[2]).select("g").attr("transform", "translate(" + m[3] + "," + m[0] + ")");
xscale = d3.scale.ordinal().rangePoints([0, w], 1).domain(dimensions);
dimensions.forEach(function(d) {
yscale[d].range([h, 0]);
});
d3.selectAll(".dimension").attr("transform", function(d) {
return "translate(" + xscale(d) + ")";
})
// update brush placement
d3.selectAll(".brush").each(function(d) {
d3.select(this).call(yscale[d].brush = d3.svg.brush().y(yscale[d]).on("brush", brush));
})
brush_count++;
// update axis placement
axis = axis.ticks(1 + height / 50), d3.selectAll(".axis").each(function(d) {
d3.select(this).call(axis.scale(yscale[d]).ticks(5, d3.format("s")));
});
// render data
brush();
path(averageLine, foreground, color("Average", 1))
};
function remove_axis(d, g) {
dimensions = _.difference(dimensions, [d]);
xscale.domain(dimensions);
g.attr("transform", function(p) {
return "translate(" + position(p) + ")";
});
g.filter(function(p) {
return p == d;
}).remove();
update_ticks();
}
d3.select("#search").on("keyup", brush);
d3.select("#dark-theme").on("click", dark_theme);
d3.select("#light-theme").on("click", light_theme);
d3.select("#remove-filters").on("click", resetFilters);
d3.select("#unselect-all").on("click", unselectAll);
d3.select("#select-all").on("click", selectAll);
function resetFilters() {
update_ticks()
brush();
}
function unselectAll() {
excluded_groups = _.keys(colors);
d3.selectAll("#select-all").attr("disabled", null);
d3.selectAll("#unselect-all").attr("disabled", "disabled");
brush();
}
function selectAll() {
excluded_groups = [];
d3.selectAll("#select-all").attr("disabled", "disabled");
d3.selectAll("#unselect-all").attr("disabled", null);
brush();
}
function show_ticks() {
d3.selectAll(".axis g").style("display", "inline");
//d3.selectAll(".axis path").style("display", null);
d3.selectAll(".background").style("visibility", null);
};
function dark_theme() {
d3.select("body").attr("class", "dark");
d3.selectAll("#dark-theme").attr("disabled", "disabled");
d3.selectAll("#light-theme").attr("disabled", null);
}
function light_theme() {
d3.select("body").attr("class", null);
d3.selectAll("#light-theme").attr("disabled", "disabled");
d3.selectAll("#dark-theme").attr("disabled", null);
}
function search(selection, str) {
pattern = new RegExp(str, "i");
_.each(dimensions, function(d, i) {
d3.select("#column-" + i).text(" ");
});
return _(selection).filter(function(d) {
return pattern.exec(d.name);
});
}

This parallel coordinates visualization of OECD 2012 Health Data demonstrates one of D3 2.5.0’s new interactive features: the brush component. By clicking and dragging along any axis, you can specify a filter for that dimension. The brush component is also used in the updated scatterplot matrix example.

Adapted from mike bostock's example

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>2012 OECD Health Data</title>
<link rel="stylesheet" type="text/css" href="parallel.css" />
</head>
<body>
<div id="header">
<div id="data-size" class="data-size">
<span>Select Theme:</span>
<button id="dark-theme" onmouseover="this.style.cursor='pointer'">Dark Theme</button><button id="light-theme" onmouseover="this.style.cursor='pointer'" disabled="disabled">Light Theme</button><br>
<span>data:</span>
<strong id="rendered-count"></strong>/<strong id="selected-count"></strong><!--<strong id="data-count"></strong>-->
<div class="fillbar"><div id="selected-bar"><div id="rendered-bar">&nbsp;</div></div></div>
<strong style="display:none" id="opacity"></strong>
</div>
<div style="min-width:540px; width:740px;">
<h1 class="header-top">2012 OECD Health Data</h1>via Parallel Coordinates
<br>
<span class="header-top">Made by Matthew Levandowski</span>
</div>
<div style="clear:both;"></div>
</div>
<div id="chart">
<canvas id="background"></canvas>
<canvas id="foreground"></canvas>
<canvas id="highlight"></canvas>
<svg></svg>
</div>
<div id="wrap">
<div class="third">
<div class="little-box">
<h3>Controls:</h3>
<button title="Remove selected data" id="remove-filters" onmouseover="this.style.cursor='pointer'" enabled="enabled">Reset Filters</button>
<p>
<strong>Filter</strong>: Drag along an axis<br>
<strong>Remove Filter</strong>: Click the axis background<br>
<strong>Drag Filter</strong> to help visualize the relation
</p>
<p>
<strong style="color:#1bff32;">——————</strong> : Average line<br>
</p>
<br>
<h3>About:</h3>
<p>
Based on data gathered from excel
<br><br>
Visualized using the Parallel Coordinates technique and d3.js. Reviews and Downloads axis are scaled logarithmically
</p>
<br>
*Download Health Data from <a href="http://www.oecd.org/els/healthpoliciesanddata/oecdhealthdata2012-frequentlyrequesteddata.htm">OECD</a>
</div>
</div>
<div class="third">
<small>
<!--Last rendered <strong id="render-speed"></strong> lines-->
</small>
<h3>Categories:</h3><button id="unselect-all" onmouseover="this.style.cursor='pointer'">Unselect All Categories</button><button id="select-all" disabled="disabled" onmouseover="this.style.cursor='pointer'">Select All Categories</button>
<p id="legend">
</p>
</div>
<div class="third">
<div id="sorted-by"></div>
<h3>Sample Data: <input type="text" id="search" placeholder="Search Data..."></h3>
<p id="category-list">
</p>
</div>
</div>
</body>
<div id="credits">
Credits, etc:
Adapted from an <a href="http://bl.ocks.org/d/3267951/">example</a> by <a href="https://github.com/syntagmatic">Kai Chang</a>. Download estimates from <a href="http://www.oecd.org/els/healthpoliciesanddata/">OECD.org</a>.
Released under the <a href="http://opensource.org/licenses/bsd-3-clause">BSD License</a>.
</div>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.0.1/d3.v3.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.3/underscore-min.js "></script>
<script src="jquery.color.js"></script>
<script src="2012oecd.js"></script>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</html>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>2012 OECD Health Data</title>
<link rel="stylesheet" type="text/css" href="parallel.css" />
</head>
<body>
<div id="header">
<div id="data-size" class="data-size">
<span>Select Theme:</span>
<button id="dark-theme" onmouseover="this.style.cursor='pointer'">Dark Theme</button><button id="light-theme" onmouseover="this.style.cursor='pointer'" disabled="disabled">Light Theme</button><br>
<span>data:</span>
<strong id="rendered-count"></strong>/<strong id="selected-count"></strong><!--<strong id="data-count"></strong>-->
<div class="fillbar"><div id="selected-bar"><div id="rendered-bar">&nbsp;</div></div></div>
<strong style="display:none" id="opacity"></strong>
</div>
<div style="min-width:540px; width:740px;">
<h1 class="header-top">2012 OECD Health Data</h1>via Parallel Coordinates
<br>
<span class="header-top">Made by Matthew Levandowski</span>
</div>
<div style="clear:both;"></div>
</div>
<div id="chart">
<canvas id="background"></canvas>
<canvas id="foreground"></canvas>
<canvas id="highlight"></canvas>
<svg></svg>
</div>
<div id="wrap">
<div class="third">
<div class="little-box">
<h3>Controls:</h3>
<button title="Remove selected data" id="remove-filters" onmouseover="this.style.cursor='pointer'" enabled="enabled">Reset Filters</button>
<p>
<strong>Filter</strong>: Drag along an axis<br>
<strong>Remove Filter</strong>: Click the axis background<br>
<strong>Drag Filter</strong> to help visualize the relation
</p>
<p>
<strong style="color:#1bff32;">——————</strong> : Average line<br>
</p>
<br>
<h3>About:</h3>
<p>
Based on data gathered from excel
<br><br>
Visualized using the Parallel Coordinates technique and d3.js. Reviews and Downloads axis are scaled logarithmically
</p>
<br>
*Download Health Data from <a href="http://www.oecd.org/els/healthpoliciesanddata/oecdhealthdata2012-frequentlyrequesteddata.htm">OECD</a>
</div>
</div>
<div class="third">
<small>
<!--Last rendered <strong id="render-speed"></strong> lines-->
</small>
<h3>Categories:</h3><button id="unselect-all" onmouseover="this.style.cursor='pointer'">Unselect All Categories</button><button id="select-all" disabled="disabled" onmouseover="this.style.cursor='pointer'">Select All Categories</button>
<p id="legend">
</p>
</div>
<div class="third">
<div id="sorted-by"></div>
<h3>Sample Data: <input type="text" id="search" placeholder="Search Data..."></h3>
<p id="category-list">
</p>
</div>
</div>
</body>
<div id="credits">
Credits, etc:
Adapted from an <a href="http://bl.ocks.org/d/3267951/">example</a> by <a href="https://github.com/syntagmatic">Kai Chang</a>. Download estimates from <a href="http://www.oecd.org/els/healthpoliciesanddata/">OECD.org</a>.
Released under the <a href="http://opensource.org/licenses/bsd-3-clause">BSD License</a>.
</div>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.0.1/d3.v3.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.3/underscore-min.js "></script>
<script src="jquery.color.js"></script>
<script src="2012oecd.js"></script>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</html>
/*!
* jQuery Color Animations v@VERSION
* https://github.com/jquery/jquery-color
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* Date: @DATE
*/
(function( jQuery, undefined ) {
var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
// plusequals test for += 100 -= 100
rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
// a set of RE's that can match strings and generate color tuples.
stringParsers = [{
re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
parse: function( execResult ) {
return [
execResult[ 1 ],
execResult[ 2 ],
execResult[ 3 ],
execResult[ 4 ]
];
}
}, {
re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
parse: function( execResult ) {
return [
execResult[ 1 ] * 2.55,
execResult[ 2 ] * 2.55,
execResult[ 3 ] * 2.55,
execResult[ 4 ]
];
}
}, {
// this regex ignores A-F because it's compared against an already lowercased string
re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
parse: function( execResult ) {
return [
parseInt( execResult[ 1 ], 16 ),
parseInt( execResult[ 2 ], 16 ),
parseInt( execResult[ 3 ], 16 )
];
}
}, {
// this regex ignores A-F because it's compared against an already lowercased string
re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
parse: function( execResult ) {
return [
parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
];
}
}, {
re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
space: "hsla",
parse: function( execResult ) {
return [
execResult[ 1 ],
execResult[ 2 ] / 100,
execResult[ 3 ] / 100,
execResult[ 4 ]
];
}
}],
// jQuery.Color( )
color = jQuery.Color = function( color, green, blue, alpha ) {
return new jQuery.Color.fn.parse( color, green, blue, alpha );
},
spaces = {
rgba: {
props: {
red: {
idx: 0,
type: "byte"
},
green: {
idx: 1,
type: "byte"
},
blue: {
idx: 2,
type: "byte"
}
}
},
hsla: {
props: {
hue: {
idx: 0,
type: "degrees"
},
saturation: {
idx: 1,
type: "percent"
},
lightness: {
idx: 2,
type: "percent"
}
}
}
},
propTypes = {
"byte": {
floor: true,
max: 255
},
"percent": {
max: 1
},
"degrees": {
mod: 360,
floor: true
}
},
support = color.support = {},
// element for support tests
supportElem = jQuery( "<p>" )[ 0 ],
// colors = jQuery.Color.names
colors,
// local aliases of functions called often
each = jQuery.each;
// determine rgba support immediately
supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
// define cache name and alpha properties
// for rgba and hsla spaces
each( spaces, function( spaceName, space ) {
space.cache = "_" + spaceName;
space.props.alpha = {
idx: 3,
type: "percent",
def: 1
};
});
function clamp( value, prop, allowEmpty ) {
var type = propTypes[ prop.type ] || {};
if ( value == null ) {
return (allowEmpty || !prop.def) ? null : prop.def;
}
// ~~ is an short way of doing floor for positive numbers
value = type.floor ? ~~value : parseFloat( value );
// IE will pass in empty strings as value for alpha,
// which will hit this case
if ( isNaN( value ) ) {
return prop.def;
}
if ( type.mod ) {
// we add mod before modding to make sure that negatives values
// get converted properly: -10 -> 350
return (value + type.mod) % type.mod;
}
// for now all property types without mod have min and max
return 0 > value ? 0 : type.max < value ? type.max : value;
}
function stringParse( string ) {
var inst = color(),
rgba = inst._rgba = [];
string = string.toLowerCase();
each( stringParsers, function( i, parser ) {
var parsed,
match = parser.re.exec( string ),
values = match && parser.parse( match ),
spaceName = parser.space || "rgba";
if ( values ) {
parsed = inst[ spaceName ]( values );
// if this was an rgba parse the assignment might happen twice
// oh well....
inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
rgba = inst._rgba = parsed._rgba;
// exit each( stringParsers ) here because we matched
return false;
}
});
// Found a stringParser that handled it
if ( rgba.length ) {
// if this came from a parsed string, force "transparent" when alpha is 0
// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
if ( rgba.join() === "0,0,0,0" ) {
jQuery.extend( rgba, colors.transparent );
}
return inst;
}
// named colors
return colors[ string ];
}
color.fn = jQuery.extend( color.prototype, {
parse: function( red, green, blue, alpha ) {
if ( red === undefined ) {
this._rgba = [ null, null, null, null ];
return this;
}
if ( red.jquery || red.nodeType ) {
red = jQuery( red ).css( green );
green = undefined;
}
var inst = this,
type = jQuery.type( red ),
rgba = this._rgba = [];
// more than 1 argument specified - assume ( red, green, blue, alpha )
if ( green !== undefined ) {
red = [ red, green, blue, alpha ];
type = "array";
}
if ( type === "string" ) {
return this.parse( stringParse( red ) || colors._default );
}
if ( type === "array" ) {
each( spaces.rgba.props, function( key, prop ) {
rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
});
return this;
}
if ( type === "object" ) {
if ( red instanceof color ) {
each( spaces, function( spaceName, space ) {
if ( red[ space.cache ] ) {
inst[ space.cache ] = red[ space.cache ].slice();
}
});
} else {
each( spaces, function( spaceName, space ) {
var cache = space.cache;
each( space.props, function( key, prop ) {
// if the cache doesn't exist, and we know how to convert
if ( !inst[ cache ] && space.to ) {
// if the value was null, we don't need to copy it
// if the key was alpha, we don't need to copy it either
if ( key === "alpha" || red[ key ] == null ) {
return;
}
inst[ cache ] = space.to( inst._rgba );
}
// this is the only case where we allow nulls for ALL properties.
// call clamp with alwaysAllowEmpty
inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
});
// everything defined but alpha?
if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
// use the default of 1
inst[ cache ][ 3 ] = 1;
if ( space.from ) {
inst._rgba = space.from( inst[ cache ] );
}
}
});
}
return this;
}
},
is: function( compare ) {
var is = color( compare ),
same = true,
inst = this;
each( spaces, function( _, space ) {
var localCache,
isCache = is[ space.cache ];
if (isCache) {
localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
each( space.props, function( _, prop ) {
if ( isCache[ prop.idx ] != null ) {
same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
return same;
}
});
}
return same;
});
return same;
},
_space: function() {
var used = [],
inst = this;
each( spaces, function( spaceName, space ) {
if ( inst[ space.cache ] ) {
used.push( spaceName );
}
});
return used.pop();
},
transition: function( other, distance ) {
var end = color( other ),
spaceName = end._space(),
space = spaces[ spaceName ],
startColor = this.alpha() === 0 ? color( "transparent" ) : this,
start = startColor[ space.cache ] || space.to( startColor._rgba ),
result = start.slice();
end = end[ space.cache ];
each( space.props, function( key, prop ) {
var index = prop.idx,
startValue = start[ index ],
endValue = end[ index ],
type = propTypes[ prop.type ] || {};
// if null, don't override start value
if ( endValue === null ) {
return;
}
// if null - use end
if ( startValue === null ) {
result[ index ] = endValue;
} else {
if ( type.mod ) {
if ( endValue - startValue > type.mod / 2 ) {
startValue += type.mod;
} else if ( startValue - endValue > type.mod / 2 ) {
startValue -= type.mod;
}
}
result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
}
});
return this[ spaceName ]( result );
},
blend: function( opaque ) {
// if we are already opaque - return ourself
if ( this._rgba[ 3 ] === 1 ) {
return this;
}
var rgb = this._rgba.slice(),
a = rgb.pop(),
blend = color( opaque )._rgba;
return color( jQuery.map( rgb, function( v, i ) {
return ( 1 - a ) * blend[ i ] + a * v;
}));
},
toRgbaString: function() {
var prefix = "rgba(",
rgba = jQuery.map( this._rgba, function( v, i ) {
return v == null ? ( i > 2 ? 1 : 0 ) : v;
});
if ( rgba[ 3 ] === 1 ) {
rgba.pop();
prefix = "rgb(";
}
return prefix + rgba.join() + ")";
},
toHslaString: function() {
var prefix = "hsla(",
hsla = jQuery.map( this.hsla(), function( v, i ) {
if ( v == null ) {
v = i > 2 ? 1 : 0;
}
// catch 1 and 2
if ( i && i < 3 ) {
v = Math.round( v * 100 ) + "%";
}
return v;
});
if ( hsla[ 3 ] === 1 ) {
hsla.pop();
prefix = "hsl(";
}
return prefix + hsla.join() + ")";
},
toHexString: function( includeAlpha ) {
var rgba = this._rgba.slice(),
alpha = rgba.pop();
if ( includeAlpha ) {
rgba.push( ~~( alpha * 255 ) );
}
return "#" + jQuery.map( rgba, function( v ) {
// default to 0 when nulls exist
v = ( v || 0 ).toString( 16 );
return v.length === 1 ? "0" + v : v;
}).join("");
},
toString: function() {
return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
}
});
color.fn.parse.prototype = color.fn;
// hsla conversions adapted from:
// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
function hue2rgb( p, q, h ) {
h = ( h + 1 ) % 1;
if ( h * 6 < 1 ) {
return p + (q - p) * h * 6;
}
if ( h * 2 < 1) {
return q;
}
if ( h * 3 < 2 ) {
return p + (q - p) * ((2/3) - h) * 6;
}
return p;
}
spaces.hsla.to = function ( rgba ) {
if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
return [ null, null, null, rgba[ 3 ] ];
}
var r = rgba[ 0 ] / 255,
g = rgba[ 1 ] / 255,
b = rgba[ 2 ] / 255,
a = rgba[ 3 ],
max = Math.max( r, g, b ),
min = Math.min( r, g, b ),
diff = max - min,
add = max + min,
l = add * 0.5,
h, s;
if ( min === max ) {
h = 0;
} else if ( r === max ) {
h = ( 60 * ( g - b ) / diff ) + 360;
} else if ( g === max ) {
h = ( 60 * ( b - r ) / diff ) + 120;
} else {
h = ( 60 * ( r - g ) / diff ) + 240;
}
// chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
// otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
if ( diff === 0 ) {
s = 0;
} else if ( l <= 0.5 ) {
s = diff / add;
} else {
s = diff / ( 2 - add );
}
return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
};
spaces.hsla.from = function ( hsla ) {
if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
return [ null, null, null, hsla[ 3 ] ];
}
var h = hsla[ 0 ] / 360,
s = hsla[ 1 ],
l = hsla[ 2 ],
a = hsla[ 3 ],
q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
p = 2 * l - q;
return [
Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
Math.round( hue2rgb( p, q, h ) * 255 ),
Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
a
];
};
each( spaces, function( spaceName, space ) {
var props = space.props,
cache = space.cache,
to = space.to,
from = space.from;
// makes rgba() and hsla()
color.fn[ spaceName ] = function( value ) {
// generate a cache for this space if it doesn't exist
if ( to && !this[ cache ] ) {
this[ cache ] = to( this._rgba );
}
if ( value === undefined ) {
return this[ cache ].slice();
}
var ret,
type = jQuery.type( value ),
arr = ( type === "array" || type === "object" ) ? value : arguments,
local = this[ cache ].slice();
each( props, function( key, prop ) {
var val = arr[ type === "object" ? key : prop.idx ];
if ( val == null ) {
val = local[ prop.idx ];
}
local[ prop.idx ] = clamp( val, prop );
});
if ( from ) {
ret = color( from( local ) );
ret[ cache ] = local;
return ret;
} else {
return color( local );
}
};
// makes red() green() blue() alpha() hue() saturation() lightness()
each( props, function( key, prop ) {
// alpha is included in more than one space
if ( color.fn[ key ] ) {
return;
}
color.fn[ key ] = function( value ) {
var vtype = jQuery.type( value ),
fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
local = this[ fn ](),
cur = local[ prop.idx ],
match;
if ( vtype === "undefined" ) {
return cur;
}
if ( vtype === "function" ) {
value = value.call( this, cur );
vtype = jQuery.type( value );
}
if ( value == null && prop.empty ) {
return this;
}
if ( vtype === "string" ) {
match = rplusequals.exec( value );
if ( match ) {
value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
}
}
local[ prop.idx ] = value;
return this[ fn ]( local );
};
});
});
// add cssHook and .fx.step function for each named hook.
// accept a space separated string of properties
color.hook = function( hook ) {
var hooks = hook.split( " " );
each( hooks, function( i, hook ) {
jQuery.cssHooks[ hook ] = {
set: function( elem, value ) {
var parsed, curElem,
backgroundColor = "";
if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
value = color( parsed || value );
if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
curElem = hook === "backgroundColor" ? elem.parentNode : elem;
while (
(backgroundColor === "" || backgroundColor === "transparent") &&
curElem && curElem.style
) {
try {
backgroundColor = jQuery.css( curElem, "backgroundColor" );
curElem = curElem.parentNode;
} catch ( e ) {
}
}
value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
backgroundColor :
"_default" );
}
value = value.toRgbaString();
}
try {
elem.style[ hook ] = value;
} catch( e ) {
// wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
}
}
};
jQuery.fx.step[ hook ] = function( fx ) {
if ( !fx.colorInit ) {
fx.start = color( fx.elem, hook );
fx.end = color( fx.end );
fx.colorInit = true;
}
jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
};
});
};
color.hook( stepHooks );
jQuery.cssHooks.borderColor = {
expand: function( value ) {
var expanded = {};
each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
expanded[ "border" + part + "Color" ] = value;
});
return expanded;
}
};
// Basic color names only.
// Usage of any of the other color names requires adding yourself or including
// jquery.color.svg-names.js.
colors = jQuery.Color.names = {
// 4.1. Basic color keywords
aqua: "#00ffff",
black: "#000000",
blue: "#0000ff",
fuchsia: "#ff00ff",
gray: "#808080",
green: "#008000",
lime: "#00ff00",
maroon: "#800000",
navy: "#000080",
olive: "#808000",
purple: "#800080",
red: "#ff0000",
silver: "#c0c0c0",
teal: "#008080",
white: "#ffffff",
yellow: "#ffff00",
// 4.2.3. "transparent" color keyword
transparent: [ null, null, null, 0 ],
_default: "#ffffff"
};
})( jQuery );
html, body {
margin: 0;
min-width:900px;
width: 100%;
height: 100%;
padding: 0;
}
body {
font-family: Ubuntu, Tahoma, Helvetica, sans-serif;
font-size: 12px;
line-height: 1.4em;
background: #f7f7f7;
color: #404040;
}
body.dark {
background: #131313;
color: #e3e3e3;
}
a {
text-decoration: none;
}
.dark a {
color: #5ae;
}
#wrap {
padding: 0 3.5%;
}
svg {
font-family: Ubuntu, Tahoma, Helvetica, sans-serif;
}
canvas, svg {
position: absolute;
top: 0;
left: 0;
}
#chart {
position: relative;
}
.brush rect.extent {
fill: rgba(100,100,100,0.15);
stroke: #fff;
}
.dark .brush rect.extent {
fill: rgba(100,100,100,0.15);
stroke: #ddd;
}
.brush:hover rect.extent {
stroke: #222;
stroke-dasharray: 5,5;
}
.brush rect.extent:hover {
stroke-dasharray: none;
}
.resize rect {
fill: none;
}
.background {
fill: none;
}
.dark .background {
fill: none;
}
.axis line, .axis path {
fill: none;
stroke: #777;
stroke-width: 1;
}
.dark .axis line, .dark .axis path {
stroke: #777;
}
.axis .tick {
width: 200px;
}
.axis text {
fill: #111;
text-anchor: left;
font-size: 10px;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
.axis text.label {
font-size: 18px;
padding-bottom: 8px;
}
.axis text.label-average {
font-size: 12px;
padding-bottom: 8px;
}
.axis g {
display: none;
}
.axis g text {
pointer-events: none;
}
.dark .axis text {
fill: #f2f2f2;
text-shadow: 0 1px 0 #000, 1px 0 0 #000, 0 -1px 0 #000, -1px 0 0 #000;
}
.dark .axis text.label {
fill: #ddd;
}
.quarter, .third, .half {
float: left;
}
.quarter {
width: 23%;
margin: 0 1%;
}
.third {
width: 31.3%;
margin: 0 1%;
}
.half {
width: 48%;
margin: 0 1%;
}
h3 {
margin: 12px 0 9px;
}
h3 small {
color: #888;
font-weight: normal;
}
p {
margin: 0.6em 0;
}
small {
line-height: 1.2em;
}
button[disabled=disabled] {
color: #555 !important;
opacity: 0.4;
}
#header .data-size button[disabled=disabled] {
display: none;
}
button[disabled=disabled] {
display: none;
}
.dark button[disabled=disabled] {
opacity: 0.2;
}
button#keep-data,
button#exclude-data {
font-weight:bold
}
button#keep-data:hover {
color: #080;
}
button#exclude-data:hover {
color: #900;
}
#food-list {
width: 100%;
height: 420px;
overflow-x: auto;
overflow-y: auto;
white-space: nowrap;
}
#legend {
text-align: left;
overflow-y: auto;
border-left: 1px solid rgba(140,140,140,0.5);
}
.row {
cursor: pointer;
}
.row:hover,
.data-row:hover {
font-weight: bold;
}
.off {
color: #999;
}
.dark .off {
color: #555;
}
.dark #legend {
border-left: 1px solid #777;
}
.color-block, .color-bar {
display: inline-block;
height: 8px;
width: 8px;
margin: 1px 4px 1px 0px;
}
#rendered-bar,
#selected-bar {
width:0%;
font-weight: bold;
}
#rendered-bar {
background: #888;
border-right: 1px solid #666;
}
#selected-bar {
background: rgba(160,160,160,0.5);
border-right: 1px solid #999;
}
.fillbar {
height: 12px;
line-height: 12px;
border:1px solid rgba(120,120,120,0.5);
width: 120px;
display: inline-block;
}
.little-box {
width: 268px;
float: left;
}
.little-box p {
width: 250px;
}
#header {
border-bottom: 1px solid rgba(100,100,100,0.35);
background: #e2e2e2;
padding: 6px 24px 4px;
line-height: 24px;
}
.dark #header {
background: #040404;
color: #f3f3f3;
}
#header h1 {
display: inline-block;
margin: 0px 14px 0 0;
}
#header button {
vertical-align: top;
}
/* Scrollbars */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: #ddd;
border-radius: 12px;
}
::-webkit-scrollbar-thumb {
background: #b5b5b5;
border-radius: 12px;
}
.dark ::-webkit-scrollbar-track {
background: #222;
}
.dark ::-webkit-scrollbar-thumb {
background: #444;
}
#data-size {
margin-right:5%;
padding-top: 6px;
float: right;
height: 48ox;
line-height: 24px;
width: 250px;
text-align: right;
}
.header-top {
padding-left:5%;
}
#column-1, #column-2, #column-3, #column-4 {
margin-top:20 px;
}
#search {
width: 200px;
margin-left:10px;
}
#dark-theme, #light-theme {
height: 20px;
}
#credits {
color:#777;
width:80%;
margin-left:4%;
margin-top:120px;
padding-top:400px;
padding-bottom:15px
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment