MDL-55015 core: Add support for axis label in charts
authorFrederic Massart <fred@moodle.com>
Fri, 24 Jun 2016 04:16:07 +0000 (12:16 +0800)
committerDan Poltawski <dan@moodle.com>
Mon, 25 Jul 2016 09:42:28 +0000 (10:42 +0100)
Part of MDL-54987 epic.

lib/amd/build/chart_axis.min.js [new file with mode: 0644]
lib/amd/build/chart_base.min.js
lib/amd/build/chart_output_chartjs.min.js
lib/amd/src/chart_axis.js [new file with mode: 0644]
lib/amd/src/chart_base.js
lib/amd/src/chart_output_chartjs.js
lib/classes/chart_axis.php [new file with mode: 0644]
lib/classes/chart_base.php

diff --git a/lib/amd/build/chart_axis.min.js b/lib/amd/build/chart_axis.min.js
new file mode 100644 (file)
index 0000000..349f434
Binary files /dev/null and b/lib/amd/build/chart_axis.min.js differ
index ee42009..0ec2d17 100644 (file)
Binary files a/lib/amd/build/chart_base.min.js and b/lib/amd/build/chart_base.min.js differ
index c85ada5..9d6efba 100644 (file)
Binary files a/lib/amd/build/chart_output_chartjs.min.js and b/lib/amd/build/chart_output_chartjs.min.js differ
diff --git a/lib/amd/src/chart_axis.js b/lib/amd/src/chart_axis.js
new file mode 100644 (file)
index 0000000..1aefb6f
--- /dev/null
@@ -0,0 +1,73 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Chart axis.
+ *
+ * @package    core
+ * @copyright  2016 Frédéric Massart - FMCorz.net
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([], function() {
+
+    /**
+     * Chart axis.
+     */
+    function Axis() {
+        // Please eslint no-empty-function.
+    }
+
+    Axis.prototype.POS_DEFAULT = null;
+    Axis.prototype.POS_BOTTOM = 'bottom';
+    Axis.prototype.POS_LEFT = 'left';
+    Axis.prototype.POS_RIGHT = 'right';
+    Axis.prototype.POS_TOP = 'top';
+
+    Axis.prototype._label = null;
+    Axis.prototype._position = null;
+
+    Axis.prototype.create = function(obj) {
+        var s = new Axis();
+        s.setPosition(obj.position);
+        s.setLabel(obj.label);
+        return s;
+    };
+
+    Axis.prototype.getLabel = function() {
+        return this._label;
+    };
+
+    Axis.prototype.getPosition = function() {
+        return this._position;
+    };
+
+    Axis.prototype.setLabel = function(label) {
+        this._label = label || null;
+    };
+
+    Axis.prototype.setPosition = function(position) {
+        if (position != this.POS_DEFAULT
+                && position != this.POS_BOTTOM
+                && position != this.POS_LEFT
+                && position != this.POS_RIGHT
+                && position != this.POS_TOP) {
+            throw new Error('Invalid axis position.');
+        }
+        this._position = position;
+    };
+
+    return Axis;
+
+});
index 6da2fcc..113f39a 100644 (file)
@@ -20,7 +20,7 @@
  * @copyright  2016 Frédéric Massart - FMCorz.net
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-define(['core/chart_series'], function(Series) {
+define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {
 
     /**
      * Chart base.
@@ -28,10 +28,15 @@ define(['core/chart_series'], function(Series) {
     function Base() {
         this._series = [];
         this._labels = [];
+        this._xaxes = [];
+        this._yaxes = [];
     }
     Base.prototype._series = null;
     Base.prototype._labels = null;
     Base.prototype._title = null;
+    Base.prototype._xaxes = null;
+    Base.prototype._yaxes = null;
+
     Base.prototype.COLORSET = ['red', 'green', 'blue', 'yellow', 'pink', 'orange'];
     Base.prototype.TYPE = null;
 
@@ -49,14 +54,40 @@ define(['core/chart_series'], function(Series) {
         // TODO Not convinced about the usage of Klass here but I can't figure out a way
         // to have a reference to the class in the sub classes, in PHP I'd do new self().
         var Chart = new Klass();
+
         Chart.setLabels(data.labels);
         Chart.setTitle(data.title);
-        for (var i = 0; i < data.series.length; i++) {
-            Chart.addSeries(Series.prototype.create(data.series[i]));
-        }
+        data.series.forEach(function(seriesData) {
+            Chart.addSeries(Series.prototype.create(seriesData));
+        });
+        data.axes.x.forEach(function(axisData, i) {
+            Chart.setXAxis(Axis.prototype.create(axisData), i);
+        });
+        data.axes.y.forEach(function(axisData, i) {
+            Chart.setYAxis(Axis.prototype.create(axisData), i);
+        });
         return Chart;
     };
 
+    Base.prototype.__getAxis = function(xy, index, createIfNotExists) {
+        var axes = xy === 'x' ? this._xaxes : this._yaxes,
+            axis;
+
+        index = typeof index === 'undefined' ? 0 : index;
+        createIfNotExists = typeof createIfNotExists === 'undefined' ? false : createIfNotExists;
+        axis = axes[index];
+
+        if (typeof axis === 'undefined') {
+            if (!createIfNotExists) {
+                throw new Error('Unknown axis.');
+            }
+            axis = new Axis();
+            axes[index] = axis;
+        }
+
+        return axis;
+    };
+
     Base.prototype.getLabels = function() {
         return this._labels;
     };
@@ -76,6 +107,22 @@ define(['core/chart_series'], function(Series) {
         return this.TYPE;
     };
 
+    Base.prototype.getXAxes = function() {
+        return this._xaxes;
+    };
+
+    Base.prototype.getXAxis = function(index, createIfNotExists) {
+        return this.__getAxis('x', index, createIfNotExists);
+    };
+
+    Base.prototype.getYAxes = function() {
+        return this._yaxes;
+    };
+
+    Base.prototype.getYAxis = function(index, createIfNotExists) {
+        return this.__getAxis('y', index, createIfNotExists);
+    };
+
     Base.prototype.setLabels = function(labels) {
         if (labels.length && this._series.length && this._series[0].length != labels.length) {
             throw new Error('Series must match label values.');
@@ -87,6 +134,16 @@ define(['core/chart_series'], function(Series) {
         this._title = title;
     };
 
+    Base.prototype.setXAxis = function(axis, index) {
+        index = typeof index === 'undefined' ? 0 : index;
+        this._xaxes[index] = axis;
+    };
+
+    Base.prototype.setYAxis = function(axis, index) {
+        index = typeof index === 'undefined' ? 0 : index;
+        this._yaxes[index] = axis;
+    };
+
     Base.prototype._validateSerie = function(serie) {
         if (this._series.length && this._series[0].getCount() != serie.getCount()) {
             throw new Error('Series do not have an equal number of values.');
index 7c80346..24e189e 100644 (file)
  * @copyright  2016 Frédéric Massart - FMCorz.net
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-define(['jquery', 'core/chartjs', 'core/chart_output_base'], function($, Chartjs, Base) {
+define([
+    'jquery',
+    'core/chartjs',
+    'core/chart_axis',
+    'core/chart_output_base',
+], function($, Chartjs, Axis, Base) {
 
     /**
      * Chart output for Chart.js.
@@ -54,6 +59,23 @@ define(['jquery', 'core/chartjs', 'core/chart_output_base'], function($, Chartjs
         this._chartjs = new Chartjs(this._node[0], this._config);
     };
 
+    Output.prototype._makeAxisConfig = function(axis) {
+        var scaleData = {};
+
+        if (axis.getPosition() !== Axis.prototype.POS_DEFAULT) {
+            scaleData.position = axis.getPosition();
+        }
+
+        if (axis.getLabel() !== null) {
+            scaleData.scaleLabel = {
+                display: true,
+                labelString: axis.getLabel()
+            };
+        }
+
+        return scaleData;
+    };
+
     Output.prototype._makeConfig = function() {
         var config = {
             type: this._chart.getType(),
@@ -68,6 +90,19 @@ define(['jquery', 'core/chartjs', 'core/chart_output_base'], function($, Chartjs
                 }
             }
         };
+
+        this._chart.getXAxes().forEach(function(axis, i) {
+            config.options.scales = config.options.scales || {};
+            config.options.scales.xAxes = config.options.scales.xAxes || [];
+            config.options.scales.xAxes[i] = this._makeAxisConfig(axis);
+        }.bind(this));
+
+        this._chart.getYAxes().forEach(function(axis, i) {
+            config.options.scales = config.options.scales || {};
+            config.options.scales.yAxes = config.options.scales.yAxes || [];
+            config.options.scales.yAxes[i] = this._makeAxisConfig(axis);
+        }.bind(this));
+
         return config;
     };
 
diff --git a/lib/classes/chart_axis.php b/lib/classes/chart_axis.php
new file mode 100644 (file)
index 0000000..9550f94
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Chart axis.
+ *
+ * @package    core
+ * @copyright  2016 Frédéric Massart - FMCorz.net
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core;
+defined('MOODLE_INTERNAL') || die();
+
+use coding_exception;
+use JsonSerializable;
+use renderable;
+
+/**
+ * Chart axis class.
+ *
+ * @package    core
+ * @copyright  2016 Frédéric Massart - FMCorz.net
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class chart_axis implements JsonSerializable {
+
+    const POS_DEFAULT = null;
+    const POS_BOTTOM = 'bottom';
+    const POS_LEFT = 'left';
+    const POS_RIGHT = 'right';
+    const POS_TOP = 'top';
+
+    protected $label = null;
+    protected $position = self::POS_DEFAULT;
+
+    public function __construct() {
+    }
+
+    public function get_label() {
+        return $this->label;
+    }
+
+    public function get_position() {
+        return $this->position;
+    }
+
+    public function jsonSerialize() {
+        return [
+            'label' => $this->label,
+            'position' => $this->position,
+        ];
+    }
+
+    public function set_label($label) {
+        return $this->label = $label;
+    }
+
+    public function set_position($position) {
+        return $this->position = $position;
+    }
+
+}
index 5e69999..1bf9082 100644 (file)
@@ -41,6 +41,8 @@ class chart_base implements JsonSerializable, renderable {
     protected $series = [];
     protected $labels = [];
     protected $title = null;
+    protected $xaxes = [];
+    protected $yaxes = [];
 
     public function __construct() {
     }
@@ -54,10 +56,31 @@ class chart_base implements JsonSerializable, renderable {
             'type' => $this->get_type(),
             'series' => $this->series,
             'labels' => $this->labels,
-            'title' => $this->title
+            'title' => $this->title,
+            'axes' => [
+                'x' => $this->xaxes,
+                'y' => $this->yaxes,
+            ]
         ];
     }
 
+    private function get_axis($type, $index, $createifnotexists) {
+        if ($type === 'x') {
+            $axes = &$this->xaxes;
+        } else {
+            $axes = &$this->yaxes;
+        }
+
+        if (!isset($axes[$index])) {
+            if (!$createifnotexists) {
+                throw new coding_exception('Unknown axis.');
+            }
+            $axes[$index] = new chart_axis();
+        }
+
+        return $axes[$index];
+    }
+
     public function get_labels() {
         return $this->labels;
     }
@@ -75,6 +98,22 @@ class chart_base implements JsonSerializable, renderable {
         return substr($classname, strpos($classname, '_') + 1);
     }
 
+    public function get_xaxes() {
+        return $this->xaxes;
+    }
+
+    public function get_xaxis($index = 0, $createifnotexists = false) {
+        return $this->get_axis('x', $index, $createifnotexists);
+    }
+
+    public function get_yaxes() {
+        return $this->yaxes;
+    }
+
+    public function get_yaxis($index = 0, $createifnotexists = false) {
+        return $this->get_axis('y', $index, $createifnotexists);
+    }
+
     public function set_labels(array $labels) {
         $this->labels = $labels;
     }
@@ -82,4 +121,13 @@ class chart_base implements JsonSerializable, renderable {
     public function set_title($title) {
         $this->title = $title;
     }
+
+    public function set_xaxis(chart_axis $axis, $index = 0) {
+        return $this->xaxes[$index] = $axis;
+    }
+
+    public function set_yaxis(chart_axis $axis, $index = 0) {
+        return $this->yaxes[$index] = $axis;
+    }
+
 }