|
/* |
|
* 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); |
|
}); |
|
|
|
}()); |