From 4a1bf4304b8d633821f25315b8d01ffdc062e59d Mon Sep 17 00:00:00 2001 From: Pedro Jordao Date: Tue, 15 Apr 2025 10:51:16 -0300 Subject: [PATCH] MDL-85158 libraries: Configure responsive options for ChartJS Currently, the attributes to configure the responsiveness of a chart are being set statically. With this change it will be possible to update these static attributes in order to change the aspectRatio and resizeDelay of the chart. --- .upgradenotes/MDL-85158-2025042205481782.yml | 8 +++++ lib/amd/build/chart_base.min.js | 2 +- lib/amd/build/chart_base.min.js.map | 2 +- lib/amd/build/chart_output_chartjs.min.js | 2 +- lib/amd/build/chart_output_chartjs.min.js.map | 2 +- lib/amd/src/chart_base.js | 32 +++++++++++++++++++ lib/amd/src/chart_output_chartjs.js | 11 +++++++ lib/classes/chart_base.php | 22 +++++++++++++ lib/tests/other/chartjstestpage.php | 1 + 9 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 .upgradenotes/MDL-85158-2025042205481782.yml diff --git a/.upgradenotes/MDL-85158-2025042205481782.yml b/.upgradenotes/MDL-85158-2025042205481782.yml new file mode 100644 index 00000000000..cc6faba99f5 --- /dev/null +++ b/.upgradenotes/MDL-85158-2025042205481782.yml @@ -0,0 +1,8 @@ +issueNumber: MDL-85158 +notes: + core: + - message: >- + Added support for configurable `aspectRatio` in charts rendered using + Chart.js. This enables developers to control chart sizing more precisely + via the `chart_base` API and the frontend renderer. + type: improved diff --git a/lib/amd/build/chart_base.min.js b/lib/amd/build/chart_base.min.js index 0eb14444f47..eda0ac340ef 100644 --- a/lib/amd/build/chart_base.min.js +++ b/lib/amd/build/chart_base.min.js @@ -5,6 +5,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @module core/chart_base */ -define("core/chart_base",["core/chart_series","core/chart_axis"],(function(Series,Axis){function Base(){this._series=[],this._labels=[],this._xaxes=[],this._yaxes=[],this._setDefaults()}return Base.prototype._series=null,Base.prototype._labels=null,Base.prototype._legendOptions=null,Base.prototype._title=null,Base.prototype._xaxes=null,Base.prototype._yaxes=null,Base.prototype.COLORSET=["#f3c300","#875692","#f38400","#a1caf1","#be0032","#c2b280","#7f180d","#008856","#e68fac","#0067a5"],Base.prototype._configColorSet=null,Base.prototype.TYPE=null,Base.prototype.addSeries=function(series){if(this._validateSeries(series),this._series.push(series),null===series.getColor()){var configColorSet=this.getConfigColorSet()||Base.prototype.COLORSET;series.setColor(configColorSet[this._series.length%configColorSet.length])}},Base.prototype.create=function(Klass,data){var Chart=new Klass;return Chart.setConfigColorSet(data.config_colorset),Chart.setLabels(data.labels),Chart.setTitle(data.title),data.legend_options&&Chart.setLegendOptions(data.legend_options),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)})),Chart},Base.prototype.__getAxis=function(xy,index,createIfNotExists){var axis,axes="x"===xy?this._xaxes:this._yaxes,setAxis=("x"===xy?this.setXAxis:this.setYAxis).bind(this);if(createIfNotExists=void 0!==createIfNotExists&&createIfNotExists,void 0===(axis=axes[index=void 0===index?0:index])){if(!createIfNotExists)throw new Error("Unknown axis.");setAxis(axis=new Axis,index)}return axis},Base.prototype.getConfigColorSet=function(){return this._configColorSet},Base.prototype.getLabels=function(){return this._labels},Base.prototype.getLegendOptions=function(){return this._legendOptions},Base.prototype.getSeries=function(){return this._series},Base.prototype.getTitle=function(){return this._title},Base.prototype.getType=function(){if(!this.TYPE)throw new Error("The TYPE property has not been set.");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.setConfigColorSet=function(colorset){this._configColorSet=colorset},Base.prototype._setDefaults=function(){},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.");this._labels=labels},Base.prototype.setLegendOptions=function(legendOptions){if("object"!=typeof legendOptions)throw new Error("Setting legend with non-object value:"+legendOptions);this._legendOptions=legendOptions},Base.prototype.setTitle=function(title){this._title=title},Base.prototype.setXAxis=function(axis,index){index=void 0===index?0:index,this._validateAxis("x",axis,index),this._xaxes[index]=axis},Base.prototype.setYAxis=function(axis,index){index=void 0===index?0:index,this._validateAxis("y",axis,index),this._yaxes[index]=axis},Base.prototype._validateAxis=function(xy,axis,index){if((index=void 0===index?0:index)>0&&void 0===("x"==xy?this._xaxes:this._yaxes)[index-1])throw new Error("Missing "+xy+" axis at index lower than "+index)},Base.prototype._validateSeries=function(series){if(this._series.length&&this._series[0].getCount()!=series.getCount())throw new Error("Series do not have an equal number of values.");if(this._labels.length&&this._labels.length!=series.getCount())throw new Error("Series must match label values.")},Base})); +define("core/chart_base",["core/chart_series","core/chart_axis"],(function(Series,Axis){function Base(){this._series=[],this._labels=[],this._xaxes=[],this._yaxes=[],this._setDefaults()}return Base.prototype._series=null,Base.prototype._labels=null,Base.prototype._legendOptions=null,Base.prototype._title=null,Base.prototype._xaxes=null,Base.prototype._yaxes=null,Base.prototype.COLORSET=["#f3c300","#875692","#f38400","#a1caf1","#be0032","#c2b280","#7f180d","#008856","#e68fac","#0067a5"],Base.prototype._configColorSet=null,Base.prototype.TYPE=null,Base.prototype._responsiveOptions=null,Base.prototype.addSeries=function(series){if(this._validateSeries(series),this._series.push(series),null===series.getColor()){var configColorSet=this.getConfigColorSet()||Base.prototype.COLORSET;series.setColor(configColorSet[this._series.length%configColorSet.length])}},Base.prototype.create=function(Klass,data){var Chart=new Klass;return Chart.setConfigColorSet(data.config_colorset),Chart.setLabels(data.labels),Chart.setTitle(data.title),data.legend_options&&Chart.setLegendOptions(data.legend_options),data.responsive_options&&Chart.setResponsiveOptions(data.responsive_options),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)})),Chart},Base.prototype.__getAxis=function(xy,index,createIfNotExists){var axis,axes="x"===xy?this._xaxes:this._yaxes,setAxis=("x"===xy?this.setXAxis:this.setYAxis).bind(this);if(createIfNotExists=void 0!==createIfNotExists&&createIfNotExists,void 0===(axis=axes[index=void 0===index?0:index])){if(!createIfNotExists)throw new Error("Unknown axis.");setAxis(axis=new Axis,index)}return axis},Base.prototype.getConfigColorSet=function(){return this._configColorSet},Base.prototype.getLabels=function(){return this._labels},Base.prototype.getLegendOptions=function(){return this._legendOptions},Base.prototype.getSeries=function(){return this._series},Base.prototype.getTitle=function(){return this._title},Base.prototype.getType=function(){if(!this.TYPE)throw new Error("The TYPE property has not been set.");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.getResponsiveOptions=function(){return this._responsiveOptions},Base.prototype.setConfigColorSet=function(colorset){this._configColorSet=colorset},Base.prototype._setDefaults=function(){},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.");this._labels=labels},Base.prototype.setLegendOptions=function(legendOptions){if("object"!=typeof legendOptions)throw new Error("Setting legend with non-object value:"+legendOptions);this._legendOptions=legendOptions},Base.prototype.setTitle=function(title){this._title=title},Base.prototype.setXAxis=function(axis,index){index=void 0===index?0:index,this._validateAxis("x",axis,index),this._xaxes[index]=axis},Base.prototype.setYAxis=function(axis,index){index=void 0===index?0:index,this._validateAxis("y",axis,index),this._yaxes[index]=axis},Base.prototype.setResponsiveOptions=function(responsiveOptions){if("object"!=typeof responsiveOptions)throw new Error("Setting responsive options with non-object value:"+responsiveOptions);this._responsiveOptions=responsiveOptions},Base.prototype._validateAxis=function(xy,axis,index){if((index=void 0===index?0:index)>0&&void 0===("x"==xy?this._xaxes:this._yaxes)[index-1])throw new Error("Missing "+xy+" axis at index lower than "+index)},Base.prototype._validateSeries=function(series){if(this._series.length&&this._series[0].getCount()!=series.getCount())throw new Error("Series do not have an equal number of values.");if(this._labels.length&&this._labels.length!=series.getCount())throw new Error("Series must match label values.")},Base})); //# sourceMappingURL=chart_base.min.js.map \ No newline at end of file diff --git a/lib/amd/build/chart_base.min.js.map b/lib/amd/build/chart_base.min.js.map index 585492df378..8ade4d97c92 100644 --- a/lib/amd/build/chart_base.min.js.map +++ b/lib/amd/build/chart_base.min.js.map @@ -1 +1 @@ -{"version":3,"file":"chart_base.min.js","sources":["../src/chart_base.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Chart base.\n *\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @module core/chart_base\n */\ndefine(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {\n\n /**\n * Chart base.\n *\n * The constructor of a chart must never take any argument.\n *\n * {@link module:core/chart_base#_setDefault} to set the defaults on instantiation.\n *\n * @class\n */\n function Base() {\n this._series = [];\n this._labels = [];\n this._xaxes = [];\n this._yaxes = [];\n\n this._setDefaults();\n }\n\n /**\n * The series constituting this chart.\n *\n * @protected\n * @type {module:core/chart_series[]}\n */\n Base.prototype._series = null;\n\n /**\n * The labels of the X axis when categorised.\n *\n * @protected\n * @type {String[]}\n */\n Base.prototype._labels = null;\n\n /**\n * Options for chart legend display.\n *\n * @protected\n * @type {Object}\n */\n Base.prototype._legendOptions = null;\n\n /**\n * The title of the chart.\n *\n * @protected\n * @type {String}\n */\n Base.prototype._title = null;\n\n /**\n * The X axes.\n *\n * @protected\n * @type {module:core/chart_axis[]}\n */\n Base.prototype._xaxes = null;\n\n /**\n * The Y axes.\n *\n * @protected\n * @type {module:core/chart_axis[]}\n */\n Base.prototype._yaxes = null;\n\n /**\n * Colours to pick from when automatically assigning them.\n *\n * @const\n * @type {String[]}\n */\n Base.prototype.COLORSET = ['#f3c300', '#875692', '#f38400', '#a1caf1', '#be0032', '#c2b280', '#7f180d', '#008856',\n '#e68fac', '#0067a5'];\n\n /**\n * Set of colours defined by setting $CFG->chart_colorset to be picked when automatically assigning them.\n *\n * @type {String[]}\n * @protected\n */\n Base.prototype._configColorSet = null;\n\n /**\n * The type of chart.\n *\n * @abstract\n * @type {String}\n * @const\n */\n Base.prototype.TYPE = null;\n\n /**\n * Add a series to the chart.\n *\n * This will automatically assign a color to the series if it does not have one.\n *\n * @param {module:core/chart_series} series The series to add.\n */\n Base.prototype.addSeries = function(series) {\n this._validateSeries(series);\n this._series.push(series);\n\n // Give a default color from the set.\n if (series.getColor() === null) {\n var configColorSet = this.getConfigColorSet() || Base.prototype.COLORSET;\n series.setColor(configColorSet[this._series.length % configColorSet.length]);\n }\n };\n\n /**\n * Create a new instance of a chart from serialised data.\n *\n * the serialised attributes they offer and support.\n *\n * @static\n * @method create\n * @param {module:core/chart_base} Klass The class oject representing the type of chart to instantiate.\n * @param {Object} data The data of the chart.\n * @return {module:core/chart_base}\n */\n Base.prototype.create = function(Klass, data) {\n // TODO Not convinced about the usage of Klass here but I can't figure out a way\n // to have a reference to the class in the sub classes, in PHP I'd do new self().\n var Chart = new Klass();\n Chart.setConfigColorSet(data.config_colorset);\n Chart.setLabels(data.labels);\n Chart.setTitle(data.title);\n if (data.legend_options) {\n Chart.setLegendOptions(data.legend_options);\n }\n data.series.forEach(function(seriesData) {\n Chart.addSeries(Series.prototype.create(seriesData));\n });\n data.axes.x.forEach(function(axisData, i) {\n Chart.setXAxis(Axis.prototype.create(axisData), i);\n });\n data.axes.y.forEach(function(axisData, i) {\n Chart.setYAxis(Axis.prototype.create(axisData), i);\n });\n return Chart;\n };\n\n /**\n * Get an axis.\n *\n * @private\n * @param {String} xy Accepts the values 'x' or 'y'.\n * @param {Number} [index=0] The index of the axis of its type.\n * @param {Bool} [createIfNotExists=false] When true, create an instance if it does not exist.\n * @return {module:core/chart_axis}\n */\n Base.prototype.__getAxis = function(xy, index, createIfNotExists) {\n var axes = xy === 'x' ? this._xaxes : this._yaxes,\n setAxis = (xy === 'x' ? this.setXAxis : this.setYAxis).bind(this),\n axis;\n\n index = typeof index === 'undefined' ? 0 : index;\n createIfNotExists = typeof createIfNotExists === 'undefined' ? false : createIfNotExists;\n axis = axes[index];\n\n if (typeof axis === 'undefined') {\n if (!createIfNotExists) {\n throw new Error('Unknown axis.');\n }\n axis = new Axis();\n setAxis(axis, index);\n }\n\n return axis;\n };\n\n /**\n * Get colours defined by setting.\n *\n * @return {String[]}\n */\n Base.prototype.getConfigColorSet = function() {\n return this._configColorSet;\n };\n\n /**\n * Get the labels of the X axis.\n *\n * @return {String[]}\n */\n Base.prototype.getLabels = function() {\n return this._labels;\n };\n\n /**\n * Get whether to display the chart legend.\n *\n * @return {Bool}\n */\n Base.prototype.getLegendOptions = function() {\n return this._legendOptions;\n };\n\n /**\n * Get the series.\n *\n * @return {module:core/chart_series[]}\n */\n Base.prototype.getSeries = function() {\n return this._series;\n };\n\n /**\n * Get the title of the chart.\n *\n * @return {String}\n */\n Base.prototype.getTitle = function() {\n return this._title;\n };\n\n /**\n * Get the type of chart.\n *\n * @see module:core/chart_base#TYPE\n * @return {String}\n */\n Base.prototype.getType = function() {\n if (!this.TYPE) {\n throw new Error('The TYPE property has not been set.');\n }\n return this.TYPE;\n };\n\n /**\n * Get the X axes.\n *\n * @return {module:core/chart_axis[]}\n */\n Base.prototype.getXAxes = function() {\n return this._xaxes;\n };\n\n /**\n * Get an X axis.\n *\n * @param {Number} [index=0] The index of the axis.\n * @param {Bool} [createIfNotExists=false] Create the instance of it does not exist at index.\n * @return {module:core/chart_axis}\n */\n Base.prototype.getXAxis = function(index, createIfNotExists) {\n return this.__getAxis('x', index, createIfNotExists);\n };\n\n /**\n * Get the Y axes.\n *\n * @return {module:core/chart_axis[]}\n */\n Base.prototype.getYAxes = function() {\n return this._yaxes;\n };\n\n /**\n * Get an Y axis.\n *\n * @param {Number} [index=0] The index of the axis.\n * @param {Bool} [createIfNotExists=false] Create the instance of it does not exist at index.\n * @return {module:core/chart_axis}\n */\n Base.prototype.getYAxis = function(index, createIfNotExists) {\n return this.__getAxis('y', index, createIfNotExists);\n };\n\n /**\n * Set colours defined by setting.\n *\n * @param {String[]} colorset An array of css colours.\n * @protected\n */\n Base.prototype.setConfigColorSet = function(colorset) {\n this._configColorSet = colorset;\n };\n\n /**\n * Set the defaults for this chart type.\n *\n * Child classes can extend this to set defaults values on instantiation.\n *\n * emphasize and self-document the defaults values set by the chart type.\n *\n * @protected\n */\n Base.prototype._setDefaults = function() {\n // For the children to extend.\n };\n\n /**\n * Set the labels of the X axis.\n *\n * This requires for each series to contain strictly as many values as there\n * are labels.\n *\n * @param {String[]} labels The labels.\n */\n Base.prototype.setLabels = function(labels) {\n if (labels.length && this._series.length && this._series[0].length != labels.length) {\n throw new Error('Series must match label values.');\n }\n this._labels = labels;\n };\n\n /**\n * Set options for chart legend display.\n *\n * @param {Object} legendOptions\n */\n Base.prototype.setLegendOptions = function(legendOptions) {\n if (typeof legendOptions !== 'object') {\n throw new Error('Setting legend with non-object value:' + legendOptions);\n }\n this._legendOptions = legendOptions;\n };\n\n /**\n * Set the title of the chart.\n *\n * @param {String} title The title.\n */\n Base.prototype.setTitle = function(title) {\n this._title = title;\n };\n\n /**\n * Set an X axis.\n *\n * Note that this will override any predefined axis without warning.\n *\n * @param {module:core/chart_axis} axis The axis.\n * @param {Number} [index=0] The index of the axis.\n */\n Base.prototype.setXAxis = function(axis, index) {\n index = typeof index === 'undefined' ? 0 : index;\n this._validateAxis('x', axis, index);\n this._xaxes[index] = axis;\n };\n\n /**\n * Set a Y axis.\n *\n * Note that this will override any predefined axis without warning.\n *\n * @param {module:core/chart_axis} axis The axis.\n * @param {Number} [index=0] The index of the axis.\n */\n Base.prototype.setYAxis = function(axis, index) {\n index = typeof index === 'undefined' ? 0 : index;\n this._validateAxis('y', axis, index);\n this._yaxes[index] = axis;\n };\n\n /**\n * Validate an axis.\n *\n * @protected\n * @param {String} xy X or Y axis.\n * @param {module:core/chart_axis} axis The axis to validate.\n * @param {Number} [index=0] The index of the axis.\n */\n Base.prototype._validateAxis = function(xy, axis, index) {\n index = typeof index === 'undefined' ? 0 : index;\n if (index > 0) {\n var axes = xy == 'x' ? this._xaxes : this._yaxes;\n if (typeof axes[index - 1] === 'undefined') {\n throw new Error('Missing ' + xy + ' axis at index lower than ' + index);\n }\n }\n };\n\n /**\n * Validate a series.\n *\n * @protected\n * @param {module:core/chart_series} series The series to validate.\n */\n Base.prototype._validateSeries = function(series) {\n if (this._series.length && this._series[0].getCount() != series.getCount()) {\n throw new Error('Series do not have an equal number of values.');\n\n } else if (this._labels.length && this._labels.length != series.getCount()) {\n throw new Error('Series must match label values.');\n }\n };\n\n return Base;\n\n});\n"],"names":["define","Series","Axis","Base","_series","_labels","_xaxes","_yaxes","_setDefaults","prototype","_legendOptions","_title","COLORSET","_configColorSet","TYPE","addSeries","series","_validateSeries","push","getColor","configColorSet","this","getConfigColorSet","setColor","length","create","Klass","data","Chart","setConfigColorSet","config_colorset","setLabels","labels","setTitle","title","legend_options","setLegendOptions","forEach","seriesData","axes","x","axisData","i","setXAxis","y","setYAxis","__getAxis","xy","index","createIfNotExists","axis","setAxis","bind","Error","getLabels","getLegendOptions","getSeries","getTitle","getType","getXAxes","getXAxis","getYAxes","getYAxis","colorset","legendOptions","_validateAxis","getCount"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"chart_base.min.js","sources":["../src/chart_base.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Chart base.\n *\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @module core/chart_base\n */\ndefine(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {\n\n /**\n * Chart base.\n *\n * The constructor of a chart must never take any argument.\n *\n * {@link module:core/chart_base#_setDefault} to set the defaults on instantiation.\n *\n * @class\n */\n function Base() {\n this._series = [];\n this._labels = [];\n this._xaxes = [];\n this._yaxes = [];\n\n this._setDefaults();\n }\n\n /**\n * The series constituting this chart.\n *\n * @protected\n * @type {module:core/chart_series[]}\n */\n Base.prototype._series = null;\n\n /**\n * The labels of the X axis when categorised.\n *\n * @protected\n * @type {String[]}\n */\n Base.prototype._labels = null;\n\n /**\n * Options for chart legend display.\n *\n * @protected\n * @type {Object}\n */\n Base.prototype._legendOptions = null;\n\n /**\n * The title of the chart.\n *\n * @protected\n * @type {String}\n */\n Base.prototype._title = null;\n\n /**\n * The X axes.\n *\n * @protected\n * @type {module:core/chart_axis[]}\n */\n Base.prototype._xaxes = null;\n\n /**\n * The Y axes.\n *\n * @protected\n * @type {module:core/chart_axis[]}\n */\n Base.prototype._yaxes = null;\n\n /**\n * Colours to pick from when automatically assigning them.\n *\n * @const\n * @type {String[]}\n */\n Base.prototype.COLORSET = ['#f3c300', '#875692', '#f38400', '#a1caf1', '#be0032', '#c2b280', '#7f180d', '#008856',\n '#e68fac', '#0067a5'];\n\n /**\n * Set of colours defined by setting $CFG->chart_colorset to be picked when automatically assigning them.\n *\n * @type {String[]}\n * @protected\n */\n Base.prototype._configColorSet = null;\n\n /**\n * The type of chart.\n *\n * @abstract\n * @type {String}\n * @const\n */\n Base.prototype.TYPE = null;\n\n /**\n * Options for chart responsiveness.\n *\n * @protected\n * @type {Object}\n */\n Base.prototype._responsiveOptions = null;\n\n /**\n * Add a series to the chart.\n *\n * This will automatically assign a color to the series if it does not have one.\n *\n * @param {module:core/chart_series} series The series to add.\n */\n Base.prototype.addSeries = function(series) {\n this._validateSeries(series);\n this._series.push(series);\n\n // Give a default color from the set.\n if (series.getColor() === null) {\n var configColorSet = this.getConfigColorSet() || Base.prototype.COLORSET;\n series.setColor(configColorSet[this._series.length % configColorSet.length]);\n }\n };\n\n /**\n * Create a new instance of a chart from serialised data.\n *\n * the serialised attributes they offer and support.\n *\n * @static\n * @method create\n * @param {module:core/chart_base} Klass The class oject representing the type of chart to instantiate.\n * @param {Object} data The data of the chart.\n * @return {module:core/chart_base}\n */\n Base.prototype.create = function(Klass, data) {\n // TODO Not convinced about the usage of Klass here but I can't figure out a way\n // to have a reference to the class in the sub classes, in PHP I'd do new self().\n var Chart = new Klass();\n Chart.setConfigColorSet(data.config_colorset);\n Chart.setLabels(data.labels);\n Chart.setTitle(data.title);\n if (data.legend_options) {\n Chart.setLegendOptions(data.legend_options);\n }\n if (data.responsive_options) {\n Chart.setResponsiveOptions(data.responsive_options);\n }\n data.series.forEach(function(seriesData) {\n Chart.addSeries(Series.prototype.create(seriesData));\n });\n data.axes.x.forEach(function(axisData, i) {\n Chart.setXAxis(Axis.prototype.create(axisData), i);\n });\n data.axes.y.forEach(function(axisData, i) {\n Chart.setYAxis(Axis.prototype.create(axisData), i);\n });\n return Chart;\n };\n\n /**\n * Get an axis.\n *\n * @private\n * @param {String} xy Accepts the values 'x' or 'y'.\n * @param {Number} [index=0] The index of the axis of its type.\n * @param {Bool} [createIfNotExists=false] When true, create an instance if it does not exist.\n * @return {module:core/chart_axis}\n */\n Base.prototype.__getAxis = function(xy, index, createIfNotExists) {\n var axes = xy === 'x' ? this._xaxes : this._yaxes,\n setAxis = (xy === 'x' ? this.setXAxis : this.setYAxis).bind(this),\n axis;\n\n index = typeof index === 'undefined' ? 0 : index;\n createIfNotExists = typeof createIfNotExists === 'undefined' ? false : createIfNotExists;\n axis = axes[index];\n\n if (typeof axis === 'undefined') {\n if (!createIfNotExists) {\n throw new Error('Unknown axis.');\n }\n axis = new Axis();\n setAxis(axis, index);\n }\n\n return axis;\n };\n\n /**\n * Get colours defined by setting.\n *\n * @return {String[]}\n */\n Base.prototype.getConfigColorSet = function() {\n return this._configColorSet;\n };\n\n /**\n * Get the labels of the X axis.\n *\n * @return {String[]}\n */\n Base.prototype.getLabels = function() {\n return this._labels;\n };\n\n /**\n * Get whether to display the chart legend.\n *\n * @return {Bool}\n */\n Base.prototype.getLegendOptions = function() {\n return this._legendOptions;\n };\n\n /**\n * Get the series.\n *\n * @return {module:core/chart_series[]}\n */\n Base.prototype.getSeries = function() {\n return this._series;\n };\n\n /**\n * Get the title of the chart.\n *\n * @return {String}\n */\n Base.prototype.getTitle = function() {\n return this._title;\n };\n\n /**\n * Get the type of chart.\n *\n * @see module:core/chart_base#TYPE\n * @return {String}\n */\n Base.prototype.getType = function() {\n if (!this.TYPE) {\n throw new Error('The TYPE property has not been set.');\n }\n return this.TYPE;\n };\n\n /**\n * Get the X axes.\n *\n * @return {module:core/chart_axis[]}\n */\n Base.prototype.getXAxes = function() {\n return this._xaxes;\n };\n\n /**\n * Get an X axis.\n *\n * @param {Number} [index=0] The index of the axis.\n * @param {Bool} [createIfNotExists=false] Create the instance of it does not exist at index.\n * @return {module:core/chart_axis}\n */\n Base.prototype.getXAxis = function(index, createIfNotExists) {\n return this.__getAxis('x', index, createIfNotExists);\n };\n\n /**\n * Get the Y axes.\n *\n * @return {module:core/chart_axis[]}\n */\n Base.prototype.getYAxes = function() {\n return this._yaxes;\n };\n\n /**\n * Get an Y axis.\n *\n * @param {Number} [index=0] The index of the axis.\n * @param {Bool} [createIfNotExists=false] Create the instance of it does not exist at index.\n * @return {module:core/chart_axis}\n */\n Base.prototype.getYAxis = function(index, createIfNotExists) {\n return this.__getAxis('y', index, createIfNotExists);\n };\n\n /**\n * Get responsive options for the chart.\n *\n * @return {Object}\n */\n Base.prototype.getResponsiveOptions = function() {\n return this._responsiveOptions;\n };\n\n /**\n * Set colours defined by setting.\n *\n * @param {String[]} colorset An array of css colours.\n * @protected\n */\n Base.prototype.setConfigColorSet = function(colorset) {\n this._configColorSet = colorset;\n };\n\n /**\n * Set the defaults for this chart type.\n *\n * Child classes can extend this to set defaults values on instantiation.\n *\n * emphasize and self-document the defaults values set by the chart type.\n *\n * @protected\n */\n Base.prototype._setDefaults = function() {\n // For the children to extend.\n };\n\n /**\n * Set the labels of the X axis.\n *\n * This requires for each series to contain strictly as many values as there\n * are labels.\n *\n * @param {String[]} labels The labels.\n */\n Base.prototype.setLabels = function(labels) {\n if (labels.length && this._series.length && this._series[0].length != labels.length) {\n throw new Error('Series must match label values.');\n }\n this._labels = labels;\n };\n\n /**\n * Set options for chart legend display.\n *\n * @param {Object} legendOptions\n */\n Base.prototype.setLegendOptions = function(legendOptions) {\n if (typeof legendOptions !== 'object') {\n throw new Error('Setting legend with non-object value:' + legendOptions);\n }\n this._legendOptions = legendOptions;\n };\n\n /**\n * Set the title of the chart.\n *\n * @param {String} title The title.\n */\n Base.prototype.setTitle = function(title) {\n this._title = title;\n };\n\n /**\n * Set an X axis.\n *\n * Note that this will override any predefined axis without warning.\n *\n * @param {module:core/chart_axis} axis The axis.\n * @param {Number} [index=0] The index of the axis.\n */\n Base.prototype.setXAxis = function(axis, index) {\n index = typeof index === 'undefined' ? 0 : index;\n this._validateAxis('x', axis, index);\n this._xaxes[index] = axis;\n };\n\n /**\n * Set a Y axis.\n *\n * Note that this will override any predefined axis without warning.\n *\n * @param {module:core/chart_axis} axis The axis.\n * @param {Number} [index=0] The index of the axis.\n */\n Base.prototype.setYAxis = function(axis, index) {\n index = typeof index === 'undefined' ? 0 : index;\n this._validateAxis('y', axis, index);\n this._yaxes[index] = axis;\n };\n\n /**\n * Set responsive options for chart.\n *\n * @param {Object} responsiveOptions\n */\n Base.prototype.setResponsiveOptions = function(responsiveOptions) {\n if (typeof responsiveOptions !== 'object') {\n throw new Error('Setting responsive options with non-object value:' + responsiveOptions);\n }\n this._responsiveOptions = responsiveOptions;\n };\n\n /**\n * Validate an axis.\n *\n * @protected\n * @param {String} xy X or Y axis.\n * @param {module:core/chart_axis} axis The axis to validate.\n * @param {Number} [index=0] The index of the axis.\n */\n Base.prototype._validateAxis = function(xy, axis, index) {\n index = typeof index === 'undefined' ? 0 : index;\n if (index > 0) {\n var axes = xy == 'x' ? this._xaxes : this._yaxes;\n if (typeof axes[index - 1] === 'undefined') {\n throw new Error('Missing ' + xy + ' axis at index lower than ' + index);\n }\n }\n };\n\n /**\n * Validate a series.\n *\n * @protected\n * @param {module:core/chart_series} series The series to validate.\n */\n Base.prototype._validateSeries = function(series) {\n if (this._series.length && this._series[0].getCount() != series.getCount()) {\n throw new Error('Series do not have an equal number of values.');\n\n } else if (this._labels.length && this._labels.length != series.getCount()) {\n throw new Error('Series must match label values.');\n }\n };\n\n return Base;\n\n});\n"],"names":["define","Series","Axis","Base","_series","_labels","_xaxes","_yaxes","_setDefaults","prototype","_legendOptions","_title","COLORSET","_configColorSet","TYPE","_responsiveOptions","addSeries","series","_validateSeries","push","getColor","configColorSet","this","getConfigColorSet","setColor","length","create","Klass","data","Chart","setConfigColorSet","config_colorset","setLabels","labels","setTitle","title","legend_options","setLegendOptions","responsive_options","setResponsiveOptions","forEach","seriesData","axes","x","axisData","i","setXAxis","y","setYAxis","__getAxis","xy","index","createIfNotExists","axis","setAxis","bind","Error","getLabels","getLegendOptions","getSeries","getTitle","getType","getXAxes","getXAxis","getYAxes","getYAxis","getResponsiveOptions","colorset","legendOptions","_validateAxis","responsiveOptions","getCount"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/build/chart_output_chartjs.min.js b/lib/amd/build/chart_output_chartjs.min.js index e381c74d98b..181823e4f31 100644 --- a/lib/amd/build/chart_output_chartjs.min.js +++ b/lib/amd/build/chart_output_chartjs.min.js @@ -5,6 +5,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @module core/chart_output_chartjs */ -define("core/chart_output_chartjs",["jquery","core/chartjs","core/chart_axis","core/chart_bar","core/chart_output_base","core/chart_line","core/chart_pie","core/chart_series"],(function($,Chartjs,Axis,Bar,Base,Line,Pie,Series){var makeAxisId=function(xy,index){return"axis-"+xy+"-"+index};function Output(){Base.prototype.constructor.apply(this,arguments),this._canvas=this._node,"CANVAS"!=this._canvas.prop("tagName")&&(this._canvas=$(""),this._node.append(this._canvas)),this._build()}return Output.prototype=Object.create(Base.prototype),Output.prototype._config=null,Output.prototype._chartjs=null,Output.prototype._canvas=null,Output.prototype._build=function(){this._config=this._makeConfig(),this._chartjs=new Chartjs(this._canvas[0],this._config)},Output.prototype._cleanData=function(data){return data instanceof Array?data.map((function(value){return $("").html(value).text()})):$("").html(data).text()},Output.prototype._getChartType=function(){var type=this._chart.getType();return this._chart.getType()===Bar.prototype.TYPE&&!0===this._chart.getHorizontal()?type="horizontalBar":this._chart.getType()===Pie.prototype.TYPE&&!0===this._chart.getDoughnut()&&(type="doughnut"),type},Output.prototype._makeAxisConfig=function(axis,xy,index){var scaleData={id:makeAxisId(xy,index)};return axis.getPosition()!==Axis.prototype.POS_DEFAULT&&(scaleData.position=axis.getPosition()),null!==axis.getLabel()&&(scaleData.title={display:!0,text:this._cleanData(axis.getLabel())}),null!==axis.getStepSize()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.stepSize=axis.getStepSize()),null!==axis.getMax()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.max=axis.getMax()),null!==axis.getMin()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.min=axis.getMin()),scaleData},Output.prototype._makeConfig=function(){var charType=this._getChartType(),labels=this._cleanData(this._chart.getLabels()),config={type:charType,data:{labels:labels.map((label=>label.length>25?"".concat(label.substring(0,25),"..."):label)),datasets:this._makeDatasetsConfig()},options:{locale:document.documentElement.getAttribute("lang"),responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:null!==this._chart.getTitle(),text:this._cleanData(this._chart.getTitle())}}}};"horizontalBar"===charType&&(config.type="bar",config.options.indexAxis="y");var legendOptions=this._chart.getLegendOptions();return legendOptions&&(config.options.plugins.legend=legendOptions),this._chart.getXAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.x=this._makeAxisConfig(axis,"x",i),null!==axisLabels&&(config.options.scales.x.ticks.callback=function(value,index){return axisLabels[index]||""}),config.options.scales.x.stacked=this._isStacked()}.bind(this)),this._chart.getYAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.y=this._makeAxisConfig(axis,"y",i),null!==axisLabels&&(config.options.scales.y.ticks.callback=function(value){return axisLabels[parseInt(value,10)]||""}),config.options.scales.y.stacked=this._isStacked()}.bind(this)),config.options.plugins.tooltip={callbacks:{title:ctx=>labels[ctx[0].dataIndex].match(/.{1,80}/g),label:this._makeTooltip.bind(this)}},config},Output.prototype._makeDatasetsConfig=function(){return this._chart.getSeries().map(function(series){var colors=series.hasColoredValues()?series.getColors():series.getColor(),dataset={label:this._cleanData(series.getLabel()),data:series.getValues(),type:series.getType(),fill:series.getFill(),backgroundColor:colors,borderColor:this._chart.getType()==Pie.prototype.TYPE?"#fff":colors,tension:this._isSmooth(series)?.3:0};return null!==series.getXAxis()&&(dataset.xAxisID=makeAxisId("x",series.getXAxis())),null!==series.getYAxis()&&(dataset.yAxisID=makeAxisId("y",series.getYAxis())),dataset}.bind(this))},Output.prototype._makeTooltip=function(tooltipItem){const formatter=new Intl.NumberFormat(this._config.options.locale);var serieLabel=this._chart.getSeries()[tooltipItem.datasetIndex].getLabel(),chartData=tooltipItem.dataset.data,tooltipData=formatter.format(chartData[tooltipItem.dataIndex]),tooltip=[];if(this._chart.getType()===Pie.prototype.TYPE){var chartLabels=this._cleanData(this._chart.getLabels());tooltip.push(chartLabels[tooltipItem.dataIndex]+" - "+this._cleanData(serieLabel)+": "+tooltipData)}else tooltip.push(this._cleanData(serieLabel)+": "+tooltipData);return tooltip},Output.prototype._isSmooth=function(series){var smooth=!1;return this._chart.getType()===Line.prototype.TYPE?null===(smooth=series.getSmooth())&&(smooth=this._chart.getSmooth()):series.getType()===Series.prototype.TYPE_LINE&&(smooth=series.getSmooth()),smooth},Output.prototype._isStacked=function(){var stacked=!1;return this._chart.getType()===Bar.prototype.TYPE&&(stacked=this._chart.getStacked()),stacked},Output.prototype.update=function(){$.extend(!0,this._config,this._makeConfig()),this._chartjs.update()},Output})); +define("core/chart_output_chartjs",["jquery","core/chartjs","core/chart_axis","core/chart_bar","core/chart_output_base","core/chart_line","core/chart_pie","core/chart_series"],(function($,Chartjs,Axis,Bar,Base,Line,Pie,Series){var makeAxisId=function(xy,index){return"axis-"+xy+"-"+index};function Output(){Base.prototype.constructor.apply(this,arguments),this._canvas=this._node,"CANVAS"!=this._canvas.prop("tagName")&&(this._canvas=$(""),this._node.append(this._canvas)),this._build()}return Output.prototype=Object.create(Base.prototype),Output.prototype._config=null,Output.prototype._chartjs=null,Output.prototype._canvas=null,Output.prototype._build=function(){this._config=this._makeConfig(),this._chartjs=new Chartjs(this._canvas[0],this._config)},Output.prototype._cleanData=function(data){return data instanceof Array?data.map((function(value){return $("").html(value).text()})):$("").html(data).text()},Output.prototype._getChartType=function(){var type=this._chart.getType();return this._chart.getType()===Bar.prototype.TYPE&&!0===this._chart.getHorizontal()?type="horizontalBar":this._chart.getType()===Pie.prototype.TYPE&&!0===this._chart.getDoughnut()&&(type="doughnut"),type},Output.prototype._makeAxisConfig=function(axis,xy,index){var scaleData={id:makeAxisId(xy,index)};return axis.getPosition()!==Axis.prototype.POS_DEFAULT&&(scaleData.position=axis.getPosition()),null!==axis.getLabel()&&(scaleData.title={display:!0,text:this._cleanData(axis.getLabel())}),null!==axis.getStepSize()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.stepSize=axis.getStepSize()),null!==axis.getMax()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.max=axis.getMax()),null!==axis.getMin()&&(scaleData.ticks=scaleData.ticks||{},scaleData.ticks.min=axis.getMin()),scaleData},Output.prototype._makeConfig=function(){var charType=this._getChartType(),labels=this._cleanData(this._chart.getLabels()),config={type:charType,data:{labels:labels.map((label=>label.length>25?"".concat(label.substring(0,25),"..."):label)),datasets:this._makeDatasetsConfig()},options:{locale:document.documentElement.getAttribute("lang"),responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:null!==this._chart.getTitle(),text:this._cleanData(this._chart.getTitle())}}}};"horizontalBar"===charType&&(config.type="bar",config.options.indexAxis="y");var legendOptions=this._chart.getLegendOptions();legendOptions&&(config.options.plugins.legend=legendOptions);var responsiveOptions=this._chart.getResponsiveOptions();if(responsiveOptions){const responsiveProperties=["aspectRatio","maintainAspectRatio","responsive","resizeDelay"];for(const key of responsiveProperties){const value=responsiveOptions[key];null!=value&&(config.options[key]=value)}}return this._chart.getXAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.x=this._makeAxisConfig(axis,"x",i),null!==axisLabels&&(config.options.scales.x.ticks.callback=function(value,index){return axisLabels[index]||""}),config.options.scales.x.stacked=this._isStacked()}.bind(this)),this._chart.getYAxes().forEach(function(axis,i){var axisLabels=axis.getLabels();config.options.scales=config.options.scales||{},config.options.scales.y=this._makeAxisConfig(axis,"y",i),null!==axisLabels&&(config.options.scales.y.ticks.callback=function(value){return axisLabels[parseInt(value,10)]||""}),config.options.scales.y.stacked=this._isStacked()}.bind(this)),config.options.plugins.tooltip={callbacks:{title:ctx=>labels[ctx[0].dataIndex].match(/.{1,80}/g),label:this._makeTooltip.bind(this)}},config},Output.prototype._makeDatasetsConfig=function(){return this._chart.getSeries().map(function(series){var colors=series.hasColoredValues()?series.getColors():series.getColor(),dataset={label:this._cleanData(series.getLabel()),data:series.getValues(),type:series.getType(),fill:series.getFill(),backgroundColor:colors,borderColor:this._chart.getType()==Pie.prototype.TYPE?"#fff":colors,tension:this._isSmooth(series)?.3:0};return null!==series.getXAxis()&&(dataset.xAxisID=makeAxisId("x",series.getXAxis())),null!==series.getYAxis()&&(dataset.yAxisID=makeAxisId("y",series.getYAxis())),dataset}.bind(this))},Output.prototype._makeTooltip=function(tooltipItem){const formatter=new Intl.NumberFormat(this._config.options.locale);var serieLabel=this._chart.getSeries()[tooltipItem.datasetIndex].getLabel(),chartData=tooltipItem.dataset.data,tooltipData=formatter.format(chartData[tooltipItem.dataIndex]),tooltip=[];if(this._chart.getType()===Pie.prototype.TYPE){var chartLabels=this._cleanData(this._chart.getLabels());tooltip.push(chartLabels[tooltipItem.dataIndex]+" - "+this._cleanData(serieLabel)+": "+tooltipData)}else tooltip.push(this._cleanData(serieLabel)+": "+tooltipData);return tooltip},Output.prototype._isSmooth=function(series){var smooth=!1;return this._chart.getType()===Line.prototype.TYPE?null===(smooth=series.getSmooth())&&(smooth=this._chart.getSmooth()):series.getType()===Series.prototype.TYPE_LINE&&(smooth=series.getSmooth()),smooth},Output.prototype._isStacked=function(){var stacked=!1;return this._chart.getType()===Bar.prototype.TYPE&&(stacked=this._chart.getStacked()),stacked},Output.prototype.update=function(){$.extend(!0,this._config,this._makeConfig()),this._chartjs.update()},Output})); //# sourceMappingURL=chart_output_chartjs.min.js.map \ No newline at end of file diff --git a/lib/amd/build/chart_output_chartjs.min.js.map b/lib/amd/build/chart_output_chartjs.min.js.map index e4e7f6b5648..24cd3c09c9c 100644 --- a/lib/amd/build/chart_output_chartjs.min.js.map +++ b/lib/amd/build/chart_output_chartjs.min.js.map @@ -1 +1 @@ -{"version":3,"file":"chart_output_chartjs.min.js","sources":["../src/chart_output_chartjs.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Chart output for chart.js.\n *\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @module core/chart_output_chartjs\n */\ndefine([\n 'jquery',\n 'core/chartjs',\n 'core/chart_axis',\n 'core/chart_bar',\n 'core/chart_output_base',\n 'core/chart_line',\n 'core/chart_pie',\n 'core/chart_series'\n], function($, Chartjs, Axis, Bar, Base, Line, Pie, Series) {\n\n /**\n * Makes an axis ID.\n *\n * @param {String} xy Accepts 'x' and 'y'.\n * @param {Number} index The axis index.\n * @return {String}\n */\n var makeAxisId = function(xy, index) {\n return 'axis-' + xy + '-' + index;\n };\n\n /**\n * Chart output for Chart.js.\n *\n * @class\n * @extends {module:core/chart_output_base}\n */\n function Output() {\n Base.prototype.constructor.apply(this, arguments);\n\n // Make sure that we've got a canvas tag.\n this._canvas = this._node;\n if (this._canvas.prop('tagName') != 'CANVAS') {\n this._canvas = $('');\n this._node.append(this._canvas);\n }\n\n this._build();\n }\n Output.prototype = Object.create(Base.prototype);\n\n /**\n * Reference to the chart config object.\n *\n * @type {Object}\n * @protected\n */\n Output.prototype._config = null;\n\n /**\n * Reference to the instance of chart.js.\n *\n * @type {Object}\n * @protected\n */\n Output.prototype._chartjs = null;\n\n /**\n * Reference to the canvas node.\n *\n * @type {Jquery}\n * @protected\n */\n Output.prototype._canvas = null;\n\n /**\n * Builds the config and the chart.\n *\n * @protected\n */\n Output.prototype._build = function() {\n this._config = this._makeConfig();\n this._chartjs = new Chartjs(this._canvas[0], this._config);\n };\n\n /**\n * Clean data.\n *\n * @param {(String|String[])} data A single string or an array of strings.\n * @returns {(String|String[])}\n * @protected\n */\n Output.prototype._cleanData = function(data) {\n if (data instanceof Array) {\n return data.map(function(value) {\n return $('').html(value).text();\n });\n } else {\n return $('').html(data).text();\n }\n };\n\n /**\n * Get the chart type and handles the Chart.js specific chart types.\n *\n * By default returns the current chart TYPE value. Also does the handling of specific chart types, for example\n * check if the bar chart should be horizontal and the pie chart should be displayed as a doughnut.\n *\n * @method getChartType\n * @returns {String} the chart type.\n * @protected\n */\n Output.prototype._getChartType = function() {\n var type = this._chart.getType();\n\n // Bars can be displayed vertically and horizontally, defining horizontalBar type.\n if (this._chart.getType() === Bar.prototype.TYPE && this._chart.getHorizontal() === true) {\n type = 'horizontalBar';\n } else if (this._chart.getType() === Pie.prototype.TYPE && this._chart.getDoughnut() === true) {\n // Pie chart can be displayed as doughnut.\n type = 'doughnut';\n }\n\n return type;\n };\n\n /**\n * Make the axis config.\n *\n * @protected\n * @param {module:core/chart_axis} axis The axis.\n * @param {String} xy Accepts 'x' or 'y'.\n * @param {Number} index The axis index.\n * @return {Object} The axis config.\n */\n Output.prototype._makeAxisConfig = function(axis, xy, index) {\n var scaleData = {\n id: makeAxisId(xy, index)\n };\n\n if (axis.getPosition() !== Axis.prototype.POS_DEFAULT) {\n scaleData.position = axis.getPosition();\n }\n\n if (axis.getLabel() !== null) {\n scaleData.title = {\n display: true,\n text: this._cleanData(axis.getLabel())\n };\n }\n\n if (axis.getStepSize() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.stepSize = axis.getStepSize();\n }\n\n if (axis.getMax() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.max = axis.getMax();\n }\n\n if (axis.getMin() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.min = axis.getMin();\n }\n\n return scaleData;\n };\n\n /**\n * Make the config config.\n *\n * @protected\n * @return {Object} The axis config.\n */\n Output.prototype._makeConfig = function() {\n var charType = this._getChartType();\n var labels = this._cleanData(this._chart.getLabels());\n var config = {\n type: charType,\n data: {\n // If the label is longer than 25 characters, truncate it and add an\n // ellipsis to avoid breaking the chart.\n labels: labels.map((label) => {\n return label.length > 25 ? `${label.substring(0, 25)}...` : label;\n }),\n datasets: this._makeDatasetsConfig()\n },\n options: {\n locale: document.documentElement.getAttribute('lang'),\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n title: {\n display: this._chart.getTitle() !== null,\n text: this._cleanData(this._chart.getTitle())\n }\n }\n }\n };\n\n if (charType === 'horizontalBar') {\n config.type = 'bar';\n config.options.indexAxis = 'y';\n }\n\n var legendOptions = this._chart.getLegendOptions();\n if (legendOptions) {\n config.options.plugins.legend = legendOptions;\n }\n\n\n this._chart.getXAxes().forEach(function(axis, i) {\n var axisLabels = axis.getLabels();\n\n config.options.scales = config.options.scales || {};\n config.options.scales.x = this._makeAxisConfig(axis, 'x', i);\n\n if (axisLabels !== null) {\n config.options.scales.x.ticks.callback = function(value, index) {\n return axisLabels[index] || '';\n };\n }\n config.options.scales.x.stacked = this._isStacked();\n }.bind(this));\n\n this._chart.getYAxes().forEach(function(axis, i) {\n var axisLabels = axis.getLabels();\n\n config.options.scales = config.options.scales || {};\n config.options.scales.y = this._makeAxisConfig(axis, 'y', i);\n\n if (axisLabels !== null) {\n config.options.scales.y.ticks.callback = function(value) {\n return axisLabels[parseInt(value, 10)] || '';\n };\n }\n config.options.scales.y.stacked = this._isStacked();\n }.bind(this));\n\n config.options.plugins.tooltip = {\n callbacks: {\n title: (ctx) => {\n // Add line breaks to the tooltip title to prevent the tooltip from cutting\n // off if the title has a character count that overlaps the width of the chart.\n var label = labels[ctx[0].dataIndex];\n return label.match(/.{1,80}/g);\n },\n label: this._makeTooltip.bind(this)\n }\n };\n\n return config;\n };\n\n /**\n * Get the datasets configurations.\n *\n * @protected\n * @return {Object[]}\n */\n Output.prototype._makeDatasetsConfig = function() {\n var sets = this._chart.getSeries().map(function(series) {\n var colors = series.hasColoredValues() ? series.getColors() : series.getColor();\n var dataset = {\n label: this._cleanData(series.getLabel()),\n data: series.getValues(),\n type: series.getType(),\n fill: series.getFill(),\n backgroundColor: colors,\n // Pie charts look better without borders.\n borderColor: this._chart.getType() == Pie.prototype.TYPE ? '#fff' : colors,\n tension: this._isSmooth(series) ? 0.3 : 0\n };\n\n if (series.getXAxis() !== null) {\n dataset.xAxisID = makeAxisId('x', series.getXAxis());\n }\n if (series.getYAxis() !== null) {\n dataset.yAxisID = makeAxisId('y', series.getYAxis());\n }\n\n return dataset;\n }.bind(this));\n return sets;\n };\n\n /**\n * Get the chart data, add labels and rebuild the tooltip.\n *\n * @param {Object[]} tooltipItem The tooltip item object.\n * @returns {Array}\n * @protected\n */\n Output.prototype._makeTooltip = function(tooltipItem) {\n\n // Get series and chart data to rebuild the tooltip and add labels.\n const formatter = new Intl.NumberFormat(this._config.options.locale);\n var series = this._chart.getSeries()[tooltipItem.datasetIndex];\n var serieLabel = series.getLabel();\n var chartData = tooltipItem.dataset.data;\n var tooltipData = formatter.format(chartData[tooltipItem.dataIndex]);\n\n // Build default tooltip.\n var tooltip = [];\n\n // Pie and doughnut charts tooltip are different.\n if (this._chart.getType() === Pie.prototype.TYPE) {\n var chartLabels = this._cleanData(this._chart.getLabels());\n tooltip.push(chartLabels[tooltipItem.dataIndex] + ' - ' + this._cleanData(serieLabel) + ': ' + tooltipData);\n } else {\n tooltip.push(this._cleanData(serieLabel) + ': ' + tooltipData);\n }\n\n return tooltip;\n };\n\n /**\n * Verify if the chart line is smooth or not.\n *\n * @protected\n * @param {module:core/chart_series} series The series.\n * @returns {Bool}\n */\n Output.prototype._isSmooth = function(series) {\n var smooth = false;\n if (this._chart.getType() === Line.prototype.TYPE) {\n smooth = series.getSmooth();\n if (smooth === null) {\n smooth = this._chart.getSmooth();\n }\n } else if (series.getType() === Series.prototype.TYPE_LINE) {\n smooth = series.getSmooth();\n }\n\n return smooth;\n };\n\n /**\n * Verify if the bar chart is stacked or not.\n *\n * @protected\n * @returns {Bool}\n */\n Output.prototype._isStacked = function() {\n var stacked = false;\n\n // Stacking is (currently) only supported for bar charts.\n if (this._chart.getType() === Bar.prototype.TYPE) {\n stacked = this._chart.getStacked();\n }\n\n return stacked;\n };\n\n /** @override */\n Output.prototype.update = function() {\n $.extend(true, this._config, this._makeConfig());\n this._chartjs.update();\n };\n\n return Output;\n\n});\n"],"names":["define","$","Chartjs","Axis","Bar","Base","Line","Pie","Series","makeAxisId","xy","index","Output","prototype","constructor","apply","this","arguments","_canvas","_node","prop","append","_build","Object","create","_config","_chartjs","_makeConfig","_cleanData","data","Array","map","value","html","text","_getChartType","type","_chart","getType","TYPE","getHorizontal","getDoughnut","_makeAxisConfig","axis","scaleData","id","getPosition","POS_DEFAULT","position","getLabel","title","display","getStepSize","ticks","stepSize","getMax","max","getMin","min","charType","labels","getLabels","config","label","length","substring","datasets","_makeDatasetsConfig","options","locale","document","documentElement","getAttribute","responsive","maintainAspectRatio","plugins","getTitle","indexAxis","legendOptions","getLegendOptions","legend","getXAxes","forEach","i","axisLabels","scales","x","callback","stacked","_isStacked","bind","getYAxes","y","parseInt","tooltip","callbacks","ctx","dataIndex","match","_makeTooltip","getSeries","series","colors","hasColoredValues","getColors","getColor","dataset","getValues","fill","getFill","backgroundColor","borderColor","tension","_isSmooth","getXAxis","xAxisID","getYAxis","yAxisID","tooltipItem","formatter","Intl","NumberFormat","serieLabel","datasetIndex","chartData","tooltipData","format","chartLabels","push","smooth","getSmooth","TYPE_LINE","getStacked","update","extend"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"chart_output_chartjs.min.js","sources":["../src/chart_output_chartjs.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Chart output for chart.js.\n *\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @module core/chart_output_chartjs\n */\ndefine([\n 'jquery',\n 'core/chartjs',\n 'core/chart_axis',\n 'core/chart_bar',\n 'core/chart_output_base',\n 'core/chart_line',\n 'core/chart_pie',\n 'core/chart_series'\n], function($, Chartjs, Axis, Bar, Base, Line, Pie, Series) {\n\n /**\n * Makes an axis ID.\n *\n * @param {String} xy Accepts 'x' and 'y'.\n * @param {Number} index The axis index.\n * @return {String}\n */\n var makeAxisId = function(xy, index) {\n return 'axis-' + xy + '-' + index;\n };\n\n /**\n * Chart output for Chart.js.\n *\n * @class\n * @extends {module:core/chart_output_base}\n */\n function Output() {\n Base.prototype.constructor.apply(this, arguments);\n\n // Make sure that we've got a canvas tag.\n this._canvas = this._node;\n if (this._canvas.prop('tagName') != 'CANVAS') {\n this._canvas = $('');\n this._node.append(this._canvas);\n }\n\n this._build();\n }\n Output.prototype = Object.create(Base.prototype);\n\n /**\n * Reference to the chart config object.\n *\n * @type {Object}\n * @protected\n */\n Output.prototype._config = null;\n\n /**\n * Reference to the instance of chart.js.\n *\n * @type {Object}\n * @protected\n */\n Output.prototype._chartjs = null;\n\n /**\n * Reference to the canvas node.\n *\n * @type {Jquery}\n * @protected\n */\n Output.prototype._canvas = null;\n\n /**\n * Builds the config and the chart.\n *\n * @protected\n */\n Output.prototype._build = function() {\n this._config = this._makeConfig();\n this._chartjs = new Chartjs(this._canvas[0], this._config);\n };\n\n /**\n * Clean data.\n *\n * @param {(String|String[])} data A single string or an array of strings.\n * @returns {(String|String[])}\n * @protected\n */\n Output.prototype._cleanData = function(data) {\n if (data instanceof Array) {\n return data.map(function(value) {\n return $('').html(value).text();\n });\n } else {\n return $('').html(data).text();\n }\n };\n\n /**\n * Get the chart type and handles the Chart.js specific chart types.\n *\n * By default returns the current chart TYPE value. Also does the handling of specific chart types, for example\n * check if the bar chart should be horizontal and the pie chart should be displayed as a doughnut.\n *\n * @method getChartType\n * @returns {String} the chart type.\n * @protected\n */\n Output.prototype._getChartType = function() {\n var type = this._chart.getType();\n\n // Bars can be displayed vertically and horizontally, defining horizontalBar type.\n if (this._chart.getType() === Bar.prototype.TYPE && this._chart.getHorizontal() === true) {\n type = 'horizontalBar';\n } else if (this._chart.getType() === Pie.prototype.TYPE && this._chart.getDoughnut() === true) {\n // Pie chart can be displayed as doughnut.\n type = 'doughnut';\n }\n\n return type;\n };\n\n /**\n * Make the axis config.\n *\n * @protected\n * @param {module:core/chart_axis} axis The axis.\n * @param {String} xy Accepts 'x' or 'y'.\n * @param {Number} index The axis index.\n * @return {Object} The axis config.\n */\n Output.prototype._makeAxisConfig = function(axis, xy, index) {\n var scaleData = {\n id: makeAxisId(xy, index)\n };\n\n if (axis.getPosition() !== Axis.prototype.POS_DEFAULT) {\n scaleData.position = axis.getPosition();\n }\n\n if (axis.getLabel() !== null) {\n scaleData.title = {\n display: true,\n text: this._cleanData(axis.getLabel())\n };\n }\n\n if (axis.getStepSize() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.stepSize = axis.getStepSize();\n }\n\n if (axis.getMax() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.max = axis.getMax();\n }\n\n if (axis.getMin() !== null) {\n scaleData.ticks = scaleData.ticks || {};\n scaleData.ticks.min = axis.getMin();\n }\n\n return scaleData;\n };\n\n /**\n * Make the config config.\n *\n * @protected\n * @return {Object} The axis config.\n */\n Output.prototype._makeConfig = function() {\n var charType = this._getChartType();\n var labels = this._cleanData(this._chart.getLabels());\n var config = {\n type: charType,\n data: {\n // If the label is longer than 25 characters, truncate it and add an\n // ellipsis to avoid breaking the chart.\n labels: labels.map((label) => {\n return label.length > 25 ? `${label.substring(0, 25)}...` : label;\n }),\n datasets: this._makeDatasetsConfig()\n },\n options: {\n locale: document.documentElement.getAttribute('lang'),\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n title: {\n display: this._chart.getTitle() !== null,\n text: this._cleanData(this._chart.getTitle())\n }\n }\n }\n };\n\n if (charType === 'horizontalBar') {\n config.type = 'bar';\n config.options.indexAxis = 'y';\n }\n\n var legendOptions = this._chart.getLegendOptions();\n if (legendOptions) {\n config.options.plugins.legend = legendOptions;\n }\n\n var responsiveOptions = this._chart.getResponsiveOptions();\n if (responsiveOptions) {\n // Possible values to be changed in the properties related to the chart's responsiveness.\n const responsiveProperties = ['aspectRatio', 'maintainAspectRatio', 'responsive', 'resizeDelay'];\n for (const key of responsiveProperties) {\n const value = responsiveOptions[key];\n if (value !== null && value !== undefined) {\n config.options[key] = value;\n }\n }\n }\n\n this._chart.getXAxes().forEach(function(axis, i) {\n var axisLabels = axis.getLabels();\n\n config.options.scales = config.options.scales || {};\n config.options.scales.x = this._makeAxisConfig(axis, 'x', i);\n\n if (axisLabels !== null) {\n config.options.scales.x.ticks.callback = function(value, index) {\n return axisLabels[index] || '';\n };\n }\n config.options.scales.x.stacked = this._isStacked();\n }.bind(this));\n\n this._chart.getYAxes().forEach(function(axis, i) {\n var axisLabels = axis.getLabels();\n\n config.options.scales = config.options.scales || {};\n config.options.scales.y = this._makeAxisConfig(axis, 'y', i);\n\n if (axisLabels !== null) {\n config.options.scales.y.ticks.callback = function(value) {\n return axisLabels[parseInt(value, 10)] || '';\n };\n }\n config.options.scales.y.stacked = this._isStacked();\n }.bind(this));\n\n config.options.plugins.tooltip = {\n callbacks: {\n title: (ctx) => {\n // Add line breaks to the tooltip title to prevent the tooltip from cutting\n // off if the title has a character count that overlaps the width of the chart.\n var label = labels[ctx[0].dataIndex];\n return label.match(/.{1,80}/g);\n },\n label: this._makeTooltip.bind(this)\n }\n };\n\n return config;\n };\n\n /**\n * Get the datasets configurations.\n *\n * @protected\n * @return {Object[]}\n */\n Output.prototype._makeDatasetsConfig = function() {\n var sets = this._chart.getSeries().map(function(series) {\n var colors = series.hasColoredValues() ? series.getColors() : series.getColor();\n var dataset = {\n label: this._cleanData(series.getLabel()),\n data: series.getValues(),\n type: series.getType(),\n fill: series.getFill(),\n backgroundColor: colors,\n // Pie charts look better without borders.\n borderColor: this._chart.getType() == Pie.prototype.TYPE ? '#fff' : colors,\n tension: this._isSmooth(series) ? 0.3 : 0\n };\n\n if (series.getXAxis() !== null) {\n dataset.xAxisID = makeAxisId('x', series.getXAxis());\n }\n if (series.getYAxis() !== null) {\n dataset.yAxisID = makeAxisId('y', series.getYAxis());\n }\n\n return dataset;\n }.bind(this));\n return sets;\n };\n\n /**\n * Get the chart data, add labels and rebuild the tooltip.\n *\n * @param {Object[]} tooltipItem The tooltip item object.\n * @returns {Array}\n * @protected\n */\n Output.prototype._makeTooltip = function(tooltipItem) {\n\n // Get series and chart data to rebuild the tooltip and add labels.\n const formatter = new Intl.NumberFormat(this._config.options.locale);\n var series = this._chart.getSeries()[tooltipItem.datasetIndex];\n var serieLabel = series.getLabel();\n var chartData = tooltipItem.dataset.data;\n var tooltipData = formatter.format(chartData[tooltipItem.dataIndex]);\n\n // Build default tooltip.\n var tooltip = [];\n\n // Pie and doughnut charts tooltip are different.\n if (this._chart.getType() === Pie.prototype.TYPE) {\n var chartLabels = this._cleanData(this._chart.getLabels());\n tooltip.push(chartLabels[tooltipItem.dataIndex] + ' - ' + this._cleanData(serieLabel) + ': ' + tooltipData);\n } else {\n tooltip.push(this._cleanData(serieLabel) + ': ' + tooltipData);\n }\n\n return tooltip;\n };\n\n /**\n * Verify if the chart line is smooth or not.\n *\n * @protected\n * @param {module:core/chart_series} series The series.\n * @returns {Bool}\n */\n Output.prototype._isSmooth = function(series) {\n var smooth = false;\n if (this._chart.getType() === Line.prototype.TYPE) {\n smooth = series.getSmooth();\n if (smooth === null) {\n smooth = this._chart.getSmooth();\n }\n } else if (series.getType() === Series.prototype.TYPE_LINE) {\n smooth = series.getSmooth();\n }\n\n return smooth;\n };\n\n /**\n * Verify if the bar chart is stacked or not.\n *\n * @protected\n * @returns {Bool}\n */\n Output.prototype._isStacked = function() {\n var stacked = false;\n\n // Stacking is (currently) only supported for bar charts.\n if (this._chart.getType() === Bar.prototype.TYPE) {\n stacked = this._chart.getStacked();\n }\n\n return stacked;\n };\n\n /** @override */\n Output.prototype.update = function() {\n $.extend(true, this._config, this._makeConfig());\n this._chartjs.update();\n };\n\n return Output;\n\n});\n"],"names":["define","$","Chartjs","Axis","Bar","Base","Line","Pie","Series","makeAxisId","xy","index","Output","prototype","constructor","apply","this","arguments","_canvas","_node","prop","append","_build","Object","create","_config","_chartjs","_makeConfig","_cleanData","data","Array","map","value","html","text","_getChartType","type","_chart","getType","TYPE","getHorizontal","getDoughnut","_makeAxisConfig","axis","scaleData","id","getPosition","POS_DEFAULT","position","getLabel","title","display","getStepSize","ticks","stepSize","getMax","max","getMin","min","charType","labels","getLabels","config","label","length","substring","datasets","_makeDatasetsConfig","options","locale","document","documentElement","getAttribute","responsive","maintainAspectRatio","plugins","getTitle","indexAxis","legendOptions","getLegendOptions","legend","responsiveOptions","getResponsiveOptions","responsiveProperties","key","getXAxes","forEach","i","axisLabels","scales","x","callback","stacked","_isStacked","bind","getYAxes","y","parseInt","tooltip","callbacks","ctx","dataIndex","match","_makeTooltip","getSeries","series","colors","hasColoredValues","getColors","getColor","dataset","getValues","fill","getFill","backgroundColor","borderColor","tension","_isSmooth","getXAxis","xAxisID","getYAxis","yAxisID","tooltipItem","formatter","Intl","NumberFormat","serieLabel","datasetIndex","chartData","tooltipData","format","chartLabels","push","smooth","getSmooth","TYPE_LINE","getStacked","update","extend"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/src/chart_base.js b/lib/amd/src/chart_base.js index fa4000797b2..a0bea29b7b9 100644 --- a/lib/amd/src/chart_base.js +++ b/lib/amd/src/chart_base.js @@ -114,6 +114,14 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) { */ Base.prototype.TYPE = null; + /** + * Options for chart responsiveness. + * + * @protected + * @type {Object} + */ + Base.prototype._responsiveOptions = null; + /** * Add a series to the chart. * @@ -153,6 +161,9 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) { if (data.legend_options) { Chart.setLegendOptions(data.legend_options); } + if (data.responsive_options) { + Chart.setResponsiveOptions(data.responsive_options); + } data.series.forEach(function(seriesData) { Chart.addSeries(Series.prototype.create(seriesData)); }); @@ -292,6 +303,15 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) { return this.__getAxis('y', index, createIfNotExists); }; + /** + * Get responsive options for the chart. + * + * @return {Object} + */ + Base.prototype.getResponsiveOptions = function() { + return this._responsiveOptions; + }; + /** * Set colours defined by setting. * @@ -379,6 +399,18 @@ define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) { this._yaxes[index] = axis; }; + /** + * Set responsive options for chart. + * + * @param {Object} responsiveOptions + */ + Base.prototype.setResponsiveOptions = function(responsiveOptions) { + if (typeof responsiveOptions !== 'object') { + throw new Error('Setting responsive options with non-object value:' + responsiveOptions); + } + this._responsiveOptions = responsiveOptions; + }; + /** * Validate an axis. * diff --git a/lib/amd/src/chart_output_chartjs.js b/lib/amd/src/chart_output_chartjs.js index 22b8791bac7..2c5958a9d35 100644 --- a/lib/amd/src/chart_output_chartjs.js +++ b/lib/amd/src/chart_output_chartjs.js @@ -222,6 +222,17 @@ define([ config.options.plugins.legend = legendOptions; } + var responsiveOptions = this._chart.getResponsiveOptions(); + if (responsiveOptions) { + // Possible values to be changed in the properties related to the chart's responsiveness. + const responsiveProperties = ['aspectRatio', 'maintainAspectRatio', 'responsive', 'resizeDelay']; + for (const key of responsiveProperties) { + const value = responsiveOptions[key]; + if (value !== null && value !== undefined) { + config.options[key] = value; + } + } + } this._chart.getXAxes().forEach(function(axis, i) { var axisLabels = axis.getLabels(); diff --git a/lib/classes/chart_base.php b/lib/classes/chart_base.php index 99a820b65e4..49a8cea9115 100644 --- a/lib/classes/chart_base.php +++ b/lib/classes/chart_base.php @@ -50,6 +50,8 @@ class chart_base implements JsonSerializable, renderable { protected $yaxes = []; /** @var array Options for the chart legend. */ protected $legendoptions = []; + /** @var array Options for the chart responsiveness. */ + protected $responsiveoptions = []; /** * Constructor. @@ -89,6 +91,7 @@ class chart_base implements JsonSerializable, renderable { 'y' => $this->yaxes, ], 'legend_options' => !empty($this->legendoptions) ? $this->legendoptions : null, + 'responsive_options' => !empty($this->responsiveoptions) ? $this->responsiveoptions : null, 'config_colorset' => !empty($CFG->chart_colorset) ? $CFG->chart_colorset : null ]; } @@ -211,6 +214,15 @@ class chart_base implements JsonSerializable, renderable { return $this->get_axis('y', $index, $createifnotexists); } + /** + * Get an array of responsive options for the chart. + * + * @return array + */ + public function get_responsive_options() { + return $this->responsiveoptions; + } + /** * Set the defaults for this chart type. * @@ -282,6 +294,16 @@ class chart_base implements JsonSerializable, renderable { return $this->yaxes[$index] = $axis; } + /** + * Set responsive options for the chart. + * See https://www.chartjs.org/docs/latest/configuration/responsive.html for options. + * + * @param array $responsiveoptions Whether or not to display the chart extra options. + */ + public function set_responsive_options(array $responsiveoptions) { + $this->responsiveoptions = $responsiveoptions; + } + /** * Validate an axis. * diff --git a/lib/tests/other/chartjstestpage.php b/lib/tests/other/chartjstestpage.php index fc54f6e0472..6fd89899b74 100644 --- a/lib/tests/other/chartjstestpage.php +++ b/lib/tests/other/chartjstestpage.php @@ -54,6 +54,7 @@ $chart3->set_title('TENSIONED LINES CHART'); $chart3->add_series($sales); $chart3->add_series($expenses); $chart3->set_labels($labels); +$chart3->set_responsive_options(['aspectRatio' => 2, 'maintainAspectRatio' => true, 'resizeDelay' => 1]); $chart4 = new \core\chart_line(); $chart4->set_smooth(true);