From ec0776caf9aad677647322ce5c387e5254b98951 Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Fri, 13 Apr 2012 01:29:47 -0700 Subject: [PATCH 01/15] Fix the weird race condition that caused the visualization to be drawn twice. --- commits.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commits.js b/commits.js index c9c7b4d..cbbc65b 100644 --- a/commits.js +++ b/commits.js @@ -38,7 +38,7 @@ svg.append("rect") $.getJSON("https://api.github.com/orgs/" + orgName + "/repos?callback=?", function(response) { var allCommits = []; var timelines = []; - $.each(response.data, function(i, repo) { + response.data.forEach(function(repo, i, array) { function gitSource(repo) { return repo.url + "/commits?callback=?"; } @@ -61,7 +61,7 @@ $.getJSON("https://api.github.com/orgs/" + orgName + "/repos?callback=?", functi }; allCommits.push.apply(allCommits, commits); timelines.push(timeline); - if (timelines.length == response.data.length) drawChart(allCommits, timelines); + if (timelines.length == array.length) drawChart(allCommits, timelines); }); }); }); From e67b992801111d56e9530b6ff2442268b67162b8 Mon Sep 17 00:00:00 2001 From: mtye Date: Thu, 12 Apr 2012 17:26:00 -0700 Subject: [PATCH 02/15] Replace link to bl.ocks.org example with link to GitHub page --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7ead218..761f447 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ This project is an experiment in visualizing code development using the amazing It was conceived at the 2012 Java Posse [Roundup][roundup], an amazing gathering of talented developers from around the world. The developers at the Roundup used [github][github] to collaborate on many different interesting projects - too many for any one person to fully participate in. -[I][githubmtye] was consequently inspired to see if I could use the d3 JavaScript library to create a [visualization][visarticle] of all the work the Roundup developers had accompished (or at least all the work they'd committed to [github][githubjpr]). This is the result. +[I][githubmtye] was consequently inspired to see if I could use the d3 JavaScript library to create a [visualization][visarticle] of all the work the Roundup developers had accomplished (or at least all the work they'd committed to [github][githubjpr]). This project is the result. + +More information is available at the d3github [project page][d3githubpage]. [d3home]: http://mbostock.github.com/d3/ "d3 at github" [githubapi]: http://developer.github.com/v3/ @@ -13,6 +15,7 @@ It was conceived at the 2012 Java Posse [Roundup][roundup], an amazing gathering [githubmtye]: https://github.com/mtye [visarticle]: http://en.wikipedia.org/wiki/Information_visualization [githubjpr]: https://github.com/JavaPosseRoundup "Java Posse Roundup at github" +[d3githubpage]: http://javaposseroundup.github.com/d3github/ ## Installation and Use @@ -20,10 +23,7 @@ To use this project, simple clone this github repository and open the ```commits ## Examples -To see the d3github visualizer in use without cloning/downloading the project, look at this [example][example] on [bl.ocks.org][blocksorg]. - -[example]: http://bl.ocks.org/2356163 -[blocksorg]: http://bl.ocks.org +A live example of the d3github visualizer can be found on the d3github [project page][d3githubpage]. ## Compatibility From 551323b9a8a9f1ce0ede058a5a545a13c09a6bd5 Mon Sep 17 00:00:00 2001 From: mtye Date: Mon, 25 Jun 2012 18:55:41 -0700 Subject: [PATCH 03/15] Replace hard-coded values with attributes on the div that contains the SVG element --- commits.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/commits.js b/commits.js index cbbc65b..1b6be06 100644 --- a/commits.js +++ b/commits.js @@ -1,10 +1,14 @@ -var orgName = "JavaPosseRoundup"; -var roundupStart = new Date(2012, 2, 25); +// Find the element where the commit timeline should be drawn +var commits = d3.select("#commits"); + +var orgName = commits.attr("orgName") || "JavaPosseRoundup"; +var startDate = commits.attr("startDate") || ""; +var width = commits.attr("width") || 1200; +var height = commits.attr("height") || 600; var margin = {top: 20, right: 0, bottom: 50, left: 180}, - w = 1200 - margin.left - margin.right, - h = 600 - margin.top - margin.bottom, - tickHeight = 10; + w = width - margin.left - margin.right, + h = height - margin.top - margin.bottom; // Scales. Note the inverted domain for the y-scale: bigger is up! var x = d3.time.scale().rangeRound([0, w]), @@ -16,7 +20,7 @@ var xAxis = d3.svg.axis().scale(x).tickSubdivide(true); var yAxis = d3.svg.axis().scale(y).tickSize(0).tickPadding(5).orient("left"); // Add an SVG element with the desired dimensions and margin. -var svg = d3.select("#chart").append("svg") +var svg = commits.append("svg") .attr("width", w + margin.left + margin.right) .attr("height", h + margin.top + margin.bottom) .append("g") @@ -68,7 +72,13 @@ $.getJSON("https://api.github.com/orgs/" + orgName + "/repos?callback=?", functi function drawChart(allCommits, timelines) { - x.domain([roundupStart, d3.time.day.ceil(new Date())]); + var tickHeight = height / (timelines.length * 4 + 1); + + var earliestCommitDate = d3.min(timelines, function(d) { return d.earliest; }); + + var start = d3.time.format("%Y-%m-%d").parse(startDate) || earliestCommitDate; + + x.domain([d3.time.day.floor(start), d3.time.day.ceil(new Date())]); y.domain(timelines.map(function(t) { return t.repo; })); From 751f43e6419c179bf3887c2d8f1e99aa3046c1e6 Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Tue, 26 Jun 2012 10:19:25 -0700 Subject: [PATCH 04/15] Replace hard-coded margin values with attributes on the SVG element --- commits.html | 2 +- commits.js | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/commits.html b/commits.html index c2c6206..cb38224 100644 --- a/commits.html +++ b/commits.html @@ -41,7 +41,7 @@
Javaposse Roundup GitHub Repositories
-
+
Mouseover a tick mark on a timeline to see commit info
Scroll to zoom in or out
Drag to pan left or right
diff --git a/commits.js b/commits.js index 1b6be06..e26326c 100644 --- a/commits.js +++ b/commits.js @@ -5,10 +5,13 @@ var orgName = commits.attr("orgName") || "JavaPosseRoundup"; var startDate = commits.attr("startDate") || ""; var width = commits.attr("width") || 1200; var height = commits.attr("height") || 600; +var leftMargin = commits.attr("leftMargin") || 180; +var rightMargin = commits.attr("rightMargin") || 0; +var topMargin = commits.attr("topMargin") || 20; +var bottomMargin = commits.attr("bottomMargin") || 50; -var margin = {top: 20, right: 0, bottom: 50, left: 180}, - w = width - margin.left - margin.right, - h = height - margin.top - margin.bottom; +var w = width - leftMargin - rightMargin, + h = height - topMargin - bottomMargin; // Scales. Note the inverted domain for the y-scale: bigger is up! var x = d3.time.scale().rangeRound([0, w]), @@ -21,10 +24,10 @@ var yAxis = d3.svg.axis().scale(y).tickSize(0).tickPadding(5).orient("left"); // Add an SVG element with the desired dimensions and margin. var svg = commits.append("svg") - .attr("width", w + margin.left + margin.right) - .attr("height", h + margin.top + margin.bottom) + .attr("width", w + leftMargin + rightMargin) + .attr("height", h + topMargin + bottomMargin) .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + .attr("transform", "translate(" + leftMargin + "," + topMargin + ")"); // Add the clip path. svg.append("clipPath") From 2e95492a9db17c94d9839b1ab238e798f1787b9e Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sat, 30 Jun 2012 17:14:00 -0700 Subject: [PATCH 05/15] Move CSS into a separate file --- commits.css | 33 +++++++++++++++++++++++++++++++++ commits.html | 36 +----------------------------------- 2 files changed, 34 insertions(+), 35 deletions(-) create mode 100644 commits.css diff --git a/commits.css b/commits.css new file mode 100644 index 0000000..39d83d9 --- /dev/null +++ b/commits.css @@ -0,0 +1,33 @@ +body { + font: 14px sans-serif; + margin: 0; +} +.title { + font-size: 24px; + margin: 25px 0px 5px 200px; +} +.commit { + stroke: black; + stroke-width: 2px; +} +.timeline { + stroke: #444; + stroke-width: 1px; +} +.axis { + shape-rendering: crispEdges; +} +.x.axis .minor { + stroke-opacity: .5; +} +.y.axis path { + stroke: #fff; +} +.axis line, .axis path { + fill: none; + stroke: #000; +} +.help { + font-size: 16px; + margin: 0px 0px 5px 200px; +} diff --git a/commits.html b/commits.html index cb38224..9609126 100644 --- a/commits.html +++ b/commits.html @@ -3,41 +3,7 @@ - +
Javaposse Roundup GitHub Repositories
From 179cd213853e4c185ccb76dcd5baadaf14b2bf0c Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sat, 30 Jun 2012 17:15:20 -0700 Subject: [PATCH 06/15] Use CDN-hosted jQuery and d3 instead of local .js files --- commits.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commits.html b/commits.html index 9609126..2367ebc 100644 --- a/commits.html +++ b/commits.html @@ -1,8 +1,8 @@ - - + + From d545c58777759b68cae158ea2a56bedd14dc9b5c Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sat, 30 Jun 2012 18:27:57 -0700 Subject: [PATCH 07/15] Remove unnecessary recalculation of svg element width and height --- commits.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commits.js b/commits.js index e26326c..5385075 100644 --- a/commits.js +++ b/commits.js @@ -24,8 +24,8 @@ var yAxis = d3.svg.axis().scale(y).tickSize(0).tickPadding(5).orient("left"); // Add an SVG element with the desired dimensions and margin. var svg = commits.append("svg") - .attr("width", w + leftMargin + rightMargin) - .attr("height", h + topMargin + bottomMargin) + .attr("width", width) + .attr("height", height) .append("g") .attr("transform", "translate(" + leftMargin + "," + topMargin + ")"); From f7fbba7b9e96242153950f1ca8e152d075f44fb4 Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sat, 30 Jun 2012 18:36:41 -0700 Subject: [PATCH 08/15] Don't apply clip path to committer and commit message --- commits.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/commits.js b/commits.js index 5385075..fb8468d 100644 --- a/commits.js +++ b/commits.js @@ -141,7 +141,6 @@ function drawChart(allCommits, timelines) { committer.enter().append("text") .attr("class", "committer") - .attr("clip-path", "url(#clip)") .attr("x", x(d.commitDate)) .attr("y", y(d.repo)) .attr("dx", -2) @@ -157,7 +156,6 @@ function drawChart(allCommits, timelines) { message.enter().append("text") .attr("class", "message") - .attr("clip-path", "url(#clip)") .attr("x", x(d.commitDate)) .attr("y", y(d.repo)) .attr("dx", 2) From 81bb86fc8d9a9702052ed38fe27f5556af4ae42d Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sat, 30 Jun 2012 18:39:04 -0700 Subject: [PATCH 09/15] Make the background, which is only there to catch mouse events, transparent --- commits.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commits.js b/commits.js index fb8468d..a8e461f 100644 --- a/commits.js +++ b/commits.js @@ -40,7 +40,7 @@ svg.append("clipPath") svg.append("rect") .attr("width", w) .attr("height", h) - .style("fill", "#fff"); + .style("fill-opacity", ".0"); $.getJSON("https://api.github.com/orgs/" + orgName + "/repos?callback=?", function(response) { var allCommits = []; From bb0a180d87980f536624431da96d0e31ce117931 Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sat, 30 Jun 2012 18:57:48 -0700 Subject: [PATCH 10/15] Position x-axis so that there's enough space between it and and the bottommost timeline to display the commit message --- commits.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commits.js b/commits.js index a8e461f..9f37c32 100644 --- a/commits.js +++ b/commits.js @@ -88,7 +88,7 @@ function drawChart(allCommits, timelines) { // Add the x-axis. svg.append("g") .attr("class", "x axis") - .attr("transform", "translate(0," + (h + 20) + ")") + .attr("transform", "translate(0," + (h + topMargin + tickHeight) + ")") .call(xAxis); // Add the y-axis. From e6a2713e02916a58e58a135dd472fc9b1d602633 Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sat, 30 Jun 2012 19:01:23 -0700 Subject: [PATCH 11/15] Rename margin attributes --- commits.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commits.js b/commits.js index 9f37c32..0ca088f 100644 --- a/commits.js +++ b/commits.js @@ -5,10 +5,10 @@ var orgName = commits.attr("orgName") || "JavaPosseRoundup"; var startDate = commits.attr("startDate") || ""; var width = commits.attr("width") || 1200; var height = commits.attr("height") || 600; -var leftMargin = commits.attr("leftMargin") || 180; -var rightMargin = commits.attr("rightMargin") || 0; -var topMargin = commits.attr("topMargin") || 20; -var bottomMargin = commits.attr("bottomMargin") || 50; +var leftMargin = commits.attr("left-margin") || 180; +var rightMargin = commits.attr("right-margin") || 0; +var topMargin = commits.attr("top-margin") || 20; +var bottomMargin = commits.attr("bottom-margin") || 60; var w = width - leftMargin - rightMargin, h = height - topMargin - bottomMargin; From c61671786e8c8a37d079a3334d5745f74b112382 Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sat, 30 Jun 2012 19:04:04 -0700 Subject: [PATCH 12/15] Set default height and width of svg element equal to that of the document, minus some padding --- commits.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/commits.js b/commits.js index 0ca088f..1d530f1 100644 --- a/commits.js +++ b/commits.js @@ -3,8 +3,10 @@ var commits = d3.select("#commits"); var orgName = commits.attr("orgName") || "JavaPosseRoundup"; var startDate = commits.attr("startDate") || ""; -var width = commits.attr("width") || 1200; -var height = commits.attr("height") || 600; +var leftRightPadding = commits.attr("left-right-padding") || 20; +var topBottomPadding = commits.attr("top-bottom-padding") || 150; +var width = commits.attr("width") || $(document).width() - leftRightPadding; +var height = commits.attr("height") || $(window).height() - topBottomPadding; var leftMargin = commits.attr("left-margin") || 180; var rightMargin = commits.attr("right-margin") || 0; var topMargin = commits.attr("top-margin") || 20; @@ -34,7 +36,7 @@ svg.append("clipPath") .attr("id", "clip") .append("rect") .attr("width", w) - .attr("height", h + 20); + .attr("height", h); // Need something in the background absorb the mouse events! svg.append("rect") From 317a545847ed2ed10b92c6d0249d0de4cd654355 Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sun, 1 Jul 2012 11:41:53 -0700 Subject: [PATCH 13/15] Update README text and links to reflect new D3 homepage and use of CDN-hosted libraries --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 761f447..11f7c9c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Introduction -This project is an experiment in visualizing code development using the amazing [d3][d3home] JavaScript library and the github REST [api][githubapi]. +This project is an experiment in visualizing code development using the amazing [D3][d3home] JavaScript library and the github REST [API][githubapi]. It was conceived at the 2012 Java Posse [Roundup][roundup], an amazing gathering of talented developers from around the world. The developers at the Roundup used [github][github] to collaborate on many different interesting projects - too many for any one person to fully participate in. -[I][githubmtye] was consequently inspired to see if I could use the d3 JavaScript library to create a [visualization][visarticle] of all the work the Roundup developers had accomplished (or at least all the work they'd committed to [github][githubjpr]). This project is the result. +[I][githubmtye] was consequently inspired to see if I could use the D3 JavaScript library to create a [visualization][visarticle] of all the work the Roundup developers had accomplished (or at least all the work they'd committed to [github][githubjpr]). This project is the result. More information is available at the d3github [project page][d3githubpage]. -[d3home]: http://mbostock.github.com/d3/ "d3 at github" +[d3home]: http://d3js.org/ "D3 (Data-Driven Documents) website" [githubapi]: http://developer.github.com/v3/ [roundup]: http://www.mindviewinc.com/Conferences/JavaPosseRoundup/ [github]: https://github.com/ "Duh!" @@ -19,7 +19,7 @@ More information is available at the d3github [project page][d3githubpage]. ## Installation and Use -To use this project, simple clone this github repository and open the ```commits.html``` file in your web browser. The d3 library is available in the root directory, so there's no need to install it (or anything else). +To use this project, simple clone this github repository and open the ```commits.html``` file in your web browser. (The file references CDN-hosted versions of the D3 and jQuery libraries, so you'l need an Internet connection.) ## Examples @@ -27,4 +27,4 @@ A live example of the d3github visualizer can be found on the d3github [project ## Compatibility -The d3 library uses the CSS Selectors API Level 1 and SVG, which may not be supported in older browsers. This code in this project has been verified to work with Google Chrome (verison 17.0), Firefox (11.0), and Safari (5.1), but not with Internet Explorer. The minimum versions that _should_ work are Chrome 4.0, Firefox 3.5, Safari 3.2, and Internet Explorer 9.0. \ No newline at end of file +The D3 library uses the CSS Selectors API Level 1 and SVG, which may not be supported in older browsers. This code in this project has been verified to work with Google Chrome (verison 17.0), Firefox (11.0), and Safari (5.1), but not with Internet Explorer. The minimum versions that _should_ work are Chrome 4.0, Firefox 3.5, Safari 3.2, and Internet Explorer 9.0. \ No newline at end of file From edc61abbf6e383687b2a963417318b2f5ec6aa32 Mon Sep 17 00:00:00 2001 From: Mark Tye Date: Sun, 1 Jul 2012 11:53:43 -0700 Subject: [PATCH 14/15] Fix README typos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11f7c9c..9b77191 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ More information is available at the d3github [project page][d3githubpage]. ## Installation and Use -To use this project, simple clone this github repository and open the ```commits.html``` file in your web browser. (The file references CDN-hosted versions of the D3 and jQuery libraries, so you'l need an Internet connection.) +To use this project, simply clone this github repository and open the ```commits.html``` file in your web browser. (The file references CDN-hosted versions of the D3 and jQuery libraries, so you'll need an Internet connection.) ## Examples From 48dacdd53cc52e46040b7bb105fae6bc1810db11 Mon Sep 17 00:00:00 2001 From: Jordan Zimmerman Date: Fri, 1 Feb 2013 14:14:06 -0800 Subject: [PATCH 15/15] Break commits.js into two parts so that the main code can be used without a particular JSON request. --- commitsaction.js | 30 ++++++++++ commitsbase.js | 139 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 commitsaction.js create mode 100644 commitsbase.js diff --git a/commitsaction.js b/commitsaction.js new file mode 100644 index 0000000..5ee38c8 --- /dev/null +++ b/commitsaction.js @@ -0,0 +1,30 @@ +$.getJSON("https://api.github.com/orgs/" + orgName + "/repos?callback=?", function(response) { + var allCommits = []; + var timelines = []; + response.data.forEach(function(repo, i, array) { + function gitSource(repo) { + return repo.url + "/commits?callback=?"; + } + $.getJSON(gitSource(repo), function(response) { + var commits = response.data.map(function(r) { + return { + repo: repo.name, + message: r.commit.message, + authorDate: d3.time.format.iso.parse(r.commit.author.date), + committerEmail: r.commit.committer.email, + committerName: r.commit.committer.name, + commitDate: d3.time.format.iso.parse(r.commit.committer.date), + sha: r.sha + } + }); + var timeline = { + repo: repo.name, + earliest: commits[commits.length - 1].commitDate, + latest: commits[0].commitDate + }; + allCommits.push.apply(allCommits, commits); + timelines.push(timeline); + if (timelines.length == array.length) drawChart(allCommits, timelines); + }); + }); +}); diff --git a/commitsbase.js b/commitsbase.js new file mode 100644 index 0000000..47db2cb --- /dev/null +++ b/commitsbase.js @@ -0,0 +1,139 @@ +// Find the element where the commit timeline should be drawn +var commits = d3.select("#commits"); + +var orgName = commits.attr("orgName") || "JavaPosseRoundup"; +var startDate = commits.attr("startDate") || ""; +var leftRightPadding = commits.attr("left-right-padding") || 20; +var topBottomPadding = commits.attr("top-bottom-padding") || 150; +var width = commits.attr("width") || $(document).width() - leftRightPadding; +var height = commits.attr("height") || $(window).height() - topBottomPadding; +var leftMargin = commits.attr("left-margin") || 180; +var rightMargin = commits.attr("right-margin") || 0; +var topMargin = commits.attr("top-margin") || 20; +var bottomMargin = commits.attr("bottom-margin") || 60; + +var w = width - leftMargin - rightMargin, + h = height - topMargin - bottomMargin; + +// Scales. Note the inverted domain for the y-scale: bigger is up! +var x = d3.time.scale().rangeRound([0, w]), + y = d3.scale.ordinal().rangePoints([0, h], .5), + colors = d3.scale.category20(); + +// Axes. +var xAxis = d3.svg.axis().scale(x).tickSubdivide(true); +var yAxis = d3.svg.axis().scale(y).tickSize(0).tickPadding(5).orient("left"); + +// Add an SVG element with the desired dimensions and margin. +var svg = commits.append("svg") + .attr("width", width) + .attr("height", height) + .append("g") + .attr("transform", "translate(" + leftMargin + "," + topMargin + ")"); + +// Add the clip path. +svg.append("clipPath") + .attr("id", "clip") + .append("rect") + .attr("width", w) + .attr("height", h); + +// Need something in the background absorb the mouse events! +svg.append("rect") + .attr("width", w) + .attr("height", h) + .style("fill-opacity", ".0"); + +function drawChart(allCommits, timelines) { + + var tickHeight = height / (timelines.length * 4 + 1); + + var earliestCommitDate = d3.min(timelines, function(d) { return d.earliest; }); + + var start = d3.time.format("%Y-%m-%d").parse(startDate) || earliestCommitDate; + + x.domain([d3.time.day.floor(start), d3.time.day.ceil(new Date())]); + + y.domain(timelines.map(function(t) { return t.repo; })); + + // Add the x-axis. + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + (h + topMargin + tickHeight) + ")") + .call(xAxis); + + // Add the y-axis. + svg.append("g") + .attr("class", "y axis") + .call(yAxis); + + svg.selectAll(".commit") + .data(allCommits) + .enter().append("line") + .attr("class", "commit") + .attr("clip-path", "url(#clip)") + .attr("x1", function(d) { return x(d.commitDate); }) + .attr("y1", function(d) { return Math.floor(y(d.repo)) - tickHeight; }) + .attr("x2", function(d) { return x(d.commitDate); }) + .attr("y2", function(d) { return Math.floor(y(d.repo)) + tickHeight; }) + .style("stroke", function(d) { return colors(d.committerEmail); }) + .on("mouseover", showCommitInfo); + + svg.selectAll(".timeline") + .data(timelines) + .enter().append("line") + .attr("class", "timeline") + .attr("clip-path", "url(#clip)") + .attr("x1", function(d) { return x(d.earliest); }) + .attr("y1", function(d) { return Math.floor(y(d.repo)); }) + .attr("x2", function(d) { return x(d.latest); }) + .attr("y2", function(d) { return Math.floor(y(d.repo)); }); + + svg.call(d3.behavior.zoom().x(x).on("zoom", zoom)); + + function zoom() { + svg.select(".x.axis").call(xAxis); + svg.selectAll(".commit") + .data(allCommits) + .attr("x1", function(d) { return x(d.commitDate); }) + .attr("x2", function(d) { return x(d.commitDate); }); + svg.selectAll(".timeline") + .data(timelines) + .attr("x1", function(d) { return x(d.earliest); }) + .attr("x2", function(d) { return x(d.latest); }); + svg.selectAll(".committer, .message") + .attr("x", function(d) { return x(d.commitDate); }); + } + + function showCommitInfo(d, i) { + + var committer = svg.selectAll(".committer") + .data([d], function(d) { return d.sha; }); + + committer.enter().append("text") + .attr("class", "committer") + .attr("x", x(d.commitDate)) + .attr("y", y(d.repo)) + .attr("dx", -2) + .attr("dy", 2 * tickHeight + 5) + .attr("text-anchor", "end") + .style("fill", colors(d.committerEmail)) + .text(d.committerName); + + committer.exit().remove(); + + var message = svg.selectAll(".message") + .data([d], function(d) { return d.sha; }); + + message.enter().append("text") + .attr("class", "message") + .attr("x", x(d.commitDate)) + .attr("y", y(d.repo)) + .attr("dx", 2) + .attr("dy", 2 * tickHeight + 5) + .text(d.message); + + message.exit().remove(); + } + +}