diff --git a/lib/amd/build/chart_axis.min.js b/lib/amd/build/chart_axis.min.js new file mode 100644 index 00000000000..349f43457d1 --- /dev/null +++ b/lib/amd/build/chart_axis.min.js @@ -0,0 +1 @@ +define([],function(){function a(){}return a.prototype.POS_DEFAULT=null,a.prototype.POS_BOTTOM="bottom",a.prototype.POS_LEFT="left",a.prototype.POS_RIGHT="right",a.prototype.POS_TOP="top",a.prototype._label=null,a.prototype._position=null,a.prototype.create=function(b){var c=new a;return c.setPosition(b.position),c.setLabel(b.label),c},a.prototype.getLabel=function(){return this._label},a.prototype.getPosition=function(){return this._position},a.prototype.setLabel=function(a){this._label=a||null},a.prototype.setPosition=function(a){if(a!=this.POS_DEFAULT&&a!=this.POS_BOTTOM&&a!=this.POS_LEFT&&a!=this.POS_RIGHT&&a!=this.POS_TOP)throw new Error("Invalid axis position.");this._position=a},a}); \ No newline at end of file diff --git a/lib/amd/build/chart_base.min.js b/lib/amd/build/chart_base.min.js index ee42009ec89..0ec2d171ebc 100644 --- a/lib/amd/build/chart_base.min.js +++ b/lib/amd/build/chart_base.min.js @@ -1 +1 @@ -define(["core/chart_series"],function(a){function b(){this._series=[],this._labels=[]}return b.prototype._series=null,b.prototype._labels=null,b.prototype._title=null,b.prototype.COLORSET=["red","green","blue","yellow","pink","orange"],b.prototype.TYPE=null,b.prototype.addSeries=function(a){this._validateSerie(a),this._series.push(a),null===a.getColor()&&a.setColor(b.prototype.COLORSET[this._series.length%b.prototype.COLORSET.length])},b.prototype.create=function(b,c){var d=new b;d.setLabels(c.labels),d.setTitle(c.title);for(var e=0;e. + +/** + * 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; + +}); diff --git a/lib/amd/src/chart_base.js b/lib/amd/src/chart_base.js index 6da2fcc26b5..113f39a2e07 100644 --- a/lib/amd/src/chart_base.js +++ b/lib/amd/src/chart_base.js @@ -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.'); diff --git a/lib/amd/src/chart_output_chartjs.js b/lib/amd/src/chart_output_chartjs.js index 7c80346c7c2..24e189ead79 100644 --- a/lib/amd/src/chart_output_chartjs.js +++ b/lib/amd/src/chart_output_chartjs.js @@ -20,7 +20,12 @@ * @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 index 00000000000..9550f94a13a --- /dev/null +++ b/lib/classes/chart_axis.php @@ -0,0 +1,76 @@ +. + +/** + * 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; + } + +} diff --git a/lib/classes/chart_base.php b/lib/classes/chart_base.php index 5e69999373f..1bf9082ff6f 100644 --- a/lib/classes/chart_base.php +++ b/lib/classes/chart_base.php @@ -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; + } + }