/* * shamelessly adapted from World Tour, http://bl.ocks.org/mbostock/4183330 */ (function () { var crtKey = function (arr) { var key = ""; arr.forEach(function (str) { key += str.toLowerCase().replace(/[^a-z]/g, ""); }); return key; }; var list = { el: {}, selectBox : {}, listBox : {}, peeps : {}, init : function (opts) { var self = this; var template = opts.template; var select = document.createElement("select"); var ul = document.createElement("ul"); this.el = d3.select(opts.selector); opts.peeps.forEach(function (peep) { var peepKey = crtKey([peep.firstname, peep.lastname]); select.appendChild(list.crtOption(peep, peepKey)); ul.appendChild(list.crtLi(peep, peepKey, template)); list.peeps[peepKey] = peep; }); select.appendChild(this.crtOption("All", "all")); this.selectBox = d3.select(opts.selector + " .select-box"); this.selectBox.node().appendChild(select); this.listBox = d3.select(opts.selector + " .list-box"); this.listBox.node().appendChild(ul); this.el.selectAll("li img").on("click", function () { var id = this.parentElement.parentElement.id; var peep = list.peeps[id]; globe.rotateTo([ peep ], 0, 1); }); this.el.select("select").on("change", function () { var peepArr = []; var peepKeys = []; if ( this.value == "all" ) { peepKeys = Object.keys(self.peeps); peepKeys.forEach(function (peep) { peepArr.push(list.peeps[peep]) }); } else { peepArr = [ list.peeps[this.value] ]; } globe.rotateTo(peepArr, 0, peepArr.length); }); }, crtOption : function (peep, val) { var option = document.createElement("option"); option.value = val; option.textContent = peep == "All" ? peep : peep.firstname + " " + peep.lastname; return option; }, crtLi : function (peep, id, template) { var li = document.createElement("li"); li.id = id; var peepBox = document.querySelector(template).cloneNode(true); var peepName = peepBox.querySelector(".peep-name"); peepName.textContent = peep.firstname + " " + peep.lastname; var peepImg = peepBox.querySelector("img"); peepImg.src = peep.img; peepImg.alt = peep.firstname + ' ' + peep.lastname; var peepLink = peepBox.querySelector("a"); peepLink.href = "https://twitter.com/" + peep.twitter var peepHandle = peepBox.querySelector(".twitter"); peepHandle.textContent = "@" + peep.twitter; li.appendChild(peepBox); return li; }, transScroll : function (id) { var offsetTop = document.querySelector("#" + id).offsetTop; var scrollTween = function (t) { return function() { var terpRound = d3.interpolateRound(this.scrollTop, offsetTop); return function(t) { this.scrollTop = terpRound(t); }; }; }; this.selectBox.select("select").node().value = id; this.listBox.transition() .duration(1250) .tween("scrollTween", scrollTween(0)) } }; var globe = { el : {}, canvas : {}, ctx : {}, cities : {}, path : {}, slug : {}, init : function (opts) { var self = this; this.el = d3.select(opts.selector).select(".canvas-box"); this.globe = {type: "Sphere"}; this.land = topojson.feature(opts.world, opts.world.objects.land); this.countries = topojson.feature(opts.world, opts.world.objects.countries).features; this.borders = topojson.mesh(opts.world, opts.world.objects.countries, function(a, b) { return a !== b; }); this.xref = this.initXref(opts.peeps); this.slug = this.el.select(".slug"); this.initCanvas(); window.addEventListener("resize", function () { self.initCanvas() }); }, initXref : function (peeps) { var self = this; var xref = {}; var ids = []; var xrefKeys = []; var createGeoMarker = function (angles, lon, lat) { var marker = []; angles.forEach(function (angle) { marker.push( d3.geo.circle().origin([lon, lat]).angle(angle)() ); }); return marker; }; peeps.forEach(function(mbr) { var cityKey = crtKey([mbr.location.city, mbr.location.country]); var cityArr = []; if ( ids.indexOf(mbr.location.id) === -1 ) { ids.push(mbr.location.id); } if ( !self.cities[cityKey] ) { self.cities[cityKey] = createGeoMarker( [1, .3], mbr.location.lon, mbr.location.lat ); } }); this.countries.forEach(function(country, cx){ if ( ids.indexOf(country.id) > -1 ) { xrefKeys.push({id : country.id, cx : cx }); } }) peeps.forEach(function(mbr) { var nameKey = crtKey([mbr.firstname, mbr.lastname]); var cityKey = crtKey([mbr.location.city, mbr.location.country]); var id, cx; xrefKeys.forEach(function(uniq) { if ( uniq.id === mbr.location.id ) { id = uniq.id; cx = uniq.cx; } }); xref[nameKey] = { id : id, cx: cx, cityKey : cityKey }; }); return xref; }, initCanvas : function (selector) { var defaultScale = 248; var defaultHeight = 500; var elRect = this.el.node().getBoundingClientRect(); var scaleFactor = (Math.min(elRect.width, elRect.height) -30) / defaultHeight; this.slug.style("opacity", 0); this.width = elRect.width; this.height = elRect.height; this.el.select("canvas").remove(); this.canvas = this.el.append("canvas") .attr("width", this.width) .attr("height", this.height); this.ctx = this.canvas.node().getContext("2d"); this.center = { x: elRect.width / 2, y: elRect.height / 2 } this.projection = d3.geo.orthographic() .translate([this.center.x, this.center.y]) .scale(scaleFactor * defaultScale) .clipAngle(90); this.path = d3.geo.path() .projection(this.projection) .context(this.ctx); this.slug.style("top", ( elRect.height * .75 ) + "px"); this.draw({}); }, draw : function (opts) { this.ctx.clearRect(0, 0, this.width, this.height); this.ctx.globalAlpha = 0.2; this.ctx.strokeStyle = "#fff", this.ctx.lineWidth = 4, this.ctx.beginPath(), this.path(this.globe), this.ctx.stroke(); this.ctx.globalAlpha = 1; this.ctx.fillStyle = "#62a4ea", this.ctx.beginPath(), this.path(this.globe), this.ctx.fill(); this.ctx.fillStyle = "#339633", this.ctx.beginPath(), this.path(this.land), this.ctx.fill(); if (opts.country) { this.ctx.fillStyle = "#246b1d", this.ctx.beginPath(), this.path(opts.country), this.ctx.fill(); } this.ctx.strokeStyle = "#246b1d", this.ctx.lineWidth = .5, this.ctx.beginPath(), this.path(this.borders), this.ctx.stroke(); if (opts.city) { this.ctx.fillStyle = "#246b1d", this.ctx.beginPath(), this.path(opts.city[0]), this.ctx.fill(); this.ctx.strokeStyle = "#fff", this.ctx.lineWidth = .8, this.ctx.beginPath(), this.path(opts.city[0]), this.ctx.stroke(); this.ctx.fillStyle = "#fff", this.ctx.beginPath(), this.path(opts.city[1]), this.ctx.fill(); } }, setSlug : function (peep) { var self = this; this.slug.transition() .duration(500) .style("opacity", 0) .each('end', function () { self.slug.select(".slug-img") .style("background-image", "url(" + peep.img + ")"); self.slug.select(".name") .text(peep.firstname + " " + peep.lastname + " - "); self.slug.select("a") .attr("href", "https://twitter.com/" + peep.twitter) self.slug.select(".twitter") .text("@" + peep.twitter); self.slug.select('.city') .text(peep.location.city + ", "); self.slug.select('.loc').text( function () { return peep.location.state ? peep.location.state + ", " + peep.location.country : peep.location.country; }); }) .transition() .duration(1250) .style("opacity", 1); }, rotateTo : function (peeps, px, pLen) { var self = this; var peep = peeps[px]; var nameKey = crtKey([peep.firstname, peep.lastname]); var cityKey = crtKey([peep.location.city, peep.location.country]); var opts = { country : this.countries[this.xref[nameKey].cx], city : this.cities[cityKey] }; list.transScroll(nameKey); this.setSlug(peep); this.draw(opts); d3.transition() .delay(500) .duration(1250) .each("start", function() { self.terp = d3.interpolate(self.projection.rotate(), [-peep.location.lon, -peep.location.lat]); }) .tween("rotate", function() { return function(t) { self.projection.rotate(self.terp(t)); self.draw(opts); }; }) .transition() .each("end", function () { px += 1; if (px < pLen) { self.rotateTo(peeps, px, pLen); } else { return; } }); } }; var loaded = function (error, peeps, world) { var listOpts = { selector : ".peeps-box", template : "#templates .peep-box", peeps : peeps }; var globeOpts = { peeps : peeps, selector : ".globe-box", world : world }; list.init(listOpts); globe.init(globeOpts); }; window.addEventListener('DOMContentLoaded', function () { queue() .defer(d3.json, "peeps.json") .defer(d3.json, "world-110m.json") .await(loaded); }); }());