bijoe/bijoe/static/js/bijoe.js

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;
});
})