Skip to content

Commit cf49640

Browse files
committed
Fix some issues Henrik found, and implement some of his suggestions:
* Fix ancestor stacking * Suppress x-axis labels that overlap with others * Make bars clickable links - Legacy-Id: 10590
1 parent bdf8089 commit cf49640

3 files changed

Lines changed: 73 additions & 29 deletions

File tree

ietf/doc/utils.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from django.db.models.query import EmptyQuerySet
99
from django.forms import ValidationError
1010
from django.utils.html import strip_tags, escape
11+
from django.core.urlresolvers import reverse as urlreverse
1112

1213
from ietf.doc.models import Document, DocHistory, State
1314
from ietf.doc.models import DocAlias, RelatedDocument, BallotType, DocReminder
@@ -561,7 +562,8 @@ def ancestors(doc):
561562
retval.append({
562563
'name': d.name,
563564
'rev': e.newrevisiondocevent.rev,
564-
'published': e.time.isoformat()
565+
'published': e.time.isoformat(),
566+
'url': urlreverse("doc_view", kwargs=dict(name=d)) + e.newrevisiondocevent.rev + "/"
565567
})
566568

567569
if doc.type_id == "draft":
@@ -572,6 +574,7 @@ def ancestors(doc):
572574
retval.append({
573575
'name': e.doc.canonical_name(),
574576
'rev': e.doc.canonical_name(),
575-
'published': e.time.isoformat()
577+
'published': e.time.isoformat(),
578+
'url': urlreverse("doc_view", kwargs=dict(name=e.doc))
576579
})
577580
return sorted(retval, key=lambda x: x['published'])

ietf/static/ietf/css/ietf.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ form.navbar-form input.form-control.input-sm { width: 141px; }
443443
#timeline .bar text {
444444
fill: white;
445445
dominant-baseline: central;
446+
pointer-events: none;
446447
}
447448

448449
/* like label-success */

ietf/static/ietf/js/document_timeline.js

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,36 @@ var y_label_width;
88
var x_axis;
99
var width;
1010

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\d+$")) {
14-
bar_y += bar_height;
11+
12+
function expiration_date(d) {
13+
return new Date(d.published.getTime() + 1000 * 60 * 60 * 24 * 185);
14+
}
15+
16+
17+
function max(arr) {
18+
return Math.max.apply(null, Object.keys(arr).map(function(e) {
19+
return arr[e];
20+
}));
21+
}
22+
23+
24+
function offset(d) {
25+
if (bar_y[d.name] === undefined) {
26+
var m = Object.keys(bar_y).length === 0 ? -bar_height : max(bar_y);
27+
bar_y[d.name] = m + bar_height;
1528
}
16-
return "translate(" + x_scale(d.published) + ", " + bar_y + ")";
29+
return "translate(" + x_scale(d.published) + ", " + bar_y[d.name] + ")";
1730
}
1831

1932

2033
function bar_width(d, i) {
21-
if (i < data.length - 1) {
22-
return x_scale(data[i + 1].published) - x_scale(d.published);
34+
// check for next rev of this name
35+
for (i++; i < data.length; i++) {
36+
if (data[i].name === d.name) { break; }
2337
}
38+
39+
var w = i === data.length ? expiration_date(d) : data[i].published;
40+
return x_scale(w) - x_scale(d.published);
2441
}
2542

2643

@@ -33,10 +50,21 @@ function scale_x() {
3350
d3.max(data, function(d) { return d.published; })
3451
]).range([y_label_width, width]);
3552

53+
// resort data by publication time to suppress some ticks if they are closer
54+
// than 12px, and don't add a tick for the final pseudo entry
55+
var tv = data
56+
.slice(0, -1)
57+
.sort(function(a, b) { return a.published - b.published; })
58+
.map(function(d, i, arr) {
59+
if (i === 0 ||
60+
x_scale(d.published) > x_scale(arr[i - 1].published) + 12) {
61+
return d.published;
62+
}
63+
}).filter(function(d) { return d !== undefined; });
64+
3665
x_axis = d3.svg.axis()
3766
.scale(x_scale)
38-
// don't add a tick for the pseudo entry
39-
.tickValues(data.slice(0, -1).map(function(d) { return d.published; }))
67+
.tickValues(tv)
4068
.tickFormat(d3.time.format("%b %Y"))
4169
.orient("bottom");
4270
}
@@ -51,10 +79,11 @@ function update_x_axis() {
5179

5280

5381
function update_timeline() {
54-
bar_y = 0;
82+
bar_y = {};
5583
scale_x();
5684
var chart = d3.select("#timeline svg").attr("width", width);
57-
var bar = chart.selectAll("g").data(data);
85+
// enter data (skip the last pseudo entry)
86+
var bar = chart.selectAll("g").data(data.slice(0, -1));
5887
bar.attr("transform", offset).select("rect").attr("width", bar_width);
5988
update_x_axis();
6089
}
@@ -84,7 +113,7 @@ function draw_timeline() {
84113
});
85114

86115
var y_labels = data
87-
.map(function(elem) { return elem.name; })
116+
.map(function(d) { return d.name; })
88117
.filter(function(val, i, self) { return self.indexOf(val) === i; });
89118

90119
// calculate the width of the widest y axis label by drawing them off-screen
@@ -107,19 +136,28 @@ function draw_timeline() {
107136
// update
108137
update_timeline();
109138

110-
// enter
111-
var bar = chart.selectAll("g").data(data);
139+
// re-order data by document name, for CSS background color alternation
140+
var ndata = [];
141+
y_labels.forEach(function(l) {
142+
ndata = ndata.concat(data.filter(function(d) { return d.name === l; }));
143+
});
144+
data = ndata;
145+
146+
// enter data (skip the last pseudo entry)
147+
var bar = chart.selectAll("g").data(data.slice(0, -1));
112148
var g = bar.enter()
113149
.append("g")
114150
.attr({
115151
class: "bar",
116152
transform: offset
117153
});
118-
g.append("rect")
119-
.attr({
120-
height: bar_height,
121-
width: bar_width
122-
});
154+
g.append("a")
155+
.attr("xlink:href", function(d) { return d.url; })
156+
.append("rect")
157+
.attr({
158+
height: bar_height,
159+
width: bar_width
160+
});
123161
g.append("text")
124162
.attr({
125163
x: 3,
@@ -129,11 +167,11 @@ function draw_timeline() {
129167

130168
// since the gradient is defined inside the SVG, we need to set the CSS
131169
// style here, so the relative URL works
132-
$("#timeline .bar:nth-last-child(2) rect").css("fill", "url(#gradient)");
170+
$("#timeline .bar:last-child rect").css("fill", "url(#gradient)");
133171

134172
var y_scale = d3.scale.ordinal()
135173
.domain(y_labels)
136-
.rangePoints([0, bar_y]);
174+
.rangePoints([0, max(bar_y) + bar_height]);
137175

138176
var y_axis = d3.svg.axis()
139177
.scale(y_scale)
@@ -142,7 +180,7 @@ function draw_timeline() {
142180

143181
chart.append("g").attr({
144182
class: "x axis",
145-
transform: "translate(0, " + bar_y + ")"
183+
transform: "translate(0, " + (max(bar_y) + bar_height) + ")"
146184
});
147185
update_x_axis();
148186

@@ -160,7 +198,7 @@ function draw_timeline() {
160198
d3.select(".x.axis").each(function() {
161199
x_label_height = this.getBBox().height;
162200
});
163-
chart.attr("height", bar_y + x_label_height);
201+
chart.attr("height", max(bar_y) + bar_height + x_label_height);
164202
}
165203

166204

@@ -172,10 +210,12 @@ d3.json("doc.json", function(error, json) {
172210
// make js dates out of publication dates
173211
data.forEach(function(d) { d.published = new Date(d.published); });
174212

175-
// add pseudo entry 185 days after last rev (when the ID will expire)
176-
var pseudo = new Date(data[data.length - 1].published.getTime() +
177-
1000 * 60 * 60 * 24 * 185);
178-
data.push({name: "", rev: "", published: pseudo});
213+
// add pseudo entry when the ID will expire
214+
data.push({
215+
name: "",
216+
rev: "",
217+
published: expiration_date(data[data.length - 1])
218+
});
179219
draw_timeline();
180220
}
181221
});

0 commit comments

Comments
 (0)