Optimized chart code.

This commit is contained in:
James Cole
2016-11-16 20:35:25 +01:00
parent cb02e0ee71
commit 4ef324cf24
20 changed files with 191 additions and 405 deletions

View File

@@ -0,0 +1,46 @@
var defaultChartOptions = {
scales: {
xAxes: [
{
gridLines: {
display: false
}
}
],
yAxes: [{
display: true,
ticks: {
callback: function (tickValue, index, ticks) {
"use strict";
return accounting.formatMoney(tickValue);
},
beginAtZero: true
}
}]
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
"use strict";
return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel);
}
}
}
};
var defaultPieOptions = {
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
"use strict";
var value = data.datasets[0].data[tooltipItem.index];
return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value);
}
}
},
maintainAspectRatio: true,
responsive: true
};

View File

@@ -47,355 +47,138 @@ Chart.defaults.global.animation.duration = 0;
Chart.defaults.global.responsive = true; Chart.defaults.global.responsive = true;
Chart.defaults.global.maintainAspectRatio = false; Chart.defaults.global.maintainAspectRatio = false;
/*
Set default options:
*/
var defaultAreaOptions = {
scales: {
xAxes: [
{
gridLines: {
display: false
}
}
],
yAxes: [{
display: true,
ticks: {
callback: function (tickValue, index, ticks) {
"use strict";
return accounting.formatMoney(tickValue);
}
}
}]
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
"use strict";
return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel);
}
}
}
};
var defaultPieOptions = {
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
"use strict";
var value = data.datasets[0].data[tooltipItem.index];
return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value);
}
}
},
maintainAspectRatio: true,
responsive: true
};
var defaultLineOptions = {
scales: {
xAxes: [
{
gridLines: {
display: false
}
}
],
yAxes: [{
display: true,
ticks: {
callback: function (tickValue, index, ticks) {
"use strict";
return accounting.formatMoney(tickValue);
}
}
}]
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
"use strict";
return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel);
}
}
}
};
var defaultColumnOptions = {
scales: {
xAxes: [
{
gridLines: {
display: false
}
}
],
yAxes: [{
ticks: {
callback: function (tickValue, index, ticks) {
"use strict";
return accounting.formatMoney(tickValue);
},
beginAtZero: true
}
}]
},
elements: {
line: {
fill: false
}
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
"use strict";
return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel);
}
}
}
};
var defaultStackedColumnOptions = {
stacked: true,
scales: {
xAxes: [{
stacked: true,
gridLines: {
display: false
}
}],
yAxes: [{
stacked: true,
ticks: {
callback: function (tickValue, index, ticks) {
"use strict";
return accounting.formatMoney(tickValue);
}
}
}]
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
"use strict";
return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel);
}
}
}
};
/** /**
* Function to draw a line chart: *
* @param URL * @param data
* @param container * @returns {{}}
* @param options
*/ */
function lineChart(URL, container, options) { function colorizeData(data) {
"use strict"; var newData = {};
$.getJSON(URL).done(function (data) { newData.datasets = [];
var ctx = document.getElementById(container).getContext("2d"); for (var i = 0; i < data.count; i++) {
var newData = {}; newData.labels = data.labels;
newData.datasets = []; var dataset = data.datasets[i];
dataset.backgroundColor = fillColors[i];
for (var i = 0; i < data.count; i++) { newData.datasets.push(dataset);
newData.labels = data.labels; }
var dataset = data.datasets[i]; return newData;
dataset.backgroundColor = fillColors[i];
newData.datasets.push(dataset);
}
new Chart(ctx, {
type: 'line',
data: data,
options: defaultLineOptions
});
}).fail(function () {
$('#' + container).addClass('general-chart-error');
});
console.log('URL for line chart : ' + URL);
} }
/** /**
* Function to draw an area chart: * @param URI
*
* @param URL
* @param container * @param container
* @param chartType
* @param options * @param options
* @param colorData
*/ */
function areaChart(URL, container, options) { function drawAChart(URI, container, chartType, options, colorData) {
"use strict";
if ($('#' + container).length === 0) { if ($('#' + container).length === 0) {
console.log('No container called ' + container + ' was found.'); console.log('No container called ' + container + ' was found.');
return; return;
} }
$.getJSON(URL).done(function (data) { // var result = true;
var ctx = document.getElementById(container).getContext("2d"); // if (options.beforeDraw) {
var newData = {}; // result = options.beforeDraw(data, {url: URL, container: container});
newData.datasets = []; // }
// if (result === false) {
// return;
// }
for (var i = 0; i < data.count; i++) { $.getJSON(URI).done(function (data) {
newData.labels = data.labels;
var dataset = data.datasets[i]; if (colorData) {
dataset.backgroundColor = fillColors[i]; data = colorizeData(data);
newData.datasets.push(dataset);
} }
new Chart(ctx, {
type: 'line',
data: newData,
options: defaultAreaOptions
});
}).fail(function () {
$('#' + container).addClass('general-chart-error');
});
console.log('URL for area chart: ' + URL);
}
/**
*
* @param URL
* @param container
* @param options
*/
function columnChart(URL, container, options) {
"use strict";
options = options || {};
$.getJSON(URL).done(function (data) {
var result = true;
if (options.beforeDraw) {
result = options.beforeDraw(data, {url: URL, container: container});
}
if (result === false) {
return;
}
console.log('Will draw columnChart(' + URL + ')');
var ctx = document.getElementById(container).getContext("2d");
var newData = {};
newData.datasets = [];
for (var i = 0; i < data.count; i++) {
newData.labels = data.labels;
var dataset = data.datasets[i];
dataset.backgroundColor = fillColors[i];
newData.datasets.push(dataset);
}
new Chart(ctx, {
type: 'bar',
data: data,
options: defaultColumnOptions
});
}).fail(function () {
$('#' + container).addClass('general-chart-error');
});
console.log('URL for column chart : ' + URL);
}
/**
*
* @param URL
* @param container
* @param options
*/
function stackedColumnChart(URL, container, options) {
"use strict";
options = options || {};
$.getJSON(URL).done(function (data) {
var result = true;
if (options.beforeDraw) {
result = options.beforeDraw(data, {url: URL, container: container});
}
if (result === false) {
return;
}
var ctx = document.getElementById(container).getContext("2d");
var newData = {};
newData.datasets = [];
for (var i = 0; i < data.count; i++) {
newData.labels = data.labels;
var dataset = data.datasets[i];
dataset.backgroundColor = fillColors[i];
newData.datasets.push(dataset);
}
new Chart(ctx, {
type: 'bar',
data: data,
options: defaultStackedColumnOptions
});
}).fail(function () {
$('#' + container).addClass('general-chart-error');
});
console.log('URL for stacked column chart : ' + URL);
}
/**
*
* @param URL
* @param container
* @param options
*/
function pieChart(URL, container, options) {
"use strict";
if ($('#' + container).length === 0) {
console.log('No container called ' + container + ' was found.');
return;
}
$.getJSON(URL).done(function (data) {
if (allCharts.hasOwnProperty(container)) { if (allCharts.hasOwnProperty(container)) {
console.log('Will draw updated pie chart'); console.log('Will draw updated ' + chartType + ' chart');
allCharts[container].data.datasets = data.datasets; allCharts[container].data.datasets = data.datasets;
allCharts[container].data.labels = data.labels; allCharts[container].data.labels = data.labels;
allCharts[container].update(); allCharts[container].update();
} else { } else {
// new chart! // new chart!
console.log('Will draw new pie chart'); console.log('Will draw new ' + chartType + 'chart');
var ctx = document.getElementById(container).getContext("2d"); var ctx = document.getElementById(container).getContext("2d");
allCharts[container] = new Chart(ctx, { allCharts[container] = new Chart(ctx, {
type: 'pie', type: chartType,
data: data, data: data,
options: defaultPieOptions options: options
}); });
} }
}).fail(function () { }).fail(function () {
console.log('Failed to draw ' + chartType + ' in container ' + container);
$('#' + container).addClass('general-chart-error'); $('#' + container).addClass('general-chart-error');
}); });
console.log('URL for ' + chartType + ' chart : ' + URL);
}
console.log('URL for pie chart : ' + URL); /**
* Function to draw a line chart:
* @param URI
* @param container
*/
function lineChart(URI, container) {
"use strict";
var colorData = true;
var options = defaultChartOptions;
var chartType = 'line';
drawAChart(URI, container, chartType, options, colorData);
}
/**
*
* @param URI
* @param container
*/
function columnChart(URI, container) {
"use strict";
var colorData = true;
var options = defaultChartOptions;
var chartType = 'bar';
drawAChart(URI, container, chartType, options, colorData);
}
/**
*
* @param URI
* @param container
*/
function stackedColumnChart(URI, container) {
"use strict";
var colorData = true;
var options = defaultChartOptions;
options.stacked = true;
options.scales.xAxes[0].stacked = true;
var chartType = 'bar';
drawAChart(URI, container, chartType, options, colorData);
}
/**
*
* @param URI
* @param container
*/
function pieChart(URI, container) {
"use strict";
var colorData = false;
var options = defaultPieOptions;
var chartType = 'pie';
drawAChart(URI, container, chartType, options, colorData);
} }

View File

@@ -1,4 +1,4 @@
/* globals $, columnChart,showTour, Tour, google, lineChart, pieChart, stackedColumnChart, areaChart */ /* globals $, columnChart,showTour, Tour, google, pieChart, stackedColumnChart */
$(function () { $(function () {
"use strict"; "use strict";
@@ -32,38 +32,38 @@ function endTheTour() {
function drawChart() { function drawChart() {
"use strict"; "use strict";
areaChart('chart/account/frontpage', 'accounts-chart'); lineChart('chart/account/frontpage', 'accounts-chart');
pieChart('chart/bill/frontpage', 'bills-chart'); pieChart('chart/bill/frontpage', 'bills-chart');
stackedColumnChart('chart/budget/frontpage', 'budgets-chart', {beforeDraw: beforeDrawIsEmpty}); stackedColumnChart('chart/budget/frontpage', 'budgets-chart');
columnChart('chart/category/frontpage', 'categories-chart', {beforeDraw: beforeDrawIsEmpty}); columnChart('chart/category/frontpage', 'categories-chart');
columnChart('chart/account/expense', 'expense-accounts-chart', {beforeDraw: beforeDrawIsEmpty}); columnChart('chart/account/expense', 'expense-accounts-chart');
columnChart('chart/account/revenue', 'revenue-accounts-chart', {beforeDraw: beforeDrawIsEmpty}); columnChart('chart/account/revenue', 'revenue-accounts-chart');
getBoxAmounts(); getBoxAmounts();
} }
/** // /**
* Removes a chart container if there is nothing for the chart to draw. // * Removes a chart box if there is nothing for the chart to draw.
* // *
* @param data // * @param data
* @param options // * @param options
* @returns {boolean} // * @returns {boolean}
*/ // */
function beforeDrawIsEmpty(data, options) { // function beforeDrawIsEmpty(data, options) {
"use strict"; // "use strict";
//
// check if chart holds data. // // check if chart holds data.
if (data.labels.length === 0) { // if (data.labels.length === 0) {
// remove the chart container + parent // // remove the chart container + parent
console.log(options.container + ' appears empty. Removed.'); // console.log(options.container + ' appears empty. Removed.');
$('#' + options.container).parent().parent().remove(); // $('#' + options.container).parent().parent().remove();
//
// return false so script stops. // // return false so script stops.
return false; // return false;
} // }
return true; // return true;
} // }
function getBoxAmounts() { function getBoxAmounts() {

View File

@@ -130,63 +130,9 @@ function clickBudgetChart(e) {
"use strict"; "use strict";
var link = $(e.target); var link = $(e.target);
var budgetId = link.data('budget'); var budgetId = link.data('budget');
var URL = 'chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds; var URL = 'chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds;
var container = 'budget_chart'; var container = 'budget_chart';
// if chart drawn is false, draw the first one, then columnChart(URL, container);
// set to true
if (chartDrawn == false) {
// do new chart:
$.getJSON(URL).done(function (data) {
console.log('Will draw new columnChart(' + URL + ')');
var ctx = document.getElementById(container).getContext("2d");
var newData = {};
newData.datasets = [];
for (var i = 0; i < data.count; i++) {
newData.labels = data.labels;
var dataset = data.datasets[i];
dataset.backgroundColor = fillColors[i];
newData.datasets.push(dataset);
}
// completely new chart.
budgetChart = new Chart(ctx, {
type: 'bar',
data: data,
options: defaultColumnOptions
});
}).fail(function () {
$('#' + container).addClass('general-chart-error');
});
console.log('URL for column chart : ' + URL);
chartDrawn = true;
} else {
console.log('Will now handle remove data and add new!');
$.getJSON(URL).done(function (data) {
console.log('Will draw updated columnChart(' + URL + ')');
var newData = {};
newData.datasets = [];
for (var i = 0; i < data.count; i++) {
newData.labels = data.labels;
var dataset = data.datasets[i];
dataset.backgroundColor = fillColors[i];
newData.datasets.push(dataset);
}
// update the chart
console.log('Now update chart thing.');
budgetChart.data.datasets = newData.datasets;
budgetChart.update();
}).fail(function () {
$('#' + container).addClass('general-chart-error');
});
}
return false; return false;
} }

View File

@@ -12,7 +12,7 @@ function drawChart() {
"use strict"; "use strict";
// income and expense over multi year: // income and expense over multi year:
lineChart('chart/report/net-worth/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'net-worth'); lineChart('chart/report/net-worth/' + startDate + '/' + endDate + '/' + accountIds, 'net-worth');
columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); columnChart('chart/report/in-out/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); columnChart('chart/report/in-out-sum/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
} }

View File

@@ -1,10 +1,7 @@
/* globals google, accountIds, budgetYearOverviewUri */ /* globals google, accountIds, budgetYearOverviewUri */
var chartDrawn;
var budgetChart;
$(function () { $(function () {
"use strict"; "use strict";
chartDrawn = false;
drawChart(); drawChart();
loadAjaxPartial('budgetOverview',budgetYearOverviewUri); loadAjaxPartial('budgetOverview',budgetYearOverviewUri);

View File

@@ -18,7 +18,8 @@ $(function () {
); );
// set values from cookies, if any: // set values from cookies, if any:
if (readCookie('report-type') !== null) { if (!(readCookie('report-type') === null)) {
console.log(readCookie('report-type'));
$('select[name="report_type"]').val(readCookie('report-type')); $('select[name="report_type"]').val(readCookie('report-type'));
} }

View File

@@ -81,6 +81,7 @@
var accountID = {{ account.id }}; var accountID = {{ account.id }};
</script> </script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script src="js/lib/jquery-ui.min.js" type="text/javascript"></script> <script src="js/lib/jquery-ui.min.js" type="text/javascript"></script>
<script src="js/lib/jquery.color-2.1.2.min.js" type="text/javascript"></script> <script src="js/lib/jquery.color-2.1.2.min.js" type="text/javascript"></script>

View File

@@ -55,6 +55,7 @@
var dateString = "{{ date }}"; var dateString = "{{ date }}";
</script> </script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script src="js/lib/jquery-ui.min.js" type="text/javascript"></script> <script src="js/lib/jquery-ui.min.js" type="text/javascript"></script>
<script src="js/lib/jquery.color-2.1.2.min.js" type="text/javascript"></script> <script src="js/lib/jquery.color-2.1.2.min.js" type="text/javascript"></script>

View File

@@ -119,6 +119,7 @@
var billID = {{ bill.id }}; var billID = {{ bill.id }};
</script> </script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript" src="js/ff/bills/show.js"></script> <script type="text/javascript" src="js/ff/bills/show.js"></script>
<script type="text/javascript" src="js/ff/transactions/list.js"></script> <script type="text/javascript" src="js/ff/transactions/list.js"></script>

View File

@@ -107,6 +107,7 @@
</script> </script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript" src="js/ff/budgets/show.js"></script> <script type="text/javascript" src="js/ff/budgets/show.js"></script>
<script type="text/javascript" src="js/ff/transactions/list.js"></script> <script type="text/javascript" src="js/ff/transactions/list.js"></script>

View File

@@ -35,6 +35,7 @@
{% block scripts %} {% block scripts %}
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript" src="js/lib/bootstrap-sortable.js"></script> <script type="text/javascript" src="js/lib/bootstrap-sortable.js"></script>
<script type="text/javascript" src="js/ff/categories/index.js"></script> <script type="text/javascript" src="js/ff/categories/index.js"></script>

View File

@@ -76,6 +76,7 @@
var categoryID = {{ category.id }}; var categoryID = {{ category.id }};
</script> </script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript" src="js/ff/categories/show.js"></script> <script type="text/javascript" src="js/ff/categories/show.js"></script>
<script type="text/javascript" src="js/ff/transactions/list.js"></script> <script type="text/javascript" src="js/ff/transactions/list.js"></script>

View File

@@ -44,6 +44,7 @@
var categoryDate = "{{ carbon.format('Y-m-d') }}"; var categoryDate = "{{ carbon.format('Y-m-d') }}";
</script> </script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript" src="js/ff/categories/show_with_date.js"></script> <script type="text/javascript" src="js/ff/categories/show_with_date.js"></script>
<script type="text/javascript" src="js/ff/transactions/list.js"></script> <script type="text/javascript" src="js/ff/transactions/list.js"></script>

View File

@@ -132,6 +132,7 @@
</script> </script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript" src="js/ff/index.js"></script> <script type="text/javascript" src="js/ff/index.js"></script>
{% endblock %} {% endblock %}

View File

@@ -107,6 +107,7 @@
</script> </script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript" src="js/ff/piggy-banks/show.js"></script> <script type="text/javascript" src="js/ff/piggy-banks/show.js"></script>
{% endblock %} {% endblock %}

View File

@@ -264,6 +264,7 @@
{% block scripts %} {% block scripts %}
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript"> <script type="text/javascript">

View File

@@ -120,6 +120,7 @@
<script type="text/javascript" src="js/lib/bootstrap-sortable.js"></script> <script type="text/javascript" src="js/lib/bootstrap-sortable.js"></script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var year = {{ start.year }}; var year = {{ start.year }};

View File

@@ -106,6 +106,7 @@
<script type="text/javascript" src="js/lib/bootstrap-sortable.js"></script> <script type="text/javascript" src="js/lib/bootstrap-sortable.js"></script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var year = {{ start.year }}; var year = {{ start.year }};

View File

@@ -112,6 +112,7 @@
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script> <script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>
<script type="text/javascript" src="js/ff/charts.defaults.js"></script>
<script type="text/javascript" src="js/ff/charts.js"></script> <script type="text/javascript" src="js/ff/charts.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var year = '{{ start.year }}'; var year = '{{ start.year }}';