Skip to content

Commit b6e02b1

Browse files
committed
More tweaks to the graphical timeline. This is getting there.
- Legacy-Id: 10554
1 parent 82ad203 commit b6e02b1

2 files changed

Lines changed: 163 additions & 118 deletions

File tree

Lines changed: 152 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,176 @@
1+
"use strict";
2+
13
var data;
4+
var x_scale;
5+
var bar_y;
6+
var bar_height;
7+
var y_label_width;
8+
var x_axis;
9+
var width;
210

3-
d3.json("doc.json", function(error, json) {
4-
if (error) return console.warn(error);
5-
data = json["rev_history"];
11+
function offset(d, i) {
12+
// increase the y offset if the document name changed in this revision
13+
if (i > 0 && data[i - 1].name !== d.name || d.rev.match("rfc"))
14+
bar_y += bar_height;
15+
return "translate(" + x_scale(d.published) + ", " + bar_y + ")";
16+
}
617

7-
// make js dates out of publication dates
8-
data.forEach(function(el) { el.published = new Date(el.published); });
918

10-
// add pseudo entry for beginning of year of first publication
11-
var year = data[0].published.getFullYear();
12-
data.unshift({ name:'', rev: '', published: new Date(year, 0, 0)});
19+
function bar_width(d, i) {
20+
if (i < data.length - 1)
21+
return x_scale(data[i + 1].published) - x_scale(d.published);
22+
}
1323

14-
// add pseudo entry at end of year of last revision
15-
year = data[data.length - 1].published.getFullYear();
16-
data.push({ name:'', rev: '', published: new Date(year + 1, 0, 0)});
1724

18-
draw_timeline();
19-
});
25+
function scale_x() {
26+
width = $("#timeline").width();
2027

28+
// scale data to width of container minus y label width
29+
x_scale = d3.time.scale().domain([
30+
d3.min(data, function(d) { return d.published; }),
31+
d3.max(data, function(d) { return d.published; })
32+
]).range([y_label_width, width]);
2133

22-
var xscale;
23-
var y;
24-
var bar_height;
34+
x_axis = d3.svg.axis()
35+
.scale(x_scale)
36+
// don't add a tick for the pseudo entry
37+
.tickValues(data.slice(0, -1).map(function(d) { return d.published; }))
38+
.tickFormat(d3.time.format("%b %Y"))
39+
.orient("bottom");
40+
}
2541

2642

27-
function offset(d, i) {
28-
if (i > 1 && data[i - 1].name !== d.name || d.rev.match("rfc"))
29-
y += bar_height;
30-
return "translate(" + xscale(d.published) + ", " + y + ")";
43+
function update_x_axis() {
44+
d3.select("#timeline svg .x.axis").call(x_axis)
45+
.selectAll("text")
46+
.style("text-anchor", "end")
47+
.attr("transform", "translate(-14, 2) rotate(-60)");
3148
}
3249

3350

34-
function bar_width(d, i) {
35-
if (i > 0 && i < data.length - 1)
36-
return xscale(data[i + 1].published) - xscale(d.published);
51+
function update_timeline() {
52+
bar_y = 0;
53+
scale_x();
54+
var chart = d3.select("#timeline svg").attr("width", width);
55+
var bar = chart.selectAll("g").data(data);
56+
bar.attr("transform", offset).select("rect").attr("width", bar_width);
57+
update_x_axis();
3758
}
3859

3960

4061
function draw_timeline() {
41-
var w = $("#timeline").width();
42-
// bar_height = parseFloat($("body").css('line-height'));
43-
bar_height = 30;
44-
45-
xscale = d3.time.scale().domain([
46-
d3.min(data, function(d) { return d.published; }),
47-
d3.max(data, function(d) { return d.published; })
48-
]).range([0, w]);
49-
50-
y = 0;
51-
var chart = d3.select("#timeline svg").attr("width", w);
52-
var bar = chart.selectAll("g").data(data);
53-
54-
// update
55-
bar
56-
.attr("transform", offset)
57-
.select("rect")
58-
.attr("width", bar_width);
59-
60-
// enter
61-
var g = bar.enter()
62-
.append("g")
63-
.attr({
64-
class: "bar",
65-
transform: offset
66-
});
67-
g.append("rect")
68-
.attr({
69-
height: bar_height,
70-
width: bar_width
62+
bar_height = parseFloat($("body").css('line-height'));
63+
// bar_height = 20;
64+
65+
var div = $("#timeline");
66+
if (div.is(":empty"))
67+
div.append("<svg></svg>");
68+
var chart = d3.select("#timeline svg").attr("width", width);
69+
70+
var gradient = chart.append("defs")
71+
.append("linearGradient")
72+
.attr("id", "gradient");
73+
gradient.append('stop')
74+
.attr('class', 'stop-left')
75+
.attr('offset', '0');
76+
gradient.append('stop')
77+
.attr('class', 'stop-right')
78+
.attr('offset', '1');
79+
80+
var y_labels = data
81+
.map(function(elem) { return elem.name; })
82+
.filter(function(val, i, self) { return self.indexOf(val) === i; });
83+
84+
// calculate the width of the widest y axis label by drawing them off-screen
85+
// and measuring the bounding boxes
86+
y_label_width = 10 + d3.max(y_labels, function(l) {
87+
var lw;
88+
chart.append("text")
89+
.attr({
90+
class: "y axis",
91+
transform: "translate(0, " + -bar_height + ")"
92+
})
93+
.text(l)
94+
.each(function() {
95+
lw = this.getBBox().width;
96+
})
97+
.remove().remove();
98+
return lw;
99+
});
100+
101+
// update
102+
update_timeline();
103+
104+
// enter
105+
var bar = chart.selectAll("g").data(data);
106+
var g = bar.enter()
107+
.append("g")
108+
.attr({
109+
class: "bar",
110+
transform: offset
111+
});
112+
g.append("rect")
113+
.attr({
114+
height: bar_height,
115+
width: bar_width
116+
});
117+
g.append("text")
118+
.attr({
119+
x: 3,
120+
y: bar_height/2
121+
})
122+
.text(function(d) { return d.rev; });
123+
124+
var y_scale = d3.scale.ordinal()
125+
.domain(y_labels)
126+
.rangePoints([0, bar_y]);
127+
128+
var y_axis = d3.svg.axis()
129+
.scale(y_scale)
130+
.tickValues(y_labels)
131+
.orient("left");
132+
133+
chart.append("g").attr({
134+
class: "x axis",
135+
transform: "translate(0, " + bar_y + ")"
71136
});
72-
g.append("text")
73-
.attr({
74-
x: 3,
75-
y: bar_height/2
76-
})
77-
.text(function(d) { return d.rev; });
78-
79-
// exit
80-
bar.exit().remove();
81-
82-
var xaxis = d3.svg.axis()
83-
.scale(xscale)
84-
.tickValues(data.slice(1, -1).map(function(d) { return d.published; }))
85-
.tickFormat(d3.time.format("%b %Y"))
86-
.orient("bottom");
87-
88-
var ids = data
89-
.map(function(elem) { return elem.name; })
90-
.filter(function(val, i, self) { return self.indexOf(val) === i; });
91-
ids.shift(); // first one is pseudo entry (last one, too, but filtered above)
92-
console.log(ids);
93-
94-
var yaxis = d3.svg.axis()
95-
.scale(d3.scale.ordinal().domain(ids).rangePoints([0, y - bar_height]))
96-
.tickValues(ids)
97-
.orient("left");
98-
99-
chart.append("g")
100-
.attr({
101-
class: "x axis",
102-
transform: "translate(0, " + y + ")"
103-
})
104-
.call(xaxis)
105-
.selectAll("text")
106-
.style("text-anchor", "end")
107-
.attr("transform", "translate(-18, 8) rotate(-90)");
108-
109-
chart.append("g")
110-
.attr({
111-
class: "y axis",
112-
transform: "translate(10, " + bar_height/2 + ")"
113-
})
114-
.call(yaxis)
115-
.selectAll("text")
116-
.style("text-anchor", "start");
117-
118-
chart.attr('height', y);
137+
update_x_axis();
138+
139+
chart.append("g")
140+
.attr({
141+
class: "y axis",
142+
transform: "translate(10, " + bar_height/2 + ")"
143+
})
144+
.call(y_axis)
145+
.selectAll("text")
146+
.style("text-anchor", "start");
147+
148+
// set height of timeline
149+
var x_label_height;
150+
d3.select(".x.axis").each(function() {
151+
x_label_height = this.getBBox().height;
152+
});
153+
chart.attr('height', bar_y + x_label_height);
119154
}
120155

121156

157+
d3.json("doc.json", function(error, json) {
158+
if (error) return; // console.warn(error);
159+
data = json["rev_history"];
160+
161+
// make js dates out of publication dates
162+
data.forEach(function(d) { d.published = new Date(d.published); });
163+
164+
// add pseudo entry 185 days after last revision (when the ID will expire)
165+
var pseudo = new Date(data[data.length - 1].published.getTime() +
166+
1000*60*60*24*185);
167+
data.push({ name: "", rev: "", published: pseudo});
168+
draw_timeline();
169+
});
170+
171+
122172
$(window).on({
123-
resize: function (event) {
124-
draw_timeline();
173+
resize: function() {
174+
update_timeline();
125175
}
126176
});

ietf/templates/doc/document_draft.html

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
{% block morecss %}
1313
.inline { display: inline; }
1414

15-
#timeline .axis text { font-size: small; }
15+
#timeline { font-size: small; }
1616

1717
#timeline .axis path, #timeline .axis line {
1818
fill: none;
@@ -21,28 +21,23 @@
2121

2222
#timeline .axis.y path, #timeline .axis.y line { stroke: none; }
2323

24-
#timeline .axis.x text {
25-
dominant-baseline: central;
26-
fill: darkgrey;
27-
}
24+
#timeline .axis.x text { dominant-baseline: central; }
2825

2926
#timeline .bar text {
3027
fill: white;
3128
dominant-baseline: central;
3229
}
3330

34-
#timeline .bar:nth-child(odd) rect {
35-
fill: #3abf03;
36-
stroke: #32a602;
37-
stroke-width: 1;
38-
}
31+
{% comment %} like label-success {% endcomment %}
32+
#timeline .bar:nth-child(odd) rect { fill: #5CB85C; }
3933

40-
#timeline .bar:nth-child(even) rect {
41-
fill: #6b5bad;
42-
stroke: #5f4f9f;
43-
stroke-width: 1;
44-
}
34+
{% comment %} like label-primary {% endcomment %}
35+
#timeline .bar:nth-child(even) rect { fill: #337AB7; }
4536

37+
{% comment %} like label-warning {% endcomment %}
38+
.stop-left { stop-color: #F0AD4E; }
39+
.stop-right { stop-color: white; }
40+
#timeline .bar:nth-last-child(4) rect { fill: url(#gradient); }
4641
{% endblock %}
4742

4843
{% block title %}
@@ -58,7 +53,7 @@
5853
{{ top|safe }}
5954

6055
{% include "doc/revisions_list.html" %}
61-
<div id="timeline"><svg></svg></div>
56+
<div id="timeline"></div>
6257

6358
<table class="table table-condensed">
6459
<thead id="message-row">

0 commit comments

Comments
 (0)