More tweaks to the graphical timeline. This is getting there.
- Legacy-Id: 10554
This commit is contained in:
parent
82ad203627
commit
b6e02b1c1f
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in a new issue