MDL-55108 core: Document the charting library

Part of MDL-54987 epic.
This commit is contained in:
Frederic Massart 2016-07-04 12:01:25 +08:00 committed by Dan Poltawski
parent 6ce35fe0e1
commit 601da0e693
17 changed files with 832 additions and 39 deletions

View File

@ -1 +1 @@
define(["jquery"],function(a){return{make:function(b){var c=a.Deferred();return require(["core/chart_"+b.type],function(a){var d=a.prototype.create(a,b);c.resolve(d)}),c.promise()}}});
define(["jquery"],function(a){var b={make:function(b){var c=a.Deferred();return require(["core/chart_"+b.type],function(a){var d=a.prototype.create(a,b);c.resolve(d)}),c.promise()}};return b});

View File

@ -1 +1 @@
define(["core/chart_output_chartjs"],function(a){return a});
define(["core/chart_output_chartjs"],function(a){var b=a;return b});

View File

@ -1 +1 @@
define(["jquery","core/chartjs","core/chart_axis","core/chart_output_base"],function(a,b,c,d){function e(){d.prototype.constructor.apply(this,arguments),this._canvas=this._node,"CANVAS"!=this._canvas.prop("tagName")&&(this._canvas=a("<canvas>"),this._node.append(this._canvas)),this._build()}return e.prototype=Object.create(d.prototype),e.prototype._config=null,e.prototype._chartjs=null,e.prototype._canvas=null,e.prototype.getDatasets=function(){var a=this._chart.getSeries().map(function(a){return{label:a.getLabel(),data:a.getValues(),type:a.getType(),fill:!1,borderColor:a.getColor(),backgroundColor:a.getColor()}});return a},e.prototype._build=function(){this._config=this._makeConfig(),this._chartjs=new b(this._canvas[0],this._config)},e.prototype._makeAxisConfig=function(a){var b={};return a.getPosition()!==c.prototype.POS_DEFAULT&&(b.position=a.getPosition()),null!==a.getLabel()&&(b.scaleLabel={display:!0,labelString:a.getLabel()}),null!==a.getStepSize()&&(b.ticks=b.ticks||{},b.ticks.stepSize=a.getStepSize()),null!==a.getMax()&&(b.ticks=b.ticks||{},b.ticks.max=a.getMax()),null!==a.getMin()&&(b.ticks=b.ticks||{},b.ticks.min=a.getMin()),b},e.prototype._makeConfig=function(){var a={type:this._chart.getType(),data:{labels:this._chart.getLabels(),datasets:this.getDatasets()},options:{title:{display:null!==this._chart.getTitle(),text:this._chart.getTitle()}}};return this._chart.getXAxes().forEach(function(b,c){a.options.scales=a.options.scales||{},a.options.scales.xAxes=a.options.scales.xAxes||[],a.options.scales.xAxes[c]=this._makeAxisConfig(b)}.bind(this)),this._chart.getYAxes().forEach(function(b,c){var d=b.getLabels();a.options.scales=a.options.scales||{},a.options.scales.yAxes=a.options.scales.yAxes||[],a.options.scales.yAxes[c]=this._makeAxisConfig(b),null!==d&&(a.options.scales.yAxes[c].ticks.callback=function(a){return d[parseInt(a,10)]||""})}.bind(this)),a},e.prototype.update=function(){a.extend(!0,this._config,this._makeConfig()),this._chartjs.update()},e});
define(["jquery","core/chartjs","core/chart_axis","core/chart_output_base"],function(a,b,c,d){function e(){d.prototype.constructor.apply(this,arguments),this._canvas=this._node,"CANVAS"!=this._canvas.prop("tagName")&&(this._canvas=a("<canvas>"),this._node.append(this._canvas)),this._build()}return e.prototype=Object.create(d.prototype),e.prototype._config=null,e.prototype._chartjs=null,e.prototype._canvas=null,e.prototype._build=function(){this._config=this._makeConfig(),this._chartjs=new b(this._canvas[0],this._config)},e.prototype._makeAxisConfig=function(a){var b={};return a.getPosition()!==c.prototype.POS_DEFAULT&&(b.position=a.getPosition()),null!==a.getLabel()&&(b.scaleLabel={display:!0,labelString:a.getLabel()}),null!==a.getStepSize()&&(b.ticks=b.ticks||{},b.ticks.stepSize=a.getStepSize()),null!==a.getMax()&&(b.ticks=b.ticks||{},b.ticks.max=a.getMax()),null!==a.getMin()&&(b.ticks=b.ticks||{},b.ticks.min=a.getMin()),b},e.prototype._makeConfig=function(){var a={type:this._chart.getType(),data:{labels:this._chart.getLabels(),datasets:this._makeDatasetsConfig()},options:{title:{display:null!==this._chart.getTitle(),text:this._chart.getTitle()}}};return this._chart.getXAxes().forEach(function(b,c){a.options.scales=a.options.scales||{},a.options.scales.xAxes=a.options.scales.xAxes||[],a.options.scales.xAxes[c]=this._makeAxisConfig(b)}.bind(this)),this._chart.getYAxes().forEach(function(b,c){var d=b.getLabels();a.options.scales=a.options.scales||{},a.options.scales.yAxes=a.options.scales.yAxes||[],a.options.scales.yAxes[c]=this._makeAxisConfig(b),null!==d&&(a.options.scales.yAxes[c].ticks.callback=function(a){return d[parseInt(a,10)]||""})}.bind(this)),a},e.prototype._makeDatasetsConfig=function(){var a=this._chart.getSeries().map(function(a){return{label:a.getLabel(),data:a.getValues(),type:a.getType(),fill:!1,borderColor:a.getColor(),backgroundColor:a.getColor()}});return a},e.prototype.update=function(){a.extend(!0,this._config,this._makeConfig()),this._chartjs.update()},e});

View File

@ -19,29 +19,102 @@
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @module core/chart_axis
*/
define([], function() {
/**
* Chart axis.
* Chart axis class.
*
* This is used to represent an axis, whether X or Y.
*
* @alias module:core/chart_axis
* @class
*/
function Axis() {
// Please eslint no-empty-function.
}
/**
* Default axis position.
* @const {Null}
*/
Axis.prototype.POS_DEFAULT = null;
/**
* Bottom axis position.
* @const {String}
*/
Axis.prototype.POS_BOTTOM = 'bottom';
/**
* Left axis position.
* @const {String}
*/
Axis.prototype.POS_LEFT = 'left';
/**
* Right axis position.
* @const {String}
*/
Axis.prototype.POS_RIGHT = 'right';
/**
* Top axis position.
* @const {String}
*/
Axis.prototype.POS_TOP = 'top';
/**
* Label of the axis.
* @type {String}
* @protected
*/
Axis.prototype._label = null;
/**
* Labels of the ticks.
* @type {String[]}
* @protected
*/
Axis.prototype._labels = null;
/**
* Maximum value of the axis.
* @type {Number}
* @protected
*/
Axis.prototype._max = null;
/**
* Minimum value of the axis.
* @type {Number}
* @protected
*/
Axis.prototype._min = null;
/**
* Position of the axis.
* @type {String}
* @protected
*/
Axis.prototype._position = null;
/**
* Steps on the axis.
* @type {Number}
* @protected
*/
Axis.prototype._stepSize = null;
/**
* Create a new instance of an axis from serialised data.
*
* @static
* @method create
* @param {Object} obj The data of the axis.
* @return {module:core/chart_axis}
*/
Axis.prototype.create = function(obj) {
var s = new Axis();
s.setPosition(obj.position);
@ -53,34 +126,90 @@ define([], function() {
return s;
};
/**
* Get the label of the axis.
*
* @method getLabel
* @return {String}
*/
Axis.prototype.getLabel = function() {
return this._label;
};
/**
* Get the labels of the ticks of the axis.
*
* @method getLabels
* @return {String[]}
*/
Axis.prototype.getLabels = function() {
return this._labels;
};
/**
* Get the maximum value of the axis.
*
* @method getMax
* @return {Number}
*/
Axis.prototype.getMax = function() {
return this._max;
};
/**
* Get the minimum value of the axis.
*
* @method getMin
* @return {Number}
*/
Axis.prototype.getMin = function() {
return this._min;
};
/**
* Get the position of the axis.
*
* @method getPosition
* @return {String}
*/
Axis.prototype.getPosition = function() {
return this._position;
};
/**
* Get the step size of the axis.
*
* @method getStepSize
* @return {Number}
*/
Axis.prototype.getStepSize = function() {
return this._stepSize;
};
/**
* Set the label of the axis.
*
* @method setLabel
* @param {String} label The label.
*/
Axis.prototype.setLabel = function(label) {
this._label = label || null;
};
/**
* Set the labels of the values on the axis.
*
* This automatically sets the [_stepSize]{@link module:core/chart_axis#_stepSize},
* [_min]{@link module:core/chart_axis#_min} and [_max]{@link module:core/chart_axis#_max}
* to define a scale from 0 to the number of labels when none of the previously
* mentioned values have been modified.
*
* You can use other values so long that your values in a series are mapped
* to the values represented by your _min, _max and _stepSize.
*
* @method setLabels
* @param {String[]} labels The labels.
*/
Axis.prototype.setLabels = function(labels) {
this._labels = labels || null;
@ -95,14 +224,45 @@ define([], function() {
}
};
/**
* Set the maximum value on the axis.
*
* When this is not set (or set to null) it is left for the output
* library to best guess what should be used.
*
* @method setMax
* @param {Number} max The value.
*/
Axis.prototype.setMax = function(max) {
this._max = typeof max !== 'undefined' ? max : null;
};
/**
* Set the minimum value on the axis.
*
* When this is not set (or set to null) it is left for the output
* library to best guess what should be used.
*
* @method setMin
* @param {Number} min The value.
*/
Axis.prototype.setMin = function(min) {
this._min = typeof min !== 'undefined' ? min : null;
};
/**
* Set the position of the axis.
*
* This does not validate whether or not the constant used is valid
* as the axis itself is not aware whether it represents the X or Y axis.
*
* The output library has to have a fallback in case the values are incorrect.
* When this is not set to {@link module:core/chart_axis#POS_DEFAULT} it is up
* to the output library to choose what position fits best.
*
* @method setPosition
* @param {String} position The value.
*/
Axis.prototype.setPosition = function(position) {
if (position != this.POS_DEFAULT
&& position != this.POS_BOTTOM
@ -114,6 +274,14 @@ define([], function() {
this._position = position;
};
/**
* Set the stepSize on the axis.
*
* This is used to determine where ticks are displayed on the axis between min and max.
*
* @method setStepSize
* @param {Number} stepSize The value.
*/
Axis.prototype.setStepSize = function(stepSize) {
if (typeof stepSize === 'undefined' || stepSize === null) {
stepSize = null;

View File

@ -19,19 +19,26 @@
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @module core/chart_bar
*/
define(['core/chart_base'], function(Base) {
/**
* Bar chart.
*
* @alias module:core/chart_bar
* @extends {module:core/chart_base}
* @class
*/
function Bar() {
Base.prototype.constructor.apply(this, arguments);
}
Bar.prototype = Object.create(Base.prototype);
/** @override */
Bar.prototype.TYPE = 'bar';
/** @override */
Bar.prototype._setDefaults = function() {
Base.prototype._setDefaults.apply(this, arguments);
var axis = this.getYAxis(0, true);

View File

@ -19,11 +19,19 @@
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @module core/chart_base
*/
define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {
/**
* Chart base.
*
* The constructor of a chart must never take any argument.
*
* {@link module:core/chart_base#_setDefault} to set the defaults on instantiation.
*
* @alias module:core/chart_base
* @class
*/
function Base() {
this._series = [];
@ -33,25 +41,92 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {
this._setDefaults();
}
/**
* The series constituting this chart.
*
* @protected
* @type {module:core/chart_series[]}
*/
Base.prototype._series = null;
/**
* The labels of the X axis when categorised.
*
* @protected
* @type {String[]}
*/
Base.prototype._labels = null;
/**
* The title of the chart.
*
* @protected
* @type {String}
*/
Base.prototype._title = null;
/**
* The X axes.
*
* @protected
* @type {module:core/chart_axis[]}
*/
Base.prototype._xaxes = null;
/**
* The Y axes.
*
* @protected
* @type {module:core/chart_axis[]}
*/
Base.prototype._yaxes = null;
/**
* Colours to pick from when automatically assigning them.
*
* @const
* @type {String[]}
*/
Base.prototype.COLORSET = ['red', 'green', 'blue', 'yellow', 'pink', 'orange'];
/**
* The type of chart.
*
* @abstract
* @type {String}
* @const
*/
Base.prototype.TYPE = null;
Base.prototype.addSeries = function(serie) {
this._validateSerie(serie);
this._series.push(serie);
/**
* Add a series to the chart.
*
* This will automatically assign a color to the series if it does not have one.
*
* @param {module:core/chart_series} series The series to add.
*/
Base.prototype.addSeries = function(series) {
this._validateSerie(series);
this._series.push(series);
// Give a default color from the set.
if (serie.getColor() === null) {
serie.setColor(Base.prototype.COLORSET[this._series.length % Base.prototype.COLORSET.length]);
if (series.getColor() === null) {
series.setColor(Base.prototype.COLORSET[this._series.length % Base.prototype.COLORSET.length]);
}
};
/**
* Create a new instance of a chart from serialised data.
*
* the serialised attributes they offer and support.
*
* @static
* @method create
* @param {module:core/chart_base} Klass The class oject representing the type of chart to instantiate.
* @param {Object} data The data of the chart.
* @return {module:core/chart_base}
*/
Base.prototype.create = function(Klass, data) {
// 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().
@ -71,6 +146,15 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {
return Chart;
};
/**
* Get an axis.
*
* @private
* @param {String} xy Accepts the values 'x' or 'y'.
* @param {Number} [index=0] The index of the axis of its type.
* @param {Bool} [createIfNotExists=false] When true, create an instance if it does not exist.
* @return {module:core/chart_axis}
*/
Base.prototype.__getAxis = function(xy, index, createIfNotExists) {
var axes = xy === 'x' ? this._xaxes : this._yaxes,
setAxis = (xy === 'x' ? this.setXAxis : this.setYAxis).bind(this),
@ -91,18 +175,39 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {
return axis;
};
/**
* Get the labels of the X axis.
*
* @return {String[]}
*/
Base.prototype.getLabels = function() {
return this._labels;
};
/**
* Get the series.
*
* @return {module:core/chart_series[]}
*/
Base.prototype.getSeries = function() {
return this._series;
};
/**
* Get the title of the chart.
*
* @return {String}
*/
Base.prototype.getTitle = function() {
return this._title;
};
/**
* Get the type of chart.
*
* @see module:core/chart_base#TYPE
* @return {String}
*/
Base.prototype.getType = function() {
if (!this.TYPE) {
throw new Error('The TYPE property has not been set.');
@ -110,26 +215,67 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {
return this.TYPE;
};
/**
* Get the X axes.
*
* @return {module:core/chart_axis[]}
*/
Base.prototype.getXAxes = function() {
return this._xaxes;
};
/**
* Get an X axis.
*
* @param {Number} [index=0] The index of the axis.
* @param {Bool} [createIfNotExists=false] Create the instance of it does not exist at index.
* @return {module:core/chart_axis}
*/
Base.prototype.getXAxis = function(index, createIfNotExists) {
return this.__getAxis('x', index, createIfNotExists);
};
/**
* Get the Y axes.
*
* @return {module:core/chart_axis[]}
*/
Base.prototype.getYAxes = function() {
return this._yaxes;
};
/**
* Get an Y axis.
*
* @param {Number} [index=0] The index of the axis.
* @param {Bool} [createIfNotExists=false] Create the instance of it does not exist at index.
* @return {module:core/chart_axis}
*/
Base.prototype.getYAxis = function(index, createIfNotExists) {
return this.__getAxis('y', index, createIfNotExists);
};
/**
* Set the defaults for this chart type.
*
* Child classes can extend this to set defaults values on instantiation.
*
* emphasize and self-document the defaults values set by the chart type.
*
* @protected
*/
Base.prototype._setDefaults = function() {
// For the children to extend.
};
/**
* Set the labels of the X axis.
*
* This requires for each series to contain strictly as many values as there
* are labels.
*
* @param {String[]} labels The labels.
*/
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.');
@ -137,25 +283,52 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {
this._labels = labels;
};
/**
* Set the title of the chart.
*
* @param {String} title The title.
*/
Base.prototype.setTitle = function(title) {
this._title = title;
};
/**
* Set an X axis.
*
* Note that this will override any predefined axis without warning.
*
* @param {module:core/chart_axis} axis The axis.
* @param {Number} [index=0] The index of the axis.
*/
Base.prototype.setXAxis = function(axis, index) {
index = typeof index === 'undefined' ? 0 : index;
this._xaxes[index] = axis;
};
/**
* Set a Y axis.
*
* Note that this will override any predefined axis without warning.
*
* @param {module:core/chart_axis} axis The axis.
* @param {Number} [index=0] The index of the 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()) {
/**
* Validate a series.
*
* @protected
* @param {module:core/chart_series} series The series to validate.
*/
Base.prototype._validateSerie = function(series) {
if (this._series.length && this._series[0].getCount() != series.getCount()) {
throw new Error('Series do not have an equal number of values.');
} else if (this._labels.length && this._labels.length != serie.getCount()) {
} else if (this._labels.length && this._labels.length != series.getCount()) {
throw new Error('Series must match label values.');
}
};

View File

@ -16,15 +16,28 @@
/**
* Chart builder.
*
* This takes data, most likely generated in PHP, and creates a chart instance.
*
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery'], function($) {
return {
/**
* Chart builder.
*
* @exports core/chart_builder
*/
var module = {
/**
* Make a chart instance.
*
* This takes data, most likely generated in PHP, and creates a chart instance from it
* deferring most of the logic to {@link module:core/chart_base.create}.
*
* @param {Object} data The data.
* @return {Promise} A promise resolved with the chart instance.
*/
make: function(data) {
var deferred = $.Deferred();
require(['core/chart_' + data.type], function(Klass) {
@ -35,4 +48,6 @@ define(['jquery'], function($) {
}
};
return module;
});

View File

@ -19,17 +19,23 @@
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @module core/chart_line
*/
define(['core/chart_base'], function(Base) {
/**
* Line chart.
*
* @alias module:core/chart_line
* @extends {module:core/chart_base}
* @class
*/
function Line() {
Base.prototype.constructor.apply(this, arguments);
}
Line.prototype = Object.create(Base.prototype);
/** @override */
Line.prototype.TYPE = 'line';
return Line;

View File

@ -24,6 +24,12 @@
*/
define(['core/chart_output_chartjs'], function(Output) {
return Output;
/**
* @exports module:core/chart_output
* @extends {module:core/chart_output_chartjs}
*/
var defaultModule = Output;
return defaultModule;
});

View File

@ -21,12 +21,24 @@
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @module core/chart_output_base
*/
define(['jquery'], function($) {
/**
* Chart output base.
*
* The constructor of an output class must instantly generate and display the
* chart. It is also the responsability of the output module to check that
* the node received is of the appropriate type, if not a new node can be
* added within.
*
* The output module has total control over the content of the node and can
* clear it or output anything to it at will. A node should not be shared by
* two simultaneous output modules.
*
* @class
* @alias module:core/chart_output_base
* @param {Node} node The node to output with/in.
* @param {Chart} chart A chart object.
*/
@ -35,6 +47,16 @@ define(['jquery'], function($) {
this._chart = chart;
}
/**
* Update method.
*
* This is the public method through which an output instance in informed
* that the chart instance has been updated and they need to update the
* chart rendering.
*
* @abstract
* @return {Void}
*/
Base.prototype.update = function() {
throw new Error('Not supported.');
};

View File

@ -19,6 +19,7 @@
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @module core/chart_output_chartjs
*/
define([
'jquery',
@ -29,6 +30,10 @@ define([
/**
* Chart output for Chart.js.
*
* @class
* @alias module:core/chart_output_chartjs
* @extends {module:core/chart_output_base}
*/
function Output() {
Base.prototype.constructor.apply(this, arguments);
@ -42,32 +47,49 @@ define([
this._build();
}
Output.prototype = Object.create(Base.prototype);
/**
* Reference to the chart config object.
*
* @type {Object}
* @protected
*/
Output.prototype._config = null;
/**
* Reference to the instance of chart.js.
*
* @type {Object}
* @protected
*/
Output.prototype._chartjs = null;
/**
* Reference to the canvas node.
*
* @type {Jquery}
* @protected
*/
Output.prototype._canvas = null;
Output.prototype.getDatasets = function() {
var sets = this._chart.getSeries().map(function(series) {
return {
label: series.getLabel(),
data: series.getValues(),
type: series.getType(),
fill: false,
borderColor: series.getColor(),
backgroundColor: series.getColor()
};
});
return sets;
};
/**
* Builds the config and the chart.
*
* @protected
*/
Output.prototype._build = function() {
this._config = this._makeConfig();
this._chartjs = new Chartjs(this._canvas[0], this._config);
};
/**
* Make the axis config.
*
* @protected
* @param {module:core/chart_axis} axis The axis.
* @return {Object} The axis config.
*/
Output.prototype._makeAxisConfig = function(axis) {
var scaleData = {};
@ -100,12 +122,19 @@ define([
return scaleData;
};
/**
* Make the config config.
*
* @protected
* @param {module:core/chart_axis} axis The axis.
* @return {Object} The axis config.
*/
Output.prototype._makeConfig = function() {
var config = {
type: this._chart.getType(),
data: {
labels: this._chart.getLabels(),
datasets: this.getDatasets()
datasets: this._makeDatasetsConfig()
},
options: {
title: {
@ -138,6 +167,27 @@ define([
return config;
};
/**
* Get the datasets configurations.
*
* @protected
* @return {Object[]}
*/
Output.prototype._makeDatasetsConfig = function() {
var sets = this._chart.getSeries().map(function(series) {
return {
label: series.getLabel(),
data: series.getValues(),
type: series.getType(),
fill: false,
borderColor: series.getColor(),
backgroundColor: series.getColor()
};
});
return sets;
};
/** @override */
Output.prototype.update = function() {
$.extend(true, this._config, this._makeConfig());
this._chartjs.update();

View File

@ -19,19 +19,33 @@
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @module core/chart_pie
*/
define(['core/chart_base'], function(Base) {
/**
* Pie chart.
*
* @class
* @alias module:core/chart_pie
* @extends {module:core/chart_base}
*/
function Pie() {
Base.prototype.constructor.apply(this, arguments);
}
Pie.prototype = Object.create(Base.prototype);
/** @override */
Pie.prototype.TYPE = 'pie';
/**
* Validate a series.
*
* Overrides parent implementation to validate that there is only
* one series per chart instance.
*
* @override
*/
Pie.prototype._validateSerie = function() {
if (this._series.length >= 1) {
throw new Error('Pie charts only support one serie.');

View File

@ -19,12 +19,15 @@
* @package core
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @module core/chart_series
*/
define([], function() {
/**
* Chart data series.
*
* @class
* @alias module:core/chart_series
* @param {String} label The series label.
* @param {Number[]} values The values.
*/
@ -43,14 +46,62 @@ define([], function() {
this._values = values;
}
/**
* The default type of series.
*
* @type {Null}
* @const
*/
Series.prototype.TYPE_DEFAULT = null;
/**
* Type of series 'line'.
*
* @type {String}
* @const
*/
Series.prototype.TYPE_LINE = 'line';
/**
* The color of the series.
*
* @type {String}
* @protected
*/
Series.prototype._color = null;
/**
* The label of the series.
*
* @type {String}
* @protected
*/
Series.prototype._label = null;
/**
* The type of the series.
*
* @type {String}
* @protected
*/
Series.prototype._type = Series.prototype.TYPE_DEFAULT;
/**
* The values in the series.
*
* @type {Number[]}
* @protected
*/
Series.prototype._values = null;
/**
* Create a new instance of a series from serialised data.
*
* @static
* @method create
* @param {Object} obj The data of the series.
* @return {module:core/chart_series}
*/
Series.prototype.create = function(obj) {
var s = new Series(obj.label, obj.values);
s.setColor(obj.color);
@ -58,30 +109,65 @@ define([], function() {
return s;
};
/**
* Get the color.
*
* @return {String}
*/
Series.prototype.getColor = function() {
return this._color;
};
/**
* Get the number of values in the series.
*
* @return {Number}
*/
Series.prototype.getCount = function() {
return this._values.length;
};
/**
* Get the series label.
*
* @return {String}
*/
Series.prototype.getLabel = function() {
return this._label;
};
/**
* Get the series type.
*
* @return {String}
*/
Series.prototype.getType = function() {
return this._type;
};
/**
* Get the series values.
*
* @return {Number[]}
*/
Series.prototype.getValues = function() {
return this._values;
};
/**
* Set the series color.
*
* @param {String} color A CSS-compatible color.
*/
Series.prototype.setColor = function(color) {
this._color = color || null;
};
/**
* Set the type of the series.
*
* @param {String} type A type constant value.
*/
Series.prototype.setType = function(type) {
if (type != this.TYPE_DEFAULT && type != this.TYPE_LINE) {
throw new Error('Invalid serie type.');

View File

@ -38,46 +38,97 @@ use renderable;
*/
class chart_axis implements JsonSerializable {
/** Default axis position. */
const POS_DEFAULT = null;
/** Bottom axis position. */
const POS_BOTTOM = 'bottom';
/** Left axis position. */
const POS_LEFT = 'left';
/** Right axis position. */
const POS_RIGHT = 'right';
/** Top axis position. */
const POS_TOP = 'top';
/** @var string The axis label. */
protected $label = null;
/** @var string[] The axis labels, tick values. */
protected $labels = null;
/** @var float The maximum tick value. */
protected $max = null;
/** @var float The minimum tick value. */
protected $min = null;
/** @var string The axis position. */
protected $position = self::POS_DEFAULT;
/** @var float The stepsize between ticks. */
protected $stepsize = null;
/**
* Constructor.
*
* Must not take any argument.
*/
public function __construct() {
}
/**
* Get the label.
*
* @return string
*/
public function get_label() {
return $this->label;
}
/**
* Get the labels.
*
* @return string[]
*/
public function get_labels() {
return $this->labels;
}
/**
* Get the max value.
*
* @return float
*/
public function get_max() {
return $this->max;
}
/**
* Get the min value.
*
* @return float
*/
public function get_min() {
return $this->min;
}
/**
* Get the axis position.
*
* @return string
*/
public function get_position() {
return $this->position;
}
/**
* Get the step size.
*
* @return float
*/
public function get_stepsize() {
return $this->stepsize;
}
/**
* Serialize the object.
*
* @return array
*/
public function jsonSerialize() {
return [
'label' => $this->label,
@ -89,28 +140,58 @@ class chart_axis implements JsonSerializable {
];
}
/**
* Set the label.
*
* @param string $label The label.
*/
public function set_label($label) {
return $this->label = $label;
$this->label = $label;
}
/**
* Set the labels.
*
* @param string[] $labels The labels.
*/
public function set_labels($labels) {
return $this->labels = $labels;
$this->labels = $labels;
}
/**
* Set the max value.
*
* @param float $max The max value.
*/
public function set_max($max) {
return $this->max = $max;
$this->max = $max;
}
/**
* Set the min value.
*
* @param float $min The min value.
*/
public function set_min($min) {
return $this->min = $min;
$this->min = $min;
}
/**
* Set the position.
*
* @param string $position Use constant self::POS_*.
*/
public function set_position($position) {
return $this->position = $position;
$this->position = $position;
}
/**
* Set the step size.
*
* @param float $stepsize The step size.
*/
public function set_stepsize($stepsize) {
return $this->stepsize = $stepsize;
$this->stepsize = $stepsize;
}
}

View File

@ -34,6 +34,9 @@ defined('MOODLE_INTERNAL') || die();
*/
class chart_bar extends chart_base {
/**
* Set the defaults.
*/
protected function set_defaults() {
parent::set_defaults();
$yaxis = $this->get_yaxis(0, true);

View File

@ -38,20 +38,43 @@ use renderable;
*/
class chart_base implements JsonSerializable, renderable {
/** @var chart_series[] The series constituting this chart. */
protected $series = [];
/** @var string[] The labels for the X axis when categorised. */
protected $labels = [];
/** @var string The title of the chart. */
protected $title = null;
/** @var chart_axis[] The X axes. */
protected $xaxes = [];
/** @var chart_axis[] The Y axes. */
protected $yaxes = [];
/**
* Constructor.
*
* Must not take any argument.
*
* Most of the time you do not want to extend this, rather extend the
* method {@link self::set_defaults} to set the defaults on instantiation.
*/
public function __construct() {
$this->set_defaults();
}
/**
* Add a series to the chart.
*
* @param chart_series $serie The serie.
*/
public function add_series(chart_series $serie) {
$this->series[] = $serie;
}
/**
* Serialize the object.
*
* @return array
*/
public function jsonSerialize() {
return [
'type' => $this->get_type(),
@ -65,6 +88,14 @@ class chart_base implements JsonSerializable, renderable {
];
}
/**
* Get an axis.
*
* @param string $type Accepts values 'x' or 'y'.
* @param int $index The index of this axis.
* @param bool $createifnotexists Whether to create the axis if not found.
* @return chart_axis
*/
private function get_axis($type, $index, $createifnotexists) {
$isx = $type === 'x';
if ($isx) {
@ -89,55 +120,134 @@ class chart_base implements JsonSerializable, renderable {
return $axis;
}
/**
* Get the labels of the X axis.
*
* @return string[]
*/
public function get_labels() {
return $this->labels;
}
/**
* Get the series.
*
* @return chart_series[]
*/
public function get_series() {
return $this->series;
}
/**
* Get the title.
*
* @return string
*/
public function get_title() {
return $this->title;
}
/**
* Get the chart type.
*
* @return string
*/
public function get_type() {
$classname = get_class($this);
return substr($classname, strpos($classname, '_') + 1);
}
/**
* Get the X axes.
*
* @return chart_axis[]
*/
public function get_xaxes() {
return $this->xaxes;
}
/**
* Get an X axis.
*
* @param int $index The index of the axis.
* @param bool $createifnotexists When true, create an instance of the axis if none exist at this index yet.
* @return chart_axis
*/
public function get_xaxis($index = 0, $createifnotexists = false) {
return $this->get_axis('x', $index, $createifnotexists);
}
/**
* Get the Y axes.
*
* @return chart_axis[]
*/
public function get_yaxes() {
return $this->yaxes;
}
/**
* Get a Y axis.
*
* @param int $index The index of the axis.
* @param bool $createifnotexists When true, create an instance of the axis if none exist at this index yet.
* @return chart_axis
*/
public function get_yaxis($index = 0, $createifnotexists = false) {
return $this->get_axis('y', $index, $createifnotexists);
}
/**
* Set the defaults for this chart type.
*
* Child classes can extend this to set default values on instantiation.
*
* In general the constructor could be used, but this method is here to
* emphasize and self-document the default values set by the chart type.
*
* @return void
*/
protected function set_defaults() {
// For the child classes to extend.
}
/**
* Set the chart labels.
*
* @param string[] $labels The labels.
*/
public function set_labels(array $labels) {
$this->labels = $labels;
}
/**
* Set the title.
*
* @param string $title The title.
*/
public function set_title($title) {
$this->title = $title;
}
/**
* Set an X axis.
*
* Note that this will override any predefined axis without warning.
*
* @param chart_axis $axis The axis.
* @param int $index The index of the axis.
*/
public function set_xaxis(chart_axis $axis, $index = 0) {
return $this->xaxes[$index] = $axis;
}
/**
* Set an Y axis.
*
* Note that this will override any predefined axis without warning.
*
* @param chart_axis $axis The axis.
* @param int $index The index of the axis.
*/
public function set_yaxis(chart_axis $axis, $index = 0) {
return $this->yaxes[$index] = $axis;
}

View File

@ -37,39 +37,81 @@ use JsonSerializable;
*/
class chart_series implements JsonSerializable {
/** Default type for a series. */
const TYPE_DEFAULT = null;
/** Series of type line. */
const TYPE_LINE = 'line';
/** @var string Color of the series. */
protected $color;
/** @var string Label for this series. */
protected $label;
/** @var string Type of the series. */
protected $type = self::TYPE_DEFAULT;
/** @var float[] Values of the series. */
protected $values = [];
/**
* Constructor.
*
* @param string $label The label of the series.
* @param float[] $values The values of this series.
*/
public function __construct($label, $values) {
$this->values = $values;
$this->label = $label;
}
/**
* Get the color.
*
* @return string
*/
public function get_color() {
return $this->color;
}
/**
* Get the number of values in this series.
*
* @return int
*/
public function get_count() {
return count($this->values);
}
/**
* Get the label of the series.
*
* @return string
*/
public function get_label() {
return $this->label;
}
/**
* Get the type of series.
*
* @return string
*/
public function get_type() {
return $this->type;
}
/**
* Get the values of the series.
*
* @return [type]
*/
public function get_values() {
return $this->values;
}
/**
* Serialize the object.
*
* @return array
*/
public function jsonSerialize() {
$data = [
'label' => $this->label,
@ -80,10 +122,20 @@ class chart_series implements JsonSerializable {
return $data;
}
/**
* Set the color of the series.
*
* @param string $color CSS compatible color.
*/
public function set_color($color) {
$this->color = $color;
}
/**
* Set the type of the series.
*
* @param string $type Constant value from self::TYPE_*.
*/
public function set_type($type) {
if (!in_array($type, [self::TYPE_DEFAULT, self::TYPE_LINE])) {
throw new coding_exception('Invalid serie type.');