Skip to content

Instantly share code, notes, and snippets.

@rickdg
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rickdg/67ae348c3f2a3d401543 to your computer and use it in GitHub Desktop.
Save rickdg/67ae348c3f2a3d401543 to your computer and use it in GitHub Desktop.
Pimp my Tribe (update3 - redux)

[ Launch: Pimp my Tribe (update3 - redux) ] 67ae348c3f2a3d401543 by rickdg
[ Launch: Pimp my Tribe (update2) ] 4603089 by zeffii
[ Launch: Pimp my Tribe (update2) ] 4596906 by enjalot
[ Launch: Pimp my Tribe ] 4594558 by zeffii
[ Launch: Pimp my Tribe ] 4585252 by zeffii
[ Launch: Pimp my Tribe ] 4573091 by zeffii
[ Launch: Pimp my Tribe ] 4572416 by zeffii
[ Launch: Pimp my Tribe ] 4571945 by zeffii
[ Launch: Pimp my Tribe ] 4568998 by enjalot
[ Launch: Pimp my Tribe ] 4567995 by zeffii
[ Launch: Pimp my Tribe ] 4564213 by zeffii
[ Launch: Pimp my Tribe ] 4558845 by enjalot
[ Launch: Pimp my Tribe ] 4558042 by zeffii
[ Launch: Tributary Inlets ] 4552099 by zeffii
[ Launch: Tributary Inlets ] 4551425 by zeffii
[ Launch: Tributary Inlets ] 4549657 by enjalot
[ Launch: An inlet to Tributary ] 4545400 by enjalot

{"description":"Pimp my Tribe (update3 - redux)","endpoint":"","display":"html","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"style.css":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"tab":"edit","display_percent":0.6597453544693251,"hidepanel":false,"fullscreen":false,"ajax-caching":true}
/* NOTES
In firefox text attribute "alignment-baseline" will fail.
Instead, to get this to look similar on a variety of
browsers i'd consider making y depend on text height of
(boxWidth / 2) + (lowercase n height / 2). Right now
this is a hardcoded value.
- marker & usernames are links
- marker mouseover shows inlet title
- username mouseover shows user stats
*/
/* HELPER FUNCTIONS + CONSTANTS */
function translate(value_x, value_y){
return "translate(" + [value_x, value_y] + ")"
}
function rotate(amount){
return "rotate(" + amount + ")"
}
var users_url = 'http://tributary.io/api/users';
var created_url = 'http://tributary.io/api/latest/created';
var trib_inlet = 'http://tributary.io/inlet/';
var iso = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ").parse;
/* AXIS SETUP */
var margin = {top: 76, right: 130, bottom: 2, left: 45},
extent_width = tributary.sw -20,
width = extent_width - margin.left - margin.right,
height = 645 - margin.top - margin.bottom;
var boxWidth = 12,
boxHeight = 12;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]);
var yScale = d3.scale.ordinal()
.rangeBands([0, height], 0)
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(7)
.tickSubdivide(1)
.tickSize(11, 4, 5);
var xAxis2 = d3.svg.axis()
.scale(x2)
.orient("bottom")
.ticks(7)
.tickSubdivide(2)
.tickSize(11, 4, 5);
/* SVG SETUP */
var display = d3.select("#display")
var button = display.append("input")
.attr({
id: "fetch",
type: "button",
value: "fetch"
})
var svg = display.append("svg")
.attr({
"xmlns": "http://www.w3.org/2000/svg",
"xlink": "http://www.w3.org/1999/xlink",
"class": "tributary_svg"
})
//initialize chart containers
var userBars = svg.append("g")
.classed("userBars", true)
.attr("transform", translate(0, margin.top))
svg.append("g")
.classed("usernames", true)
.attr("transform", translate(width, margin.top))
var box = svg.append("g")
.classed("funky", true)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", translate(margin.left, margin.top));
box.append("g").attr({
"class": "x axis",
"transform": translate(0, height)
})
box.append("g").attr({
"class": "x2 axis",
"transform": translate(0, -37)
})
// bush setup (slider)
var xscale = d3.scale.linear()
.domain([0, 1])
.range([0, width]);
var brush = d3.svg.brush()
.x(xscale);
var slider = svg.append("g").attr("transform", translate(margin.left,0))
brush.extent([0.6144, 1]);
brush(slider);
slider.selectAll("rect.background")
.attr("height", 32);
slider.selectAll("g.resize rect")
.attr("height", 32);
slider.selectAll(".extent")
.attr("height", 32);
var posx = brush.extent();
// theme
var marker_style = {
"fill": "#49BDFF",
"fill-opacity": 0.33,
"stroke": "none"
}
var bar_style = {
"fill": "#FFFFFF",
"fill-opacity": 0.74681
}
var text_style = {
"font-size": 1.3 + "em",
"font-family": 'Roboto', // sans-serif',
"font-weight": 300
}
//font
WebFontConfig = {
google: { families: [ 'Roboto:400,100:latin' ] }
};
(function() {
var wf = document.createElement('script');
var dlp = document.location.protocol;
wf.src = ('https:' == dlp ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();
/* GET USERS */
if(!tributary.users) {
d3.json(users_url, function(err, res) {
tributary.users = {};
res.forEach(function(u) {
tributary.users[u.login] = u;
})
render();
})
}
button.on("click", function() {
d3.json(created_url, function(err, res) {
tributary.inlets = res;
render();
})
})
/* DATA PRUNING FUNCTIONS */
function make_user_obj(){
var inlets = tributary.inlets
var user_list = tributary.users;
var user_strata = new Object();
// convert the userlist to a strata index
var counter = 0;
function populateStrata(name){
user_strata[name] = counter
counter += 1
}
user_list = d3.keys(user_list);
user_list.forEach(populateStrata);
return {
user_list: user_list,
user_strata: user_strata
}
}
function get_domain(){
return {
newest: tributary.inlets.slice(-1)[0].createdAt,
oldest: tributary.inlets.slice(0, 1)[0].createdAt
}
}
function filter_time(cutoff){
// pick time cutoff
var time_domain = get_domain(),
time_start = iso(time_domain.newest),
time_end = iso(time_domain.oldest);
var transit = d3.interpolate(time_start, time_end)(cutoff);
return new Date(transit);
}
function between_cutoffs(d, cutoff_0, cutoff_1){
var ctime = d.createdAt;
return (ctime >= cutoff_0) && (ctime <= cutoff_1)
}
function cherry_pick(){
var data_reduced = [];
var cutoff_0 = filter_time(posx[0]);
var cutoff_1 = filter_time(posx[1]);
// use the map iterator to populate data_reduced
var data = _.map(tributary.inlets, function(d){
var user_datum = {
createdAt: iso(d.createdAt),
t_user: d.user.login,
description: d.description || "no description",
gist_id: d.gistid
}
if (between_cutoffs(user_datum, cutoff_0, cutoff_1)) {
data_reduced.push(user_datum)
}
return user_datum
});
return {data: data, reduced: data_reduced}
}
function compose_stats(d){
var stat = tributary.users[d];
return "visits: " + (stat.visits || 0) +
" forks: " + (stat.nforks || 0) +
" inlets: " + (stat.inlets || 0)
}
/* MAIN DRAW FUNCTION */
function render() {
if(!tributary.inlets) return;
var user_object = make_user_obj();
var cp = cherry_pick();
var data = cp.data;
var data_reduced = cp.reduced;
x.domain(d3.extent(data_reduced, function(d) { return d.createdAt } ));
x2.domain(d3.extent(data, function(d) { return d.createdAt } ));
yScale.domain(user_object.user_list)
svg.select("g.x.axis")
.call(xAxis)
.style(text_style)
svg.select("g.x2.axis")
.call(xAxis2)
.style(text_style)
var ubSel = userBars
.selectAll("rect.userbar")
.data(user_object.user_list)
ubSel
.enter()
.append("rect")
.classed("userbar", true)
ubSel
.attr({
"y": function(d) { return yScale(d) - boxHeight/2 - 1 },
"x": margin.left - boxWidth/2,
"width": width + margin.left + margin.right + 90,
"height": boxHeight + 2
})
.style(bar_style)
var markers = svg.select("g.funky")
.selectAll("a")
.data(data_reduced);
markers
.enter()
.append("a")
.attr({
"xlink:href": function(d){ return trib_inlet + d.gist_id },
"xlink:title": function(d){ return d.description },
"xlink:show": "new",
"cursor": "pointer"
})
.append("rect")
markers.select("rect")
.attr({
"transform": function(d, i) {
var y_loc = yScale(d.t_user) - boxHeight/2;
return translate(x(d.createdAt)- boxWidth/2, y_loc)
},
"width": boxWidth,
"height": boxHeight
})
.style(marker_style)
var userNames = svg.select("g.usernames")
.selectAll("g.username")
.data(user_object.user_list);
userNames
.enter()
.append("g")
.classed("username", true)
.append("a")
.attr({
"xlink:href": function(d){
return tributary.users[d].html_url
},
"xlink:title": function(d){
return compose_stats(d)
},
"xlink:show": "new",
"cursor": "pointer"
})
.append("text")
userNames.select("text")
.text(function(d) { return d })
.attr({
"y": function(d) { return yScale(d) + 3.864 },
"x": -margin.left + margin.right - 25
})
.style(text_style)
};
render();
if(!tributary.inlets || !tributary.inlets.length) {
d3.json(created_url, function(err, res) {
tributary.inlets = res;
render();
})
}
function update_content(){
posx = brush.extent();
render();
}
brush.on("brushstart", function(){ update_content() })
brush.on("brush", function(){ update_content() })
brush.on("brushend", function(){ update_content() })
/* EOF */
#fetch {
position:relative;
margin-top: 20px;
left: 10px;
font-size:1.7em;
padding: 3px 10px 3px 10px;
float:left;
}
.inlet {
margin-left: 10px;
margin-top: 20px;
}
.inlet img {
margin-left: 10px;
//margin-top: 5px;
}
.time {
font-size: 0.8em;
margin-left: 10px;
}
svg {
font: 9px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #919191;
shape-rendering: crispEdges;
}
#panel #file-list {overflow-x: hidden;}
/*
.x.axis path {
display: none;
}
*/
.background{
visibility: visible !important;
fill: #FFFFFF;
}
.extent {
visibility: visible !important;
fill: #84D8F8;
}
.resize rect {
visibility: visible !important;
fill: #888888;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment