273 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			273 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|   | $(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 + | ||
|  |             "<br/>$" + | ||
|  |             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; | ||
|  |       }); | ||
|  |   }); | ||
|  | }); |