$(document).ready(function () { var bootcamps = '' $.getJSON('/json/bootcamps.json', function(data) { bootcamps = data; }); var city = ""; $("body").data("state", "stacked"); $('#city-buttons').on("click", "button", function () { $(this).addClass('animated pulse'); city = $(this).attr("id"); $('#chosen').text('Coming from ' + city.replace(/-/g, ' ').replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }) + ', and making $_______, your true costs will be:'); setTimeout(function () { $('#city-buttons').hide(); $('#income').addClass('animated fadeIn').show(); }, 1000); }); $('#income').on("click", "button", function () { $(this).addClass('animated pulse'); setTimeout(function () { $('#income').hide(); $('#chart').addClass('animated fadeIn').show(); $('#chart-controls').addClass('animated fadeIn').show(); $('#explanation').addClass('animated fadeIn').show(); }, 1000); var lastYearsIncome = parseInt($(this).attr("id")); $('#chosen').text('Coming from ' + city.replace(/-/g, ' ').replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }) + ', and making $' + lastYearsIncome.toString().replace(/0000$/, '0,000') + ', your true costs will be:'); var categoryNames = ['Lost Wages', 'Financing Cost', 'Housing Cost', 'Tuition / Est. Wage Garnishing']; bootcamps.forEach(function (camp) { var x0 = 0; if (camp.cities.indexOf(city) > -1) { weeklyHousing = 0; } else { weeklyHousing = +camp.housing; } camp.mapping = [{ name: camp.name, label: 'Tuition / Est. Wage Garnishing', value: +camp.cost, x0: x0, x1: x0 += +camp.cost }, { name: camp.name, label: 'Financing Cost', value: +Math.floor(camp.cost * .09519), x0: +camp.cost, x1: camp.finance ? x0 += +Math.floor(camp.cost * .09519) : 0 }, { name: camp.name, label: 'Housing Cost', value: +weeklyHousing * camp.weeks, x0: camp.finance ? +Math.floor(camp.cost * 1.09519) : camp.cost, x1: x0 += weeklyHousing * camp.weeks }, { name: camp.name, label: 'Lost Wages', value: +(Math.floor(camp.weeks * lastYearsIncome / 50)), x0: camp.finance ? +(Math.floor(camp.cost * 1.09519) + weeklyHousing * camp.weeks) : +camp.cost + weeklyHousing * camp.weeks, x1: x0 += +(Math.floor(camp.weeks * lastYearsIncome / 50)) }]; camp.total = camp.mapping[camp.mapping.length - 1].x1; }); bootcamps.sort(function (a, b) { return a.total - b.total; }); maxValue = 0; bootcamps.forEach(function (camp) { camp.mapping.forEach(function (elem) { if (elem.value > maxValue) { maxValue = elem.value; } }); }); var xStackMax = d3.max(bootcamps, function (d) { return d.total; }), //Scale for Stacked xGroupMax = bootcamps.map(function (camp) { return camp.mapping.reduce(function (a, b) { return a.value > b.value ? a.value : b.value; }); }).reduce(function (a, b) { return a > b ? a : b; }); var margin = { top: 30, right: 60, bottom: 50, left: 140 }, width = 800 - margin.left - margin.right, height = 1200 - margin.top - margin.bottom; var barHeight = 20; var xScale = d3.scale.linear() .domain([0, xStackMax]) .rangeRound([0, width]); var y0Scale = d3.scale.ordinal() .domain(bootcamps.map(function (d) { return d.name; })) .rangeRoundBands([0, height], .1); var y1Scale = d3.scale.ordinal() .domain(categoryNames).rangeRoundBands([0, y0Scale.rangeBand()]); var color = d3.scale.ordinal() .range(["#215f1e", "#5f5c1e", "#1e215f", "#5c1e5f"]) .domain(categoryNames); var svg = d3.select("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var selection = svg.selectAll(".series") .data(bootcamps) .enter().append("g") .attr("class", "series") .attr("transform", function (d) { return "translate(0," + y0Scale(d.name) + ")"; }); var rect = selection.selectAll("rect") .data(function (d) { return d.mapping; }) .enter().append("rect") .attr("x", 0) .attr("width", 0) .attr("height", y0Scale.rangeBand()) .style("fill", function (d) { return color(d.label); }) .style("stroke", "white") .on("mouseover", function (d) { showPopover.call(this, d); }) .on("mouseout", function (d) { removePopovers(); }); rect.transition() .delay(function (d, i) { return i * 10; }) .attr("x", function (d) { return xScale(d.x0); }) .attr("width", function (d) { return xScale((d.x1) - (d.x0)); }); d3.selectAll("#transform").on("click", function () { $('#transform').addClass('animated pulse'); change(); setTimeout(function () { $('#transform').removeClass('animated pulse'); }, 1000); }); d3.selectAll("#chart").on("click", function () { change(); }); function change() { if ($("body").data("state") === "stacked") { transitionGrouped(); $("body").data("state", "grouped"); } else { transitionStacked(); $("body").data("state", "stacked"); } } function transitionGrouped() { xScale.domain = ([0, xGroupMax]); rect.transition() .duration(500) .delay(function (d, i) { return i * 10; }) .attr("width", function (d) { return xScale((d.x1) - (d.x0)); }) .transition() .attr("y", function (d) { return y1Scale(d.label); }) .attr("x", 0) .attr("height", y1Scale.rangeBand()); } function transitionStacked() { xScale.domain = ([0, xStackMax]); rect.transition() .duration(500) .delay(function (d, i) { return i * 10; }) .attr("x", function (d) { return xScale(d.x0); }) .transition() .attr("y", function (d) { return y0Scale(d.label); }) .attr("height", y0Scale.rangeBand()); } //axes var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y0Scale) .orient("left"); svg.append("g") .attr("class", "y axis") .call(yAxis); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .append("text") .attr("x", 300) .attr("y", 35) .attr("dy", ".35em") .style("text-anchor", "middle") .text("Cost in $USD"); //tooltips function removePopovers() { $('.popover').each(function () { $(this).remove(); }); } function showPopover(d) { $(this).popover({ title: d.name, placement: 'auto top', container: 'body', trigger: 'manual', html: true, content: function () { return d.label + "
$" + d3.format(",")(d.value ? d.value : d.x1 - d.x0); } }); $(this).popover('show'); } //legends var legend = svg.selectAll(".legend") .data(categoryNames.slice().reverse()) .enter().append("g") .attr("class", "legend") .attr("transform", function (d, i) { return "translate(30," + i * y0Scale.rangeBand() * 1.1 + ")"; }); legend.append("rect") .attr("x", width - y0Scale.rangeBand()) .attr("width", y0Scale.rangeBand()) .attr("height", y0Scale.rangeBand()) .style("fill", color) .style("stroke", "white"); legend.append("text") .attr("x", width - y0Scale.rangeBand() * 1.2) .attr("y", 12) .attr("dy", ".35em") .style("text-anchor", "end") .text(function (d) { return d; }); }); });