From b6e02b1c1fd009165c1aa6c5817ec2f3b91bdc88 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Wed, 9 Dec 2015 15:38:33 +0000 Subject: [PATCH] More tweaks to the graphical timeline. This is getting there. - Legacy-Id: 10554 --- ietf/static/ietf/js/document_timeline.js | 242 ++++++++++++++--------- ietf/templates/doc/document_draft.html | 27 ++- 2 files changed, 157 insertions(+), 112 deletions(-) diff --git a/ietf/static/ietf/js/document_timeline.js b/ietf/static/ietf/js/document_timeline.js index a84944746..2b42b7597 100644 --- a/ietf/static/ietf/js/document_timeline.js +++ b/ietf/static/ietf/js/document_timeline.js @@ -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(""); + 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(); } }); diff --git a/ietf/templates/doc/document_draft.html b/ietf/templates/doc/document_draft.html index 6d1eadc9b..6af87a31e 100644 --- a/ietf/templates/doc/document_draft.html +++ b/ietf/templates/doc/document_draft.html @@ -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" %} -
+