diff --git a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php
index b3b22e11b5..f1ef92aa97 100644
--- a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php
+++ b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php
@@ -101,7 +101,6 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
];
$current = clone $start;
- $today = new Carbon;
while ($end >= $current) {
$data['labels'][] = $current->formatLocalized($format);
diff --git a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php
index 2c0a3e0f2e..4759744124 100644
--- a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php
+++ b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php
@@ -3,7 +3,9 @@
namespace FireflyIII\Generator\Chart\Budget;
+use Config;
use Illuminate\Support\Collection;
+use Preferences;
/**
* Class ChartJsBudgetChartGenerator
@@ -20,6 +22,29 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
*/
public function budget(Collection $entries)
{
+ $data = [
+ 'count' => 1,
+ 'labels' => [],
+ 'datasets' => [
+ [
+ 'label' => 'Amount',
+ 'data' => [],
+ ]
+ ],
+ ];
+
+ // language:
+ $language = Preferences::get('language', 'en')->data;
+ $format = Config::get('firefly.month.' . $language);
+
+ /** @var array $entry */
+ foreach ($entries as $entry) {
+ $data['labels'][] = $entry[0]->formatLocalized($format);
+ $data['datasets'][0]['data'][] = $entry[1];
+
+ }
+
+ return $data;
}
/**
@@ -29,6 +54,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
*/
public function budgetLimit(Collection $entries)
{
+ return $this->budget($entries);
}
/**
@@ -55,14 +81,14 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
$spent = [];
$overspent = [];
$amount = [];
- $expenses = [];
+ $expenses = [];
foreach ($entries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$left[] = round($entry[1], 2);
$spent[] = round($entry[2], 2);
$overspent[] = round($entry[3], 2);
$amount[] = round($entry[4], 2);
- $expenses[] = round($entry[5], 2);
+ $expenses[] = round($entry[5], 2);
//$data['count']++;
}
}
@@ -87,5 +113,50 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
*/
public function year(Collection $budgets, Collection $entries)
{
+ // language:
+ $language = Preferences::get('language', 'en')->data;
+ $format = Config::get('firefly.month.' . $language);
+
+ $data = [
+ 'count' => 0,
+ 'labels' => [],
+ 'datasets' => [],
+ ];
+
+ foreach ($budgets as $budget) {
+ $data['labels'][] = $budget->name;
+ $data['count']++;
+ }
+ /** @var array $entry */
+ foreach ($entries as $entry) {
+ $array = [
+ 'label' => $entry[0]->formatLocalized($format),
+ 'data' => [],
+ ];
+ array_shift($entry);
+ $array['data'] = $entry;
+ $data['datasets'][] = $array;
+
+ }
+
+ return $data;
+
+
+ $chart = new GChart;
+ // add columns:
+ $chart->addColumn(trans('firefly.month'), 'date');
+ foreach ($budgets as $budget) {
+ $chart->addColumn($budget->name, 'number');
+ }
+
+ /** @var array $entry */
+ foreach ($entries as $entry) {
+
+ $chart->addRowArray($entry);
+ }
+
+ $chart->generate();
+
+ return $chart->getData();
}
}
\ No newline at end of file
diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php
index 3eea5b5218..74c004477a 100644
--- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php
+++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php
@@ -2,7 +2,9 @@
namespace FireflyIII\Generator\Chart\Category;
+use Config;
use Illuminate\Support\Collection;
+use Preferences;
/**
@@ -20,6 +22,27 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
*/
public function all(Collection $entries)
{
+ // language:
+ $language = Preferences::get('language', 'en')->data;
+ $format = Config::get('firefly.month.' . $language);
+
+ $data = [
+ 'count' => 1,
+ 'labels' => [],
+ 'datasets' => [
+ [
+ 'label' => 'Spent',
+ 'data' => []
+ ]
+ ],
+ ];
+
+ foreach ($entries as $entry) {
+ $data['labels'][] = $entry[0]->formatLocalized($format);
+ $data['datasets'][0]['data'][] = round($entry[1], 2);
+ }
+
+ return $data;
}
/**
@@ -42,7 +65,7 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
foreach ($entries as $entry) {
if ($entry['sum'] != 0) {
$data['labels'][] = $entry['name'];
- $data['datasets'][0]['data'][] = round($entry['sum'],2);
+ $data['datasets'][0]['data'][] = round($entry['sum'], 2);
}
}
@@ -56,6 +79,8 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
*/
public function month(Collection $entries)
{
+ return $this->all($entries);
+
}
/**
@@ -66,5 +91,29 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
*/
public function year(Collection $categories, Collection $entries)
{
+
+ // language:
+ $language = Preferences::get('language', 'en')->data;
+ $format = Config::get('firefly.month.' . $language);
+
+ $data = [
+ 'count' => 0,
+ 'labels' => [],
+ 'datasets' => [],
+ ];
+
+ foreach ($categories as $category) {
+ $data['labels'][] = $category->name;
+ }
+
+ foreach ($entries as $entry) {
+ $date = $entry[0]->formatLocalized($format);
+ array_shift($entry);
+ $data['count']++;
+ $data['datasets'][] = ['label' => $date, 'data' => $entry];
+ }
+
+ return $data;
+
}
}
\ No newline at end of file
diff --git a/app/Generator/Chart/Report/ChartJsReportChartGenerator.php b/app/Generator/Chart/Report/ChartJsReportChartGenerator.php
new file mode 100644
index 0000000000..de51c0e6f2
--- /dev/null
+++ b/app/Generator/Chart/Report/ChartJsReportChartGenerator.php
@@ -0,0 +1,87 @@
+data;
+ $format = Config::get('firefly.month.' . $language);
+
+ $data = [
+ 'count' => 2,
+ 'labels' => [],
+ 'datasets' => [
+ [
+ 'label' => trans('firefly.income'),
+ 'data' => []
+ ],
+ [
+ 'label' => trans('firefly.expenses'),
+ 'data' => []
+ ]
+ ],
+ ];
+
+ foreach ($entries as $entry) {
+ $data['labels'][] = $entry[0]->formatLocalized($format);
+ $data['datasets'][0]['data'][] = round($entry[1], 2);
+ $data['datasets'][1]['data'][] = round($entry[2], 2);
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param string $income
+ * @param string $expense
+ * @param int $count
+ *
+ * @return array
+ */
+ public function yearInOutSummarized($income, $expense, $count)
+ {
+
+ // language:
+ $language = Preferences::get('language', 'en')->data;
+ $format = Config::get('firefly.month.' . $language);
+
+ $data = [
+ 'count' => 2,
+ 'labels' => [],
+ 'datasets' => [
+ [
+ 'label' => trans('firefly.income'),
+ 'data' => []
+ ],
+ [
+ 'label' => trans('firefly.expenses'),
+ 'data' => []
+ ]
+ ],
+ ];
+ $data['datasets'][0]['data'][] = round($income, 2);
+ $data['datasets'][1]['data'][] = round($expense, 2);
+ $data['datasets'][0]['data'][] = round(($income / $count), 2);
+ $data['datasets'][1]['data'][] = round(($expense / $count), 2);
+ return $data;
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php
index edccfd042f..b63119a092 100644
--- a/app/Http/Controllers/Chart/BudgetController.php
+++ b/app/Http/Controllers/Chart/BudgetController.php
@@ -60,7 +60,7 @@ class BudgetController extends Controller
$cache->addProperty($last);
$cache->addProperty('budget');
if ($cache->has()) {
- return Response::json($cache->get()); // @codeCoverageIgnore
+ //return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
@@ -105,7 +105,7 @@ class BudgetController extends Controller
$cache->addProperty($budget->id);
$cache->addProperty($repetition->id);
if ($cache->has()) {
- return Response::json($cache->get()); // @codeCoverageIgnore
+ //return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
@@ -215,7 +215,7 @@ class BudgetController extends Controller
$cache->addProperty('budget');
$cache->addProperty('year');
if ($cache->has()) {
- return Response::json($cache->get()); // @codeCoverageIgnore
+ //return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php
index cd8a01cfb3..57ca30bfd8 100644
--- a/app/Http/Controllers/Chart/CategoryController.php
+++ b/app/Http/Controllers/Chart/CategoryController.php
@@ -62,7 +62,7 @@ class CategoryController extends Controller
$cache->addProperty('all');
$cache->addProperty('categories');
if ($cache->has()) {
- return Response::json($cache->get()); // @codeCoverageIgnore
+ //return Response::json($cache->get()); // @codeCoverageIgnore
}
while ($start <= $end) {
@@ -184,7 +184,7 @@ class CategoryController extends Controller
$cache->addProperty('category');
$cache->addProperty('year');
if ($cache->has()) {
- return Response::json($cache->get()); // @codeCoverageIgnore
+ //return Response::json($cache->get()); // @codeCoverageIgnore
}
$shared = $shared == 'shared' ? true : false;
diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php
index 7e1b435e8d..120126dc59 100644
--- a/app/Http/Controllers/Chart/ReportController.php
+++ b/app/Http/Controllers/Chart/ReportController.php
@@ -55,7 +55,7 @@ class ReportController extends Controller
$cache->addProperty($year);
$cache->addProperty($shared);
if ($cache->has()) {
- return Response::json($cache->get()); // @codeCoverageIgnore
+ //return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
@@ -95,7 +95,7 @@ class ReportController extends Controller
$cache->addProperty($year);
$cache->addProperty($shared);
if ($cache->has()) {
- return Response::json($cache->get()); // @codeCoverageIgnore
+ //return Response::json($cache->get()); // @codeCoverageIgnore
}
$start = new Carbon($year . '-01-01');
diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php
index 857361af76..686953c1a7 100644
--- a/app/Providers/FireflyServiceProvider.php
+++ b/app/Providers/FireflyServiceProvider.php
@@ -107,8 +107,8 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator', 'FireflyIII\Generator\Chart\PiggyBank\GooglePiggyBankChartGenerator');
//$this->app->bind('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator', 'FireflyIII\Generator\Chart\PiggyBank\GooglePiggyBankChartGenerator');
- $this->app->bind('FireflyIII\Generator\Chart\Report\ReportChartGenerator', 'FireflyIII\Generator\Chart\Report\GoogleReportChartGenerator');
//$this->app->bind('FireflyIII\Generator\Chart\Report\ReportChartGenerator', 'FireflyIII\Generator\Chart\Report\GoogleReportChartGenerator');
+ $this->app->bind('FireflyIII\Generator\Chart\Report\ReportChartGenerator', 'FireflyIII\Generator\Chart\Report\ChartJsReportChartGenerator');
$this->app->bind('FireflyIII\Helpers\Help\HelpInterface', 'FireflyIII\Helpers\Help\Help');
diff --git a/public/js/Chart.StackedBar.js b/public/js/Chart.StackedBar.js
new file mode 100644
index 0000000000..71b50ddf11
--- /dev/null
+++ b/public/js/Chart.StackedBar.js
@@ -0,0 +1,556 @@
+(function (factory) {
+ "use strict";
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['chart.js'], factory);
+ } else if (typeof exports === 'object') {
+ // Node/CommonJS
+ module.exports = factory(require('chart.js'));
+ } else {
+ // Global browser
+ factory(Chart);
+ }
+}(function (Chart) {
+ "use strict";
+
+ var helpers = Chart.helpers;
+
+ var defaultConfig = {
+ 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,
+
+ //Boolean - Whether bars should be rendered on a percentage base
+ relativeBars : false,
+
+ //String - A legend template
+ legendTemplate : "
-legend\"><% for (var i=0; i- \"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
<%}%>
",
+
+ //Boolean - Show total legend
+ showTotal: false,
+
+ //String - Color of total legend
+ totalColor: '#fff',
+
+ //String - Total Label
+ totalLabel: 'Total',
+
+ //Boolean - Hide labels with value set to 0
+ tooltipHideZero: false
+ };
+
+ Chart.Type.extend({
+ name: "StackedBar",
+ defaults : defaultConfig,
+ initialize: function(data){
+ //Expose options as a scope variable here so we can access it in the ScaleClass
+ var options = this.options;
+
+ // Save data as a source for updating of values & methods
+ this.data = data;
+
+ this.ScaleClass = Chart.Scale.extend({
+ offsetGridLines : true,
+ calculateBarX : function(barIndex){
+ return this.calculateX(barIndex);
+ },
+ calculateBarY : function(datasets, dsIndex, barIndex, value){
+ var offset = 0,
+ sum = 0;
+
+ for(var i = 0; i < datasets.length; i++) {
+ sum += datasets[i].bars[barIndex].value;
+ }
+ for(i = dsIndex; i < datasets.length; i++) {
+ if(i === dsIndex && value) {
+ offset += value;
+ } else {
+ offset = +offset + +datasets[i].bars[barIndex].value;
+ }
+ }
+
+ if(options.relativeBars) {
+ offset = offset / sum * 100;
+ }
+
+ return this.calculateY(offset);
+ },
+ calculateBaseWidth : function(){
+ return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
+ },
+ calculateBaseHeight : function(){
+ return (this.calculateY(1) - this.calculateY(0));
+ },
+ calculateBarWidth : function(datasetCount){
+ //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
+ return this.calculateBaseWidth();
+ },
+ calculateBarHeight : function(datasets, dsIndex, barIndex, value) {
+ var sum = 0;
+
+ for(var i = 0; i < datasets.length; i++) {
+ sum += datasets[i].bars[barIndex].value;
+ }
+
+ if(!value) {
+ value = datasets[dsIndex].bars[barIndex].value;
+ }
+
+ if(options.relativeBars) {
+ value = value / sum * 100;
+ }
+
+ return this.calculateY(value);
+ }
+ });
+
+ this.datasets = [];
+
+ //Set up tooltip events on the chart
+ if (this.options.showTooltips){
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
+ var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
+
+ this.eachBars(function(bar){
+ bar.restore(['fillColor', 'strokeColor']);
+ });
+ helpers.each(activeBars, function(activeBar){
+ activeBar.fillColor = activeBar.highlightFill;
+ activeBar.strokeColor = activeBar.highlightStroke;
+ });
+ this.showTooltip(activeBars);
+ });
+ }
+
+ //Declare the extension of the default point, to cater for the options passed in to the constructor
+ this.BarClass = Chart.Rectangle.extend({
+ strokeWidth : this.options.barStrokeWidth,
+ showStroke : this.options.barShowStroke,
+ ctx : this.chart.ctx
+ });
+
+ //Iterate through each of the datasets, and build this into a property of the chart
+ helpers.each(data.datasets,function(dataset,datasetIndex){
+
+ var datasetObject = {
+ label : dataset.label || null,
+ fillColor : dataset.fillColor,
+ strokeColor : dataset.strokeColor,
+ bars : []
+ };
+
+ this.datasets.push(datasetObject);
+
+ helpers.each(dataset.data,function(dataPoint,index){
+ if(!helpers.isNumber(dataPoint)){
+ dataPoint = 0;
+ }
+ //Add a new point for each piece of data, passing any required data to draw.
+ //Add 0 as value if !isNumber (e.g. empty values are useful when 0 values should be hidden in tooltip)
+ datasetObject.bars.push(new this.BarClass({
+ value : dataPoint,
+ label : data.labels[index],
+ datasetLabel: dataset.label,
+ strokeColor : dataset.strokeColor,
+ fillColor : dataset.fillColor,
+ highlightFill : dataset.highlightFill || dataset.fillColor,
+ highlightStroke : dataset.highlightStroke || dataset.strokeColor
+ }));
+ },this);
+
+ },this);
+
+ this.buildScale(data.labels);
+
+ this.eachBars(function(bar, index, datasetIndex){
+ helpers.extend(bar, {
+ base: this.scale.endPoint,
+ height: 0,
+ width : this.scale.calculateBarWidth(this.datasets.length),
+ x: this.scale.calculateBarX(index),
+ y: this.scale.endPoint
+ });
+ bar.save();
+ }, this);
+
+ this.render();
+ },
+ showTooltip : function(ChartElements, forceRedraw){
+ // Only redraw the chart if we've actually changed what we're hovering on.
+ if (typeof this.activeElements === 'undefined') this.activeElements = [];
+
+ helpers = Chart.helpers;
+
+ var isChanged = (function(Elements){
+ var changed = false;
+
+ if (Elements.length !== this.activeElements.length){
+ changed = true;
+ return changed;
+ }
+
+ helpers.each(Elements, function(element, index){
+ if (element !== this.activeElements[index]){
+ changed = true;
+ }
+ }, this);
+ return changed;
+ }).call(this, ChartElements);
+
+ if (!isChanged && !forceRedraw){
+ return;
+ }
+ else{
+ this.activeElements = ChartElements;
+ }
+ this.draw();
+ if(this.options.customTooltips){
+ this.options.customTooltips(false);
+ }
+ if (ChartElements.length > 0){
+ // If we have multiple datasets, show a MultiTooltip for all of the data points at that index
+ if (this.datasets && this.datasets.length > 1) {
+ var dataArray,
+ dataIndex;
+
+ for (var i = this.datasets.length - 1; i >= 0; i--) {
+ dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;
+ dataIndex = helpers.indexOf(dataArray, ChartElements[0]);
+ if (dataIndex !== -1){
+ break;
+ }
+ }
+ var tooltipLabels = [],
+ tooltipColors = [],
+ medianPosition = (function(index) {
+
+ // Get all the points at that particular index
+ var Elements = [],
+ dataCollection,
+ xPositions = [],
+ yPositions = [],
+ xMax,
+ yMax,
+ xMin,
+ yMin;
+ helpers.each(this.datasets, function(dataset){
+ dataCollection = dataset.points || dataset.bars || dataset.segments;
+ if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){
+ Elements.push(dataCollection[dataIndex]);
+ }
+ });
+
+ var total = {
+ datasetLabel: this.options.totalLabel,
+ value: 0,
+ fillColor: this.options.totalColor,
+ strokeColor: this.options.totalColor
+ };
+
+ helpers.each(Elements, function(element) {
+ if (this.options.tooltipHideZero && element.value === 0) {
+ return;
+ }
+
+ xPositions.push(element.x);
+ yPositions.push(element.y);
+
+ total.value += element.value;
+
+ //Include any colour information about the element
+ tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));
+ tooltipColors.push({
+ fill: element._saved.fillColor || element.fillColor,
+ stroke: element._saved.strokeColor || element.strokeColor
+ });
+
+ }, this);
+
+ if (this.options.showTotal) {
+ tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, total));
+ tooltipColors.push({
+ fill: total.fillColor,
+ stroke: total.strokeColor
+ });
+ }
+
+ yMin = helpers.min(yPositions);
+ yMax = helpers.max(yPositions);
+
+ xMin = helpers.min(xPositions);
+ xMax = helpers.max(xPositions);
+
+ return {
+ x: (xMin > this.chart.width/2) ? xMin : xMax,
+ y: (yMin + yMax)/2
+ };
+ }).call(this, dataIndex);
+
+ new Chart.MultiTooltip({
+ x: medianPosition.x,
+ y: medianPosition.y,
+ xPadding: this.options.tooltipXPadding,
+ yPadding: this.options.tooltipYPadding,
+ xOffset: this.options.tooltipXOffset,
+ fillColor: this.options.tooltipFillColor,
+ textColor: this.options.tooltipFontColor,
+ fontFamily: this.options.tooltipFontFamily,
+ fontStyle: this.options.tooltipFontStyle,
+ fontSize: this.options.tooltipFontSize,
+ titleTextColor: this.options.tooltipTitleFontColor,
+ titleFontFamily: this.options.tooltipTitleFontFamily,
+ titleFontStyle: this.options.tooltipTitleFontStyle,
+ titleFontSize: this.options.tooltipTitleFontSize,
+ cornerRadius: this.options.tooltipCornerRadius,
+ labels: tooltipLabels,
+ legendColors: tooltipColors,
+ legendColorBackground : this.options.multiTooltipKeyBackground,
+ title: ChartElements[0].label,
+ chart: this.chart,
+ ctx: this.chart.ctx,
+ custom: this.options.customTooltips
+ }).draw();
+
+ } else {
+ helpers.each(ChartElements, function(Element) {
+ var tooltipPosition = Element.tooltipPosition();
+ new Chart.Tooltip({
+ x: Math.round(tooltipPosition.x),
+ y: Math.round(tooltipPosition.y),
+ xPadding: this.options.tooltipXPadding,
+ yPadding: this.options.tooltipYPadding,
+ fillColor: this.options.tooltipFillColor,
+ textColor: this.options.tooltipFontColor,
+ fontFamily: this.options.tooltipFontFamily,
+ fontStyle: this.options.tooltipFontStyle,
+ fontSize: this.options.tooltipFontSize,
+ caretHeight: this.options.tooltipCaretSize,
+ cornerRadius: this.options.tooltipCornerRadius,
+ text: helpers.template(this.options.tooltipTemplate, Element),
+ chart: this.chart,
+ custom: this.options.customTooltips
+ }).draw();
+ }, this);
+ }
+ }
+ return this;
+ },
+ update : function(){
+
+ //Iterate through each of the datasets, and build this into a property of the chart
+ helpers.each(this.data.datasets,function(dataset,datasetIndex){
+
+ helpers.extend(this.datasets[datasetIndex], {
+ label : dataset.label || null,
+ fillColor : dataset.fillColor,
+ strokeColor : dataset.strokeColor,
+ });
+
+ helpers.each(dataset.data,function(dataPoint,index){
+ helpers.extend(this.datasets[datasetIndex].bars[index], {
+ value : dataPoint,
+ label : this.data.labels[index],
+ datasetLabel: dataset.label,
+ strokeColor : dataset.strokeColor,
+ fillColor : dataset.fillColor,
+ highlightFill : dataset.highlightFill || dataset.fillColor,
+ highlightStroke : dataset.highlightStroke || dataset.strokeColor
+ });
+ },this);
+
+ },this);
+
+
+ this.scale.update();
+ // Reset any highlight colours before updating.
+ helpers.each(this.activeElements, function(activeElement){
+ activeElement.restore(['fillColor', 'strokeColor']);
+ });
+
+ this.eachBars(function(bar){
+ bar.save();
+ });
+ this.render();
+ },
+ eachBars : function(callback){
+ helpers.each(this.datasets,function(dataset, datasetIndex){
+ helpers.each(dataset.bars, callback, this, datasetIndex);
+ },this);
+ },
+ getBarsAtEvent : function(e){
+ var barsArray = [],
+ eventPosition = helpers.getRelativePosition(e),
+ datasetIterator = function(dataset){
+ barsArray.push(dataset.bars[barIndex]);
+ },
+ barIndex;
+
+ for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
+ for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
+ if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
+ helpers.each(this.datasets, datasetIterator);
+ return barsArray;
+ }
+ }
+ }
+
+ return barsArray;
+ },
+ buildScale : function(labels){
+ var self = this;
+
+ var dataTotal = function(){
+ var values = [];
+ helpers.each(self.datasets, function(dataset) {
+ helpers.each(dataset.bars, function(bar, barIndex) {
+ if(!values[barIndex]) values[barIndex] = 0;
+ if(self.options.relativeBars) {
+ values[barIndex] = 100;
+ } else {
+ values[barIndex] = +values[barIndex] + +bar.value;
+ }
+ });
+ });
+ return values;
+ };
+
+ var scaleOptions = {
+ templateString : this.options.scaleLabel,
+ height : this.chart.height,
+ width : this.chart.width,
+ ctx : this.chart.ctx,
+ textColor : this.options.scaleFontColor,
+ fontSize : this.options.scaleFontSize,
+ fontStyle : this.options.scaleFontStyle,
+ fontFamily : this.options.scaleFontFamily,
+ valuesCount : labels.length,
+ beginAtZero : this.options.scaleBeginAtZero,
+ integersOnly : this.options.scaleIntegersOnly,
+ calculateYRange: function(currentHeight){
+ var updatedRanges = helpers.calculateScaleRange(
+ dataTotal(),
+ currentHeight,
+ this.fontSize,
+ this.beginAtZero,
+ this.integersOnly
+ );
+ helpers.extend(this, updatedRanges);
+ },
+ xLabels : this.options.xLabels || labels,
+ font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
+ lineWidth : this.options.scaleLineWidth,
+ lineColor : this.options.scaleLineColor,
+ gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
+ gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
+ showHorizontalLines : this.options.scaleShowHorizontalLines,
+ showVerticalLines : this.options.scaleShowVerticalLines,
+ padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
+ showLabels : this.options.scaleShowLabels,
+ display : this.options.showScale
+ };
+
+ if (this.options.scaleOverride){
+ helpers.extend(scaleOptions, {
+ calculateYRange: helpers.noop,
+ steps: this.options.scaleSteps,
+ stepValue: this.options.scaleStepWidth,
+ min: this.options.scaleStartValue,
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
+ });
+ }
+
+ this.scale = new this.ScaleClass(scaleOptions);
+ },
+ addData : function(valuesArray,label){
+ //Map the values array for each of the datasets
+ helpers.each(valuesArray,function(value,datasetIndex){
+ if (helpers.isNumber(value)){
+ //Add a new point for each piece of data, passing any required data to draw.
+ //Add 0 as value if !isNumber (e.g. empty values are useful when 0 values should be hidden in tooltip)
+ this.datasets[datasetIndex].bars.push(new this.BarClass({
+ value : helpers.isNumber(value)?value:0,
+ label : label,
+ x: this.scale.calculateBarX(this.scale.valuesCount+1),
+ y: this.scale.endPoint,
+ width : this.scale.calculateBarWidth(this.datasets.length),
+ base : this.scale.endPoint,
+ strokeColor : this.datasets[datasetIndex].strokeColor,
+ fillColor : this.datasets[datasetIndex].fillColor
+ }));
+ }
+ },this);
+
+ this.scale.addXLabel(label);
+ //Then re-render the chart.
+ this.update();
+ },
+ removeData : function(){
+ this.scale.removeXLabel();
+ //Then re-render the chart.
+ helpers.each(this.datasets,function(dataset){
+ dataset.bars.shift();
+ },this);
+ this.update();
+ },
+ reflow : function(){
+ helpers.extend(this.BarClass.prototype,{
+ y: this.scale.endPoint,
+ base : this.scale.endPoint
+ });
+ var newScaleProps = helpers.extend({
+ height : this.chart.height,
+ width : this.chart.width
+ });
+ this.scale.update(newScaleProps);
+ },
+ draw : function(ease){
+ var easingDecimal = ease || 1;
+ this.clear();
+
+ var ctx = this.chart.ctx;
+
+ this.scale.draw(easingDecimal);
+
+ //Draw all the bars for each dataset
+ helpers.each(this.datasets,function(dataset,datasetIndex){
+ helpers.each(dataset.bars,function(bar,index){
+ var y = this.scale.calculateBarY(this.datasets, datasetIndex, index, bar.value),
+ height = this.scale.calculateBarHeight(this.datasets, datasetIndex, index, bar.value);
+
+ //Transition then draw
+ if(bar.value > 0) {
+ bar.transition({
+ base : this.scale.endPoint - (Math.abs(height) - Math.abs(y)),
+ x : this.scale.calculateBarX(index),
+ y : Math.abs(y),
+ height : Math.abs(height),
+ width : this.scale.calculateBarWidth(this.datasets.length)
+ }, easingDecimal).draw();
+ }
+ },this);
+ },this);
+ }
+ });
+}));
diff --git a/public/js/charts.js b/public/js/charts.js
index 60bd4ecccb..cfd00f5d2f 100644
--- a/public/js/charts.js
+++ b/public/js/charts.js
@@ -4,22 +4,36 @@
Make some colours:
*/
/*
-#555299
-#4285f4
-#db4437
-#f4b400
-#0f9d58
-#ab47bc
-#00acc1
-#ff7043
-#9e9d24
-#5c6bc0", "#f06292", "#00796b", "#c2185b"],
+ #555299
+ #4285f4
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #", "#", "#", "#"],
*/
var colourSet = [
[53, 124, 165],
[0, 141, 76],
[219, 139, 11],
- [202, 25, 90]
+ [202, 25, 90],
+ [85, 82, 153],
+ [66, 133, 244],
+ [219, 68, 55],
+ [244, 180, 0],
+ [15, 157, 88],
+ [171, 71, 188],
+ [0, 172, 193],
+ [255, 112, 67],
+ [158, 157, 36],
+ [92, 107, 192],
+ [240, 98, 146],
+ [0, 121, 107],
+ [194, 24, 91],
+
];
var fillColors = [];
@@ -153,6 +167,7 @@ function columnChart(URL, container, options) {
for (var i = 0; i < data.count; i++) {
newData.labels = data.labels;
var dataset = data.datasets[i];
+ console.log('Now at index ' + i + ' (count is ' + fillColors.length + ')');
dataset.fillColor = fillColors[i];
dataset.strokeColor = strokePointHighColors[i];
dataset.pointColor = strokePointHighColors[i];
@@ -178,7 +193,36 @@ function columnChart(URL, container, options) {
*/
function stackedColumnChart(URL, container, options) {
"use strict";
- columnChart(URL, container, options);
+ $.getJSON(URL).success(function (data) {
+
+ var ctx = document.getElementById(container).getContext("2d");
+ var newData = {};
+ newData.datasets = [];
+ console.log('----');
+ console.log('URL: ' + URL);
+ console.log('Total datasets: ' + data.datasets.length);
+ console.log('data.count: ' + data.count);
+ console.log('----');
+
+ for (var i = 0; i < data.count; i++) {
+ newData.labels = data.labels;
+ var dataset = data.datasets[i];
+ console.log('Now at index ' + i + ' (count is ' + fillColors.length + ')');
+ dataset.fillColor = fillColors[i];
+ dataset.strokeColor = strokePointHighColors[i];
+ dataset.pointColor = strokePointHighColors[i];
+ dataset.pointStrokeColor = "#fff";
+ dataset.pointHighlightFill = "#fff";
+ dataset.pointHighlightStroke = strokePointHighColors[i];
+ newData.datasets.push(dataset);
+ }
+ console.log(newData);
+ new Chart(ctx).StackedBar(newData, options);
+
+ }).fail(function () {
+ $('#' + container).addClass('google-chart-error');
+ });
+ console.log('URL for column chart : ' + URL);
}
/**
diff --git a/public/js/reports.js b/public/js/reports.js
index 563ea30237..0a790fa256 100644
--- a/public/js/reports.js
+++ b/public/js/reports.js
@@ -12,13 +12,17 @@ $(function () {
function drawChart() {
"use strict";
- columnChart('chart/report/in-out/' + year + shared, 'income-expenses-chart');
- columnChart('chart/report/in-out-sum/' + year + shared, 'income-expenses-sum-chart');
-
- stackedColumnChart('chart/budget/year/' + year + shared, 'budgets');
- stackedColumnChart('chart/category/year/' + year + shared, 'categories');
-
- lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart');
+ if (typeof columnChart !== undefined) {
+ columnChart('chart/report/in-out/' + year + shared, 'income-expenses-chart');
+ columnChart('chart/report/in-out-sum/' + year + shared, 'income-expenses-sum-chart');
+ }
+ if (typeof stackedColumnChart !== undefined) {
+ stackedColumnChart('chart/budget/year/' + year + shared, 'budgets');
+ stackedColumnChart('chart/category/year/' + year + shared, 'categories');
+ }
+ if (typeof lineChart !== undefined && typeof month !== 'undefined') {
+ lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart');
+ }
}
diff --git a/resources/twig/budgets/show.twig b/resources/twig/budgets/show.twig
index 8dbf2a3800..cf2734d3f6 100644
--- a/resources/twig/budgets/show.twig
+++ b/resources/twig/budgets/show.twig
@@ -27,7 +27,12 @@
-
+ {% if Config.get('firefly.chart') == 'google' %}
+
+ {% endif %}
+ {% if Config.get('firefly.chart') == 'chartjs' %}
+
+ {% endif %}
diff --git a/resources/twig/categories/show.twig b/resources/twig/categories/show.twig
index 32884ac3cb..771c5d56b8 100644
--- a/resources/twig/categories/show.twig
+++ b/resources/twig/categories/show.twig
@@ -12,7 +12,12 @@
{{ 'overview'|_ }} (month)
-
+ {% if Config.get('firefly.chart') == 'google' %}
+
+ {% endif %}
+ {% if Config.get('firefly.chart') == 'chartjs' %}
+
+ {% endif %}
@@ -22,7 +27,12 @@
{{ 'overview'|_ }} (all)
-
+ {% if Config.get('firefly.chart') == 'google' %}
+
+ {% endif %}
+ {% if Config.get('firefly.chart') == 'chartjs' %}
+
+ {% endif %}
diff --git a/resources/twig/reports/year.twig b/resources/twig/reports/year.twig
index 3c072a7222..24c0c21d39 100644
--- a/resources/twig/reports/year.twig
+++ b/resources/twig/reports/year.twig
@@ -13,7 +13,12 @@
{{ 'incomeVsExpenses'|_ }}
-
+ {% if Config.get('firefly.chart') == 'google' %}
+
+ {% endif %}
+ {% if Config.get('firefly.chart') == 'chartjs' %}
+
+ {% endif %}
@@ -23,7 +28,12 @@
{{ 'incomeVsExpenses'|_ }}
-
+ {% if Config.get('firefly.chart') == 'google' %}
+
+ {% endif %}
+ {% if Config.get('firefly.chart') == 'chartjs' %}
+
+ {% endif %}
@@ -49,7 +59,12 @@
{{ 'categories'|_ }}
-
+ {% if Config.get('firefly.chart') == 'google' %}
+
+ {% endif %}
+ {% if Config.get('firefly.chart') == 'chartjs' %}
+
+ {% endif %}
@@ -61,7 +76,12 @@
{{ 'budgets'|_ }}
-
+ {% if Config.get('firefly.chart') == 'google' %}
+
+ {% endif %}
+ {% if Config.get('firefly.chart') == 'chartjs' %}
+
+ {% endif %}
@@ -76,12 +96,15 @@
{% endif %}
{% if Config.get('firefly.chart') == 'chartjs' %}
+
+
{% endif %}