liberapay.com/js/charts.js

121 lines
3.7 KiB
JavaScript
Raw Permalink Normal View History

Liberapay.charts = {};
2018-01-09 18:31:36 +01:00
Liberapay.charts.init = function() {
$('[data-charts]').each(function () {
var url = $(this).data('charts');
if (this.tagName == 'BUTTON') {
var $container = $($(this).data('charts-container'));
$(this).click(function() {
$(this).attr('disabled', '').prop('disabled');
Liberapay.charts.load(url, $container);
});
} else {
Liberapay.charts.load(url, $(this));
}
2018-01-09 18:31:36 +01:00
});
}
Liberapay.charts.load = function(url, $container) {
fetch(url).then(function(response) {
response.json().then(function(series) {
$(function() {
Liberapay.charts.make(series, $container);
});
}).catch(Liberapay.error);
}).catch(Liberapay.error);
}
Liberapay.charts.make = function(series, $container) {
if (series.length) {
$('.chart-wrapper').show();
} else {
if ($container.attr('data-msg-empty')) {
$container.append($('<span>').text(' '+$container.attr('data-msg-empty')));
}
return;
}
2017-11-02 21:16:49 +01:00
function parsePoint(o) {
return parseFloat(o ? o.amount || o : 0);
2017-11-02 21:16:49 +01:00
}
// Reverse the series.
// ===================
2015-01-07 22:27:40 -05:00
// For historical reasons the API is descending when we want ascending.
series.reverse();
2013-11-12 22:54:31 -05:00
// Gather charts.
// ==============
// Sniff the first element to determine what data points are available, and
// then search the page for chart containers matching each data point
// variable name.
2015-01-07 22:27:40 -05:00
var charts = Object.keys(series[0]).map(function(name) {
return $('[data-chart=' + name + ']');
}).filter(function(c) { return c.length });
2015-01-07 22:27:40 -05:00
var H = $('.chart').height() - 20;
2017-03-22 11:17:27 +01:00
var W = (1 / series.length).toFixed(10) * $('.chart').width();
var skip = 0;
if (W < 5) {
var keep = Math.floor($('.chart').width() / 5);
skip = series.length - keep;
series = series.slice(-keep);
}
2017-03-22 11:17:27 +01:00
W = W > 10 ? '10px' : (W < 5 ? '5px' : Math.floor(W)+'px');
// Compute maxes and scales.
// =========================
2015-01-07 12:37:19 -05:00
var maxes = charts.map(function(chart) {
return series.reduce(function(previous, current) {
2017-11-02 21:16:49 +01:00
return Math.max(previous, parsePoint(current[chart.data('chart')]));
2015-01-07 12:37:19 -05:00
}, 0);
});
2015-01-07 12:37:19 -05:00
var scales = maxes.map(function(max) {
return Math.ceil(max / 100) * 100; // round to nearest hundred
});
2015-01-06 16:47:33 -05:00
// Draw bars.
// ==========
2015-01-07 22:27:40 -05:00
charts.forEach(function(chart, chart_index) {
chart.css('min-width', (series.length * 5) + 'px');
2015-01-07 22:27:40 -05:00
series.forEach(function(point, index) {
2017-11-02 21:16:49 +01:00
var y = parsePoint(point[chart.data('chart')]);
2015-01-07 22:27:40 -05:00
var bar = $('<div>').addClass('bar');
var shaded = $('<div>').addClass('shaded');
shaded.html('<span class="y-label">'+ y.toFixed() +'</span>');
2018-11-03 16:23:11 +01:00
if (index < series.length / 2) {
bar.addClass('left');
}
2015-01-07 22:27:40 -05:00
bar.append(shaded);
var xTick = $('<span>').addClass('x-tick');
xTick.text(point.date);
2015-01-07 22:27:40 -05:00
bar.append(xTick);
// Display a max flag (only once)
if (y === maxes[chart_index] && !chart.data('max-applied')) {
bar.addClass('flagged');
2018-11-03 16:29:52 +01:00
chart.data('max-applied', true);
2015-01-07 22:27:40 -05:00
}
bar.css('width', W);
2015-01-07 23:21:15 -05:00
var h = y / scales[chart_index] * H;
if (y > 0) h = Math.max(h, 1); // make sure only true 0 is 0 height
shaded.css('height', h);
2015-01-07 22:27:40 -05:00
bar.click(function() {
$(this).toggleClass('flagged');
});
chart.append(bar);
});
2015-01-07 22:27:40 -05:00
});
};