383 lines
11 KiB
JavaScript
383 lines
11 KiB
JavaScript
|
|
/* Utility functions */
|
|
|
|
function fmtChartJSPerso(config, value, fmt) {
|
|
if (fmt == "ellipsize") {
|
|
return ellipsize(value, 15);
|
|
}
|
|
if (fmt == "wrap") {
|
|
return wrap_text(value, 15);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function capfirst(a) {
|
|
if (! a.length) {
|
|
return a;
|
|
}
|
|
if (a.charCodeAt(0) > 96 && a.charCodeAt(0) < 123) {
|
|
return String.fromCharCode(a.charCodeAt(0) - 32) + a.slice(1);
|
|
}
|
|
}
|
|
|
|
function days_to_string(days) {
|
|
l = []
|
|
if (days >= 1) {
|
|
l.push(Math.floor(days).toString() + " jour(s) ")
|
|
days -= Math.floor(days);
|
|
}
|
|
hours = days * 24;
|
|
if (hours > 1) {
|
|
l.push(Math.floor(hours).toString() + " heure(s)")
|
|
hours -= Math.floor(hours);
|
|
}
|
|
minutes = hours * 60;
|
|
if (l.length < 2 && minutes > 1) {
|
|
l.push(Math.floor(minutes).toString() + " minutes(s)")
|
|
}
|
|
if (l.length) {
|
|
return human_join(l);
|
|
}
|
|
return "Aucun";
|
|
}
|
|
|
|
function human_join(a) {
|
|
/* Build french string for an enumeration */
|
|
if (a.length == 0) {
|
|
return '';
|
|
}else if (a.length == 1) {
|
|
return a[0].toString();
|
|
} else {
|
|
return a.slice(0, -1).join(', ') + ' et ' + a.slice(-1)[0];
|
|
}
|
|
}
|
|
|
|
function wrap_text(s, width) {
|
|
/* Wrap text after width characters */
|
|
var words = s.split(" ");
|
|
var s = "";
|
|
var l = 0;
|
|
for (var i = 0; i < words.length; i++) {
|
|
l += 1 + words[i].length;
|
|
if (l > width) {
|
|
s = s + "\n";
|
|
l = words[i].length;
|
|
} else {
|
|
s = s + " ";
|
|
}
|
|
s = s + words[i];
|
|
}
|
|
return s;
|
|
}
|
|
|
|
function ellipsize(s, length) {
|
|
if (s.length > length) {
|
|
return s.slice(0, length) + '…';
|
|
}
|
|
return s;
|
|
}
|
|
|
|
function spaced_hsla(i, n, s, l, a) {
|
|
/* Compute a specific color in a hue gradient in hsla colorspace, used to generate color maps for
|
|
* charts. */
|
|
var h = 360 * i/n;
|
|
return "hsla(" + h.toString() + ', ' + s.toString() + '%, ' + l.toString() + '%, ' + a.toString() + ')';
|
|
};
|
|
|
|
function toDataURL(canvas, filename) {
|
|
/* Convert a canvas to a PNG image with a white background, and generate a data url for it */
|
|
|
|
/* Temporary canvas with white background to print original canvas over */
|
|
destinationCanvas = document.createElement("canvas");
|
|
destinationCanvas.width = canvas.width;
|
|
destinationCanvas.height = canvas.height;
|
|
|
|
destCtx = destinationCanvas.getContext('2d');
|
|
|
|
// create a rectangle with the desired color
|
|
destCtx.fillStyle = "#FFFFFF";
|
|
destCtx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// draw the original canvas onto the destination canvas
|
|
destCtx.drawImage(canvas, 0, 0);
|
|
var data = destinationCanvas.toDataURL("image/png");
|
|
data = data.split(";", 2)[1];
|
|
data = "data:image/png;headers=Content-Disposition%3A%20attachment%3B%20filename=" + filename + ";" + data;
|
|
return data
|
|
}
|
|
|
|
var defaul_cn_options = {
|
|
/* Default ChartNew.js options */
|
|
animationSteps: 10,
|
|
/* Adapt graph to change in layout */
|
|
responsive: true,
|
|
/* Let aspect ratio match the window dimensions */
|
|
maintainAspectRatio: false,
|
|
/* Re-scale font based on responsive relayout */
|
|
responseScaleContent: true,
|
|
/* Show legend describing each color */
|
|
legend: true,
|
|
/* Start animation immediately do not wait for the chart to be visible */
|
|
dynamicDisplay: false,
|
|
/* Mimimum interval between y-axis ticks */
|
|
yAxisMinimumInterval: 10,
|
|
/* Show label on bars/points */
|
|
inGraphDataShow: true,
|
|
/* Set legend text color to black */
|
|
legendFontColor: "rgb(0,0,0,1)",
|
|
/* Set axis text color to black */
|
|
scaleFontColor: "rgb(0,0,0,1)",
|
|
/* Round number to two decimals */
|
|
roundNumber: -2,
|
|
/* Display data title when mouse is over the chart */
|
|
annotateDisplay : true,
|
|
/* Label do display on scale */
|
|
scaleLabel: "<%=value%>",
|
|
/* Set style for axis texts */
|
|
scaleFontSize : 16,
|
|
/* Set style of graph title */
|
|
graphTitleFontFamily : "'Arial'",
|
|
graphTitleFontSize : 24,
|
|
graphTitleFontStyle : "bold",
|
|
graphTitleFontColor : "#666",
|
|
/* Set style of footnote text (only used in piecharts) */
|
|
footNoteFontSize: 16,
|
|
fmtXLabel: "ellipsize",
|
|
}
|
|
|
|
function draw_piechart(canvas, dimensions, measures, measure, data) {
|
|
/* Draw piechart given a cavans element, a list of dimensions, a list of measure, the name of the
|
|
* measure to represent and data rows containing in the same order the dimensions and measures
|
|
* values */
|
|
var datasets = [];
|
|
var linedata = {datasets: datasets};
|
|
linedata.labels = [""];
|
|
var empty = []
|
|
var n = dimensions.length;
|
|
var option = $.extend({}, defaul_cn_options);
|
|
var ctx = canvas.getContext("2d");
|
|
var dimension_labels = [];
|
|
for (var i = 0; i < dimensions.length; i++) {
|
|
dimension_labels.push(dimensions[i].label);
|
|
}
|
|
|
|
for (var j = 0; j < measures.length; j++) {
|
|
if (measures[j].name == measure) {
|
|
break;
|
|
}
|
|
}
|
|
if (j == measures.length) {
|
|
// measure not found
|
|
return;
|
|
}
|
|
option.graphTitle = wrap_text(
|
|
capfirst(measures[j].label) + " par " + human_join(dimension_labels), 30)
|
|
|
|
/* sort pie by slice size */
|
|
data.sort(function (a, b) {
|
|
return a.measures[j].value - b.measures[j].value;
|
|
});
|
|
|
|
/* Count number of empty cells */
|
|
var n = data.length;
|
|
var empty_count = 0;
|
|
for (var i = 0; i < n; i++) {
|
|
var value = data[i].measures[j].value;
|
|
if (value > 1) {
|
|
empty_count += 1;
|
|
}
|
|
}
|
|
|
|
/* Build one dataset by measurement, it's the way the piecharts expects it. */
|
|
var n = data.length;
|
|
for (var i = 0; i < n; i++) {
|
|
var value = data[i].measures[j].value;
|
|
var title = [];
|
|
|
|
for (var k = 0; k < data[i].coords.length; k++) {
|
|
title.push(data[i].coords[k].value);
|
|
}
|
|
title = title.join();
|
|
|
|
if (value > 1) {
|
|
dataset = {
|
|
data: [value],
|
|
title: title,
|
|
fillColor: spaced_hsla(datasets.length, empty_count, 100, 30, 0.5),
|
|
strokeColor: spaced_hsla(datasets.length, empty_count, 100, 30, 0.75),
|
|
highlightFill: spaced_hsla(datasets.length, empty_count, 100, 30, 0.75),
|
|
highlightStroke: spaced_hsla(datasets.length, empty_count, 100, 30, 1)
|
|
};
|
|
datasets.push(dataset);
|
|
} else {
|
|
/* push empty pie slices to the empty array */
|
|
empty.push(title);
|
|
}
|
|
}
|
|
|
|
/* show only percentages */
|
|
option.inGraphDataTmpl = "<%=v1+': '+v6+' %'%>";
|
|
option.annotateLabel = "<%=v1+' pour '+v2+': '+v6+' %'%>";
|
|
|
|
/* if some slices are empty or nearly empty, do not try to show them in the piechart but add a
|
|
* footnore */
|
|
if (empty.length) {
|
|
option.footNote = wrap_text("Les valeurs pour " + human_join(empty)
|
|
+ " sont nulles ou presque.", 110);
|
|
}
|
|
new Chart(ctx).Pie(linedata, option);
|
|
}
|
|
|
|
function draw_barchart(canvas, dimensions, measures, measure, data) {
|
|
/* Draw bar chart given a cavans element, a list of dimensions, a list of measure, the name of the
|
|
* measure to represent and data rows containing in the same order the dimensions and measures
|
|
* values */
|
|
var dataset = {
|
|
data: [],
|
|
title: "",
|
|
fillColor: [],
|
|
strokeColor: [],
|
|
highlightFill: [],
|
|
highlightStroke: []
|
|
}
|
|
var datasets = [dataset];
|
|
var linedata = {labels: [], datasets: datasets};
|
|
var empty = []
|
|
var n = dimensions.length;
|
|
var option = $.extend({}, defaul_cn_options);
|
|
var ctx = canvas.getContext("2d");
|
|
var dimension_labels = [];
|
|
for (var i = 0; i < dimensions.length; i++) {
|
|
dimension_labels.push(dimensions[i].label);
|
|
}
|
|
|
|
for (var j = 0; j < measures.length; j++) {
|
|
if (measures[j].name == measure) {
|
|
break;
|
|
}
|
|
}
|
|
if (j == measures.length) {
|
|
// measure not found
|
|
return;
|
|
}
|
|
measure = measures[j]
|
|
|
|
dataset.title = measures[j].label;
|
|
option.graphTitle = wrap_text(
|
|
capfirst(measures[j].label) + " par " + human_join(dimension_labels), 30)
|
|
|
|
dataset.fillColor = spaced_hsla(j, measures.length, 100, 30, 0.5);
|
|
dataset.strokeColor = spaced_hsla(j, measures.length, 100, 30, 0.75);
|
|
dataset.highlightFill = spaced_hsla(j, measures.length, 100, 30, 0.75);
|
|
dataset.highlightStroke = spaced_hsla(j, measures.length, 100, 30, 1);
|
|
|
|
var n = data.length;
|
|
|
|
option.annotateLabel = "<%=wrap_text(v1+' pour <br/>'+ v2 + ': '+v3, 10)%>";
|
|
option.inGraphDataTmpl = "<%=v3%>";
|
|
|
|
if (measure.name == "count") {
|
|
option.yAxisLabel = "quantité";
|
|
}
|
|
|
|
if (measure.name == "percent") {
|
|
option.yAxisLabel = "pourcentage";
|
|
}
|
|
|
|
if (measure.type == "duration") {
|
|
var max = 0;
|
|
var scale = 0;
|
|
|
|
for (var i = 0; i < n; i++) {
|
|
var row = data[i];
|
|
max = Math.max(row.measures[j].value, max);
|
|
console.log("max", max)
|
|
}
|
|
console.log("max final", max)
|
|
|
|
if (max > 1) {
|
|
option.yAxisLabel = "jours";
|
|
option.annotateLabel = "<%=v1+' pour ' + v2 + ': '+days_to_string(v3)%>";
|
|
option.inGraphDataTmpl = "<%=days_to_string(v3)%>";
|
|
} else if (max > (1. / 24)) {
|
|
option.yAxisLabel = "heures";
|
|
scale = 24;
|
|
option.annotateLabel = "<%=v1+' pour ' + v2 + ': '+days_to_string(v3 / 24.)%>";
|
|
option.inGraphDataTmpl = "<%=days_to_string(v3 / 24.)%>";
|
|
} else {
|
|
option.yAxisLabel = "minutes";
|
|
scale = 24 * 60;
|
|
option.annotateLabel = "<%=v1+' pour ' + v2 + ': '+days_to_string(v3 / (60 * 24))%>";
|
|
option.inGraphDataTmpl = "<%=days_to_string(v3 / (60 * 24))%>";
|
|
}
|
|
if (scale > 0) {
|
|
for (var i = 0; i < n; i++) {
|
|
var row = data[i];
|
|
row.measures[j].value *= scale;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < n; i++) {
|
|
var row = data[i];
|
|
var title = [];
|
|
|
|
for (var k = 0; k < data[i].coords.length; k++) {
|
|
title.push(data[i].coords[k].value);
|
|
}
|
|
title = title.join();
|
|
linedata.labels.push(title)
|
|
|
|
dataset.data.push(row.measures[j].value);
|
|
}
|
|
|
|
$.extend(option, {
|
|
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
|
scaleBeginAtZero : true,
|
|
|
|
//Boolean - Whether grid lines are shown across the chart
|
|
scaleShowGridLines : true,
|
|
|
|
//String - Colour of the grid lines
|
|
scaleGridLineColor : "rgba(0,0,0,.05)",
|
|
|
|
//Number - Width of the grid lines
|
|
scaleGridLineWidth : 1,
|
|
|
|
//Boolean - Whether to show horizontal lines (except X axis)
|
|
scaleShowHorizontalLines: true,
|
|
|
|
//Boolean - Whether to show vertical lines (except Y axis)
|
|
scaleShowVerticalLines: true,
|
|
|
|
//Boolean - If there is a stroke on each bar
|
|
// barShowStroke : true,
|
|
|
|
//Number - Pixel width of the bar stroke
|
|
barStrokeWidth : 2,
|
|
|
|
//Number - Spacing between each of the X value sets
|
|
barValueSpacing : 5,
|
|
|
|
//Number - Spacing between data sets within X values
|
|
barDatasetSpacing : 1,
|
|
});
|
|
new Chart(ctx).Bar(linedata, option);
|
|
}
|
|
|
|
/* jQuery event handlers and widget setup */
|
|
$(function () {
|
|
/* Submit after 300ms on any change to form input */
|
|
$('input, select').on('change', function () {
|
|
setTimeout(function () {
|
|
$('form').ajaxSubmit({success: function (data) {
|
|
var $content = $(data);
|
|
$('#data').replaceWith($content.find('#data'));
|
|
$('#breadcrumb').replaceWith($content.find('#breadcrumb'));
|
|
location.hash = 'data';
|
|
}});
|
|
}, 300);
|
|
return true;
|
|
});
|
|
})
|