diff --git a/swh/web/static/js/calendar.js b/swh/web/static/js/calendar.js
index 4429fba5..c9faa307 100644
--- a/swh/web/static/js/calendar.js
+++ b/swh/web/static/js/calendar.js
@@ -1,365 +1,379 @@
/**
* Calendar:
* A one-off object that makes an AJAX call to the API's visit stats
* endpoint, then displays these statistics in a zoomable timeline-like
* format.
* Args:
* browse_url: the relative URL for browsing a revision via the web ui,
* accurate up to the origin
* visit_url: the complete relative URL for getting the origin's visit
* stats
* origin_id: the origin being browsed
* zoomw: the element that should contain the zoomable part of the calendar
* staticw: the element that should contain the static part of the calendar
* reset: the element that should reset the zoom level on click
*/
var Calendar = function(browse_url, data, origin_id,
zoomw, staticw, reset) {
/** Constants **/
this.month_names = ['Jan', 'Feb', 'Mar',
'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec'];
/** Display **/
this.desiredPxWidth = 7;
- this.padding = 0.01;
/** Object vars **/
this.origin_id = origin_id;
this.zoomw = zoomw;
this.staticw = staticw;
/** Calendar data **/
this.cal_data = null;
this.static = {
group_factor: 3600 * 1000,
group_data: null,
plot_data: null
};
this.zoom = {
group_factor: 3600 * 1000,
group_data: null,
plot_data: null
};
+ zoomw.css('height', '100%');
+
/**
* Keep a reference to the Calendar object context.
* Otherwise, 'this' changes to represent current function caller scope
*/
var self = this;
/**
* Group the plot's base data according to the grouping ratio and the
* range required
* Args:
* groupFactor: the amount the data should be grouped by
* range:
* Returns:
* A dictionary containing timestamps divided by the grouping ratio as
* keys, a list of the corresponding complete timestamps as values
*/
this.dataGroupedRange = function(groupFactor, range) {
var group_dict = {};
var start = range.xaxis.from;
var end = range.xaxis.to;
var range_data = self.cal_data.filter(function(item, index, arr) {
return item >= start && item <= end;
});
for (var date_idx in range_data) {
var date = range_data[date_idx];
var floor = Math.floor(date / groupFactor);
if (group_dict[floor] == undefined)
group_dict[floor] = [date];
else
group_dict[floor].push(date);
}
return group_dict;
};
/**
* Update the ratio that governs how the data is grouped based on changes
* in the data range or the display size, and regroup the plot's data
* according to this value.
*
* Args:
* element: the element in which the plot is displayed
* plotprops: the properties corresponding to that plot
* range: the range of the data displayed
*/
this.updateGroupFactorAndData = function(element, plotprops, range) {
var milli_length = range.xaxis.to - range.xaxis.from;
var px_length = element.width();
plotprops.group_factor = Math.floor(
self.desiredPxWidth * (milli_length / px_length));
plotprops.group_data = self.dataGroupedRange(
plotprops.group_factor, range);
};
/** Get plot data from the group data **/
this.getPlotData = function(grouped_data) {
var plot_data = [];
if (self.cal_data.length == 1) {
plot_data = [[self.cal_data[0] - 3600*1000*24*30, 0],
[self.cal_data[0], 1],
[self.cal_data[0] + 3600*1000*24*30, 0]];
}
else {
$.each(grouped_data, function(key, value) {
plot_data.push([value[0], value.length]);
});
}
return [{ label: 'Calendar', data: plot_data }];
};
this.plotZoom = function(zoom_options) {
return $.plot(self.zoomw, self.zoom.plot_data, zoom_options);
};
this.plotStatic = function(static_options) {
return $.plot(self.staticw, self.static.plot_data, static_options);
};
/**
* Display a zoomable calendar with click-through links to revisions
* of the same origin
*
* Args:
* data: the data that the calendar should present, as a list of
* POSIX second-since-epoch timestamps
*/
this.calendar = function(data) {
// POSIX timestamps to JS timestamps
self.cal_data = data.map(function(e)
{ return Math.floor(e['date'] * 1000); });
/** Bootstrap the group ratio **/
var cal_data_range = null;
if (self.cal_data.length == 1) {
var padding_qty = 3600*1000*24*30;
cal_data_range = {xaxis: {from: self.cal_data[0] - padding_qty,
to: self.cal_data[0] + padding_qty}};
}
else
cal_data_range = {xaxis: {from: self.cal_data[0],
to: self.cal_data[self.cal_data.length -1]
}
};
self.updateGroupFactorAndData(self.zoomw,
self.zoom,
cal_data_range);
self.updateGroupFactorAndData(self.staticw,
self.static,
cal_data_range);
/** Bootstrap the plot data **/
self.zoom.plot_data = self.getPlotData(self.zoom.group_data);
self.static.plot_data = self.getPlotData(self.zoom.group_data);
/**
* Return the flot-required function for displaying tooltips, according to
* the group we want to display the tooltip for
* Args:
* group_options: the group we want to display the tooltip for (self.static
* or self.zoom)
*/
function tooltip_fn(group_options) {
return function (label, x_timestamp, y_hits, item) {
var floor_index = Math.floor(
item.datapoint[0] / group_options.group_factor);
var tooltip_text = group_options.group_data[floor_index].map(
function(elem) {
var date = new Date(elem);
var year = date.getUTCFullYear();
var month = self.month_names[date.getUTCMonth()];
var day = date.getUTCDate();
var hr = date.getUTCHours();
var min = date.getUTCMinutes();
if (min < 10) min = '0'+min;
return [day,
month,
year + ',',
hr+':'+min,
'UTC'].join(' ');
}
);
return tooltip_text.join('
');
};
}
/** Plot options for both graph windows **/
var zoom_options = {
legend: {
show: false
},
series: {
clickable: true,
bars: {
show: true,
lineWidth: 1,
barWidth: self.zoom.group_factor
}
},
xaxis: {
mode: 'time',
minTickSize: [1, 'day'],
// monthNames: self.month_names,
- position: 'top'
+ position: 'top',
+ timeformat: "%e %b %Y"
},
yaxis: {
show: false
},
selection: {
mode: 'x'
},
grid: {
clickable: true,
- hoverable: true
+ hoverable: true,
+ margin: {
+ left: 30,
+ right: 30
+ }
},
tooltip: {
show: true,
content: tooltip_fn(self.zoom)
}
};
var overview_options = {
legend: {
show: false
},
series: {
clickable: true,
bars: {
show: true,
lineWidth: 1,
barWidth: self.static.group_factor
},
shadowSize: 0
},
yaxis: {
show: false
},
xaxis: {
mode: 'time',
minTickSize: [1, 'day']
},
grid: {
clickable: true,
hoverable: true,
- color: '#999'
+ color: '#999',
+ margin: {
+ left: 30,
+ right: 30
+ }
},
selection: {
mode: 'x'
},
tooltip: {
show: true,
content: tooltip_fn(self.static)
}
};
- function addPadding(options, range) {
- var len = range.xaxis.to - range.xaxis.from;
- return $.extend(true, {}, options, {
- xaxis: {
- min: range.xaxis.from - (self.padding * len),
- max: range.xaxis.to + (self.padding * len)
- }
- });
- }
-
/** draw the windows **/
- var plot = self.plotZoom(addPadding(zoom_options, cal_data_range));
- var overview = self.plotStatic(
- addPadding(overview_options, cal_data_range));
+ var plot = self.plotZoom(zoom_options);
+ var overview = self.plotStatic(overview_options);
var current_ranges = $.extend(true, {}, cal_data_range);
/**
* Zoom to the mouse-selected range in the given window
*
* Args:
* plotzone: the jQuery-selected element the zoomed plot should be
* in (usually the same as the original 'zoom plot' element)
* range: the data range as a dict {xaxis: {from:, to:},
* yaxis:{from:, to:}}
*/
function zoom(ranges) {
current_ranges.xaxis.from = ranges.xaxis.from;
current_ranges.xaxis.to = ranges.xaxis.to;
self.updateGroupFactorAndData(
self.zoomw, self.zoom, current_ranges);
self.zoom.plot_data = self.getPlotData(self.zoom.group_data);
var zoomedopts = $.extend(true, {}, zoom_options, {
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
series: {
bars: {barWidth: self.zoom.group_factor}
}
});
return self.plotZoom(zoomedopts);
}
function resetZoomW(plot_options) {
self.zoom.group_data = self.static.group_data;
self.zoom.plot_data = self.static.plot_data;
self.updateGroupFactorAndData(zoomw, self.zoom, cal_data_range);
- plot = self.plotZoom(addPadding(plot_options, cal_data_range));
+ plot = self.plotZoom(plot_options);
}
// now connect the two
self.zoomw.bind('plotselected', function (event, ranges) {
+ zoomw.css('height', '60%');
+ staticw.show();
+ reset.show();
// clamp the zooming to prevent eternal zoom
if (ranges.xaxis.to - ranges.xaxis.from < 0.00001)
ranges.xaxis.to = ranges.xaxis.from + 0.00001;
// do the zooming
plot = zoom(ranges);
// don't fire event on the overview to prevent eternal loop
overview.setSelection(ranges, true);
});
+ self.staticw_just_selected = false;
+
self.staticw.bind('plotselected', function (event, ranges) {
plot.setSelection(ranges);
+ self.staticw_just_selected = true;
});
function unbindClick() {
self.zoomw.unbind('plotclick');
self.staticw.unbind('plotclick');
}
function bindClick() {
self.zoomw.bind('plotclick', redirect_to_revision);
self.staticw.bind('plotclick', redirect_to_revision);
}
function redirect_to_revision(event, pos, item) {
- if (item) {
- var ts = Math.floor(item.datapoint[0] / 1000); // POSIX ts
- var url = browse_url + 'ts/' + ts + '/directory/';
+ if (item && !self.staticw_just_selected) {
+ var js_date = new Date(item.datapoint[0]);
+ js_date.setUTCSeconds(0);
+ js_date.setUTCMilliseconds(0);
+ var date = js_date.toISOString();
+ var url = browse_url + 'ts/' + date + '/directory/';
window.location.href = url;
}
+ self.staticw_just_selected = false;
}
reset.click(function(event) {
plot.clearSelection();
overview.clearSelection();
current_ranges = $.extend(true, {}, cal_data_range);
+ zoomw.css('height', '100%');
resetZoomW(zoom_options);
+ staticw.hide();
+ reset.hide();
+
});
$(window).resize(function(event) {
/** Update zoom display **/
self.updateGroupFactorAndData(zoomw, self.zoom, current_ranges);
self.zoom.plot_data = self.getPlotData(self.zoom.group_data);
/** Update static display **/
self.updateGroupFactorAndData(staticw, self.static, cal_data_range);
self.static.plot_data = self.getPlotData(self.static.group_data);
/** Replot **/
- plot = self.plotZoom(
- addPadding(zoom_options, current_ranges));
- overview = self.plotStatic(
- addPadding(overview_options, cal_data_range));
+ plot = self.plotZoom(zoom_options);
+ overview = self.plotStatic(overview_options);
});
bindClick();
+ staticw.hide();
+ reset.hide();
+
};
self.calendar(data);
};
diff --git a/swh/web/templates/origin.html b/swh/web/templates/origin.html
index 1735c92a..d81692ce 100644
--- a/swh/web/templates/origin.html
+++ b/swh/web/templates/origin.html
@@ -1,60 +1,60 @@
{% extends "browse.html" %}
{% load static %}
{% load swh_templatetags %}
{% block title %}Origin information{% endblock %}
{% block swh-browse-main-panel-content %}
Visit id | Visit date | Visit status | Browse revision url |
---|---|---|---|
{{ v.visit }} | {{ v.date }} | {{ v.status }} | {{ v.browse_url }} |