More tweaks to the graphical timeline. This is getting there.

- Legacy-Id: 10554
This commit is contained in:
Lars Eggert 2015-12-09 15:38:33 +00:00
parent 82ad203627
commit b6e02b1c1f
2 changed files with 157 additions and 112 deletions

View file

@ -1,126 +1,176 @@
"use strict";
var data;
d3.json("doc.json", function(error, json) {
if (error) return console.warn(error);
data = json["rev_history"];
// make js dates out of publication dates
data.forEach(function(el) { el.published = new Date(el.published); });
// add pseudo entry for beginning of year of first publication
var year = data[0].published.getFullYear();
data.unshift({ name:'', rev: '', published: new Date(year, 0, 0)});
// add pseudo entry at end of year of last revision
year = data[data.length - 1].published.getFullYear();
data.push({ name:'', rev: '', published: new Date(year + 1, 0, 0)});
draw_timeline();
});
var xscale;
var y;
var x_scale;
var bar_y;
var bar_height;
var y_label_width;
var x_axis;
var width;
function offset(d, i) {
if (i > 1 && data[i - 1].name !== d.name || d.rev.match("rfc"))
y += bar_height;
return "translate(" + xscale(d.published) + ", " + y + ")";
// increase the y offset if the document name changed in this revision
if (i > 0 && data[i - 1].name !== d.name || d.rev.match("rfc"))
bar_y += bar_height;
return "translate(" + x_scale(d.published) + ", " + bar_y + ")";
}
function bar_width(d, i) {
if (i > 0 && i < data.length - 1)
return xscale(data[i + 1].published) - xscale(d.published);
if (i < data.length - 1)
return x_scale(data[i + 1].published) - x_scale(d.published);
}
function scale_x() {
width = $("#timeline").width();
// scale data to width of container minus y label width
x_scale = d3.time.scale().domain([
d3.min(data, function(d) { return d.published; }),
d3.max(data, function(d) { return d.published; })
]).range([y_label_width, width]);
x_axis = d3.svg.axis()
.scale(x_scale)
// don't add a tick for the pseudo entry
.tickValues(data.slice(0, -1).map(function(d) { return d.published; }))
.tickFormat(d3.time.format("%b %Y"))
.orient("bottom");
}
function update_x_axis() {
d3.select("#timeline svg .x.axis").call(x_axis)
.selectAll("text")
.style("text-anchor", "end")
.attr("transform", "translate(-14, 2) rotate(-60)");
}
function update_timeline() {
bar_y = 0;
scale_x();
var chart = d3.select("#timeline svg").attr("width", width);
var bar = chart.selectAll("g").data(data);
bar.attr("transform", offset).select("rect").attr("width", bar_width);
update_x_axis();
}
function draw_timeline() {
var w = $("#timeline").width();
// bar_height = parseFloat($("body").css('line-height'));
bar_height = 30;
bar_height = parseFloat($("body").css('line-height'));
// bar_height = 20;
xscale = d3.time.scale().domain([
d3.min(data, function(d) { return d.published; }),
d3.max(data, function(d) { return d.published; })
]).range([0, w]);
var div = $("#timeline");
if (div.is(":empty"))
div.append("<svg></svg>");
var chart = d3.select("#timeline svg").attr("width", width);
y = 0;
var chart = d3.select("#timeline svg").attr("width", w);
var bar = chart.selectAll("g").data(data);
var gradient = chart.append("defs")
.append("linearGradient")
.attr("id", "gradient");
gradient.append('stop')
.attr('class', 'stop-left')
.attr('offset', '0');
gradient.append('stop')
.attr('class', 'stop-right')
.attr('offset', '1');
// update
bar
.attr("transform", offset)
.select("rect")
.attr("width", bar_width);
var y_labels = data
.map(function(elem) { return elem.name; })
.filter(function(val, i, self) { return self.indexOf(val) === i; });
// enter
var g = bar.enter()
.append("g")
.attr({
class: "bar",
transform: offset
});
g.append("rect")
.attr({
height: bar_height,
width: bar_width
// calculate the width of the widest y axis label by drawing them off-screen
// and measuring the bounding boxes
y_label_width = 10 + d3.max(y_labels, function(l) {
var lw;
chart.append("text")
.attr({
class: "y axis",
transform: "translate(0, " + -bar_height + ")"
})
.text(l)
.each(function() {
lw = this.getBBox().width;
})
.remove().remove();
return lw;
});
g.append("text")
.attr({
x: 3,
y: bar_height/2
})
.text(function(d) { return d.rev; });
// exit
bar.exit().remove();
// update
update_timeline();
var xaxis = d3.svg.axis()
.scale(xscale)
.tickValues(data.slice(1, -1).map(function(d) { return d.published; }))
.tickFormat(d3.time.format("%b %Y"))
.orient("bottom");
// enter
var bar = chart.selectAll("g").data(data);
var g = bar.enter()
.append("g")
.attr({
class: "bar",
transform: offset
});
g.append("rect")
.attr({
height: bar_height,
width: bar_width
});
g.append("text")
.attr({
x: 3,
y: bar_height/2
})
.text(function(d) { return d.rev; });
var ids = data
.map(function(elem) { return elem.name; })
.filter(function(val, i, self) { return self.indexOf(val) === i; });
ids.shift(); // first one is pseudo entry (last one, too, but filtered above)
console.log(ids);
var y_scale = d3.scale.ordinal()
.domain(y_labels)
.rangePoints([0, bar_y]);
var yaxis = d3.svg.axis()
.scale(d3.scale.ordinal().domain(ids).rangePoints([0, y - bar_height]))
.tickValues(ids)
.orient("left");
var y_axis = d3.svg.axis()
.scale(y_scale)
.tickValues(y_labels)
.orient("left");
chart.append("g")
.attr({
class: "x axis",
transform: "translate(0, " + y + ")"
})
.call(xaxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("transform", "translate(-18, 8) rotate(-90)");
chart.append("g").attr({
class: "x axis",
transform: "translate(0, " + bar_y + ")"
});
update_x_axis();
chart.append("g")
.attr({
class: "y axis",
transform: "translate(10, " + bar_height/2 + ")"
})
.call(yaxis)
.selectAll("text")
.style("text-anchor", "start");
chart.append("g")
.attr({
class: "y axis",
transform: "translate(10, " + bar_height/2 + ")"
})
.call(y_axis)
.selectAll("text")
.style("text-anchor", "start");
chart.attr('height', y);
// set height of timeline
var x_label_height;
d3.select(".x.axis").each(function() {
x_label_height = this.getBBox().height;
});
chart.attr('height', bar_y + x_label_height);
}
d3.json("doc.json", function(error, json) {
if (error) return; // console.warn(error);
data = json["rev_history"];
// make js dates out of publication dates
data.forEach(function(d) { d.published = new Date(d.published); });
// add pseudo entry 185 days after last revision (when the ID will expire)
var pseudo = new Date(data[data.length - 1].published.getTime() +
1000*60*60*24*185);
data.push({ name: "", rev: "", published: pseudo});
draw_timeline();
});
$(window).on({
resize: function (event) {
draw_timeline();
resize: function() {
update_timeline();
}
});

View file

@ -12,7 +12,7 @@
{% block morecss %}
.inline { display: inline; }
#timeline .axis text { font-size: small; }
#timeline { font-size: small; }
#timeline .axis path, #timeline .axis line {
fill: none;
@ -21,28 +21,23 @@
#timeline .axis.y path, #timeline .axis.y line { stroke: none; }
#timeline .axis.x text {
dominant-baseline: central;
fill: darkgrey;
}
#timeline .axis.x text { dominant-baseline: central; }
#timeline .bar text {
fill: white;
dominant-baseline: central;
}
#timeline .bar:nth-child(odd) rect {
fill: #3abf03;
stroke: #32a602;
stroke-width: 1;
}
{% comment %} like label-success {% endcomment %}
#timeline .bar:nth-child(odd) rect { fill: #5CB85C; }
#timeline .bar:nth-child(even) rect {
fill: #6b5bad;
stroke: #5f4f9f;
stroke-width: 1;
}
{% comment %} like label-primary {% endcomment %}
#timeline .bar:nth-child(even) rect { fill: #337AB7; }
{% comment %} like label-warning {% endcomment %}
.stop-left { stop-color: #F0AD4E; }
.stop-right { stop-color: white; }
#timeline .bar:nth-last-child(4) rect { fill: url(#gradient); }
{% endblock %}
{% block title %}
@ -58,7 +53,7 @@
{{ top|safe }}
{% include "doc/revisions_list.html" %}
<div id="timeline"><svg></svg></div>
<div id="timeline"></div>
<table class="table table-condensed">
<thead id="message-row">