Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Chart base.
18
 *
19
 * @copyright  2016 Frédéric Massart - FMCorz.net
20
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
21
 * @module     core/chart_base
22
 */
23
define(['core/chart_series', 'core/chart_axis'], function(Series, Axis) {
24
 
25
    /**
26
     * Chart base.
27
     *
28
     * The constructor of a chart must never take any argument.
29
     *
30
     * {@link module:core/chart_base#_setDefault} to set the defaults on instantiation.
31
     *
32
     * @class
33
     */
34
    function Base() {
35
        this._series = [];
36
        this._labels = [];
37
        this._xaxes = [];
38
        this._yaxes = [];
39
 
40
        this._setDefaults();
41
    }
42
 
43
    /**
44
     * The series constituting this chart.
45
     *
46
     * @protected
47
     * @type {module:core/chart_series[]}
48
     */
49
    Base.prototype._series = null;
50
 
51
    /**
52
     * The labels of the X axis when categorised.
53
     *
54
     * @protected
55
     * @type {String[]}
56
     */
57
    Base.prototype._labels = null;
58
 
59
    /**
60
     * Options for chart legend display.
61
     *
62
     * @protected
63
     * @type {Object}
64
     */
65
    Base.prototype._legendOptions = null;
66
 
67
    /**
68
     * The title of the chart.
69
     *
70
     * @protected
71
     * @type {String}
72
     */
73
    Base.prototype._title = null;
74
 
75
    /**
76
     * The X axes.
77
     *
78
     * @protected
79
     * @type {module:core/chart_axis[]}
80
     */
81
    Base.prototype._xaxes = null;
82
 
83
    /**
84
     * The Y axes.
85
     *
86
     * @protected
87
     * @type {module:core/chart_axis[]}
88
     */
89
    Base.prototype._yaxes = null;
90
 
91
    /**
92
     * Colours to pick from when automatically assigning them.
93
     *
94
     * @const
95
     * @type {String[]}
96
     */
97
    Base.prototype.COLORSET = ['#f3c300', '#875692', '#f38400', '#a1caf1', '#be0032', '#c2b280', '#7f180d', '#008856',
98
            '#e68fac', '#0067a5'];
99
 
100
    /**
101
     * Set of colours defined by setting $CFG->chart_colorset to be picked when automatically assigning them.
102
     *
103
     * @type {String[]}
104
     * @protected
105
     */
106
    Base.prototype._configColorSet = null;
107
 
108
    /**
109
     * The type of chart.
110
     *
111
     * @abstract
112
     * @type {String}
113
     * @const
114
     */
115
    Base.prototype.TYPE = null;
116
 
117
    /**
118
     * Add a series to the chart.
119
     *
120
     * This will automatically assign a color to the series if it does not have one.
121
     *
122
     * @param {module:core/chart_series} series The series to add.
123
     */
124
    Base.prototype.addSeries = function(series) {
125
        this._validateSeries(series);
126
        this._series.push(series);
127
 
128
        // Give a default color from the set.
129
        if (series.getColor() === null) {
130
            var configColorSet = this.getConfigColorSet() || Base.prototype.COLORSET;
131
            series.setColor(configColorSet[this._series.length % configColorSet.length]);
132
        }
133
    };
134
 
135
    /**
136
     * Create a new instance of a chart from serialised data.
137
     *
138
     * the serialised attributes they offer and support.
139
     *
140
     * @static
141
     * @method create
142
     * @param {module:core/chart_base} Klass The class oject representing the type of chart to instantiate.
143
     * @param {Object} data The data of the chart.
144
     * @return {module:core/chart_base}
145
     */
146
    Base.prototype.create = function(Klass, data) {
147
        // TODO Not convinced about the usage of Klass here but I can't figure out a way
148
        // to have a reference to the class in the sub classes, in PHP I'd do new self().
149
        var Chart = new Klass();
150
        Chart.setConfigColorSet(data.config_colorset);
151
        Chart.setLabels(data.labels);
152
        Chart.setTitle(data.title);
153
        if (data.legend_options) {
154
            Chart.setLegendOptions(data.legend_options);
155
        }
156
        data.series.forEach(function(seriesData) {
157
            Chart.addSeries(Series.prototype.create(seriesData));
158
        });
159
        data.axes.x.forEach(function(axisData, i) {
160
            Chart.setXAxis(Axis.prototype.create(axisData), i);
161
        });
162
        data.axes.y.forEach(function(axisData, i) {
163
            Chart.setYAxis(Axis.prototype.create(axisData), i);
164
        });
165
        return Chart;
166
    };
167
 
168
    /**
169
     * Get an axis.
170
     *
171
     * @private
172
     * @param {String} xy Accepts the values 'x' or 'y'.
173
     * @param {Number} [index=0] The index of the axis of its type.
174
     * @param {Bool} [createIfNotExists=false] When true, create an instance if it does not exist.
175
     * @return {module:core/chart_axis}
176
     */
177
    Base.prototype.__getAxis = function(xy, index, createIfNotExists) {
178
        var axes = xy === 'x' ? this._xaxes : this._yaxes,
179
            setAxis = (xy === 'x' ? this.setXAxis : this.setYAxis).bind(this),
180
            axis;
181
 
182
        index = typeof index === 'undefined' ? 0 : index;
183
        createIfNotExists = typeof createIfNotExists === 'undefined' ? false : createIfNotExists;
184
        axis = axes[index];
185
 
186
        if (typeof axis === 'undefined') {
187
            if (!createIfNotExists) {
188
                throw new Error('Unknown axis.');
189
            }
190
            axis = new Axis();
191
            setAxis(axis, index);
192
        }
193
 
194
        return axis;
195
    };
196
 
197
    /**
198
     * Get colours defined by setting.
199
     *
200
     * @return {String[]}
201
     */
202
    Base.prototype.getConfigColorSet = function() {
203
        return this._configColorSet;
204
    };
205
 
206
    /**
207
     * Get the labels of the X axis.
208
     *
209
     * @return {String[]}
210
     */
211
    Base.prototype.getLabels = function() {
212
        return this._labels;
213
    };
214
 
215
    /**
216
     * Get whether to display the chart legend.
217
     *
218
     * @return {Bool}
219
     */
220
    Base.prototype.getLegendOptions = function() {
221
        return this._legendOptions;
222
    };
223
 
224
    /**
225
     * Get the series.
226
     *
227
     * @return {module:core/chart_series[]}
228
     */
229
    Base.prototype.getSeries = function() {
230
        return this._series;
231
    };
232
 
233
    /**
234
     * Get the title of the chart.
235
     *
236
     * @return {String}
237
     */
238
    Base.prototype.getTitle = function() {
239
        return this._title;
240
    };
241
 
242
    /**
243
     * Get the type of chart.
244
     *
245
     * @see module:core/chart_base#TYPE
246
     * @return {String}
247
     */
248
    Base.prototype.getType = function() {
249
        if (!this.TYPE) {
250
            throw new Error('The TYPE property has not been set.');
251
        }
252
        return this.TYPE;
253
    };
254
 
255
    /**
256
     * Get the X axes.
257
     *
258
     * @return {module:core/chart_axis[]}
259
     */
260
    Base.prototype.getXAxes = function() {
261
        return this._xaxes;
262
    };
263
 
264
    /**
265
     * Get an X axis.
266
     *
267
     * @param {Number} [index=0] The index of the axis.
268
     * @param {Bool} [createIfNotExists=false] Create the instance of it does not exist at index.
269
     * @return {module:core/chart_axis}
270
     */
271
    Base.prototype.getXAxis = function(index, createIfNotExists) {
272
        return this.__getAxis('x', index, createIfNotExists);
273
    };
274
 
275
    /**
276
     * Get the Y axes.
277
     *
278
     * @return {module:core/chart_axis[]}
279
     */
280
    Base.prototype.getYAxes = function() {
281
        return this._yaxes;
282
    };
283
 
284
    /**
285
     * Get an Y axis.
286
     *
287
     * @param {Number} [index=0] The index of the axis.
288
     * @param {Bool} [createIfNotExists=false] Create the instance of it does not exist at index.
289
     * @return {module:core/chart_axis}
290
     */
291
    Base.prototype.getYAxis = function(index, createIfNotExists) {
292
        return this.__getAxis('y', index, createIfNotExists);
293
    };
294
 
295
    /**
296
     * Set colours defined by setting.
297
     *
298
     * @param {String[]} colorset An array of css colours.
299
     * @protected
300
     */
301
    Base.prototype.setConfigColorSet = function(colorset) {
302
        this._configColorSet = colorset;
303
    };
304
 
305
    /**
306
     * Set the defaults for this chart type.
307
     *
308
     * Child classes can extend this to set defaults values on instantiation.
309
     *
310
     * emphasize and self-document the defaults values set by the chart type.
311
     *
312
     * @protected
313
     */
314
    Base.prototype._setDefaults = function() {
315
        // For the children to extend.
316
    };
317
 
318
    /**
319
     * Set the labels of the X axis.
320
     *
321
     * This requires for each series to contain strictly as many values as there
322
     * are labels.
323
     *
324
     * @param {String[]} labels The labels.
325
     */
326
    Base.prototype.setLabels = function(labels) {
327
        if (labels.length && this._series.length && this._series[0].length != labels.length) {
328
            throw new Error('Series must match label values.');
329
        }
330
        this._labels = labels;
331
    };
332
 
333
    /**
334
     * Set options for chart legend display.
335
     *
336
     * @param {Object} legendOptions
337
     */
338
    Base.prototype.setLegendOptions = function(legendOptions) {
339
        if (typeof legendOptions !== 'object') {
340
            throw new Error('Setting legend with non-object value:' + legendOptions);
341
        }
342
        this._legendOptions = legendOptions;
343
    };
344
 
345
    /**
346
     * Set the title of the chart.
347
     *
348
     * @param {String} title The title.
349
     */
350
    Base.prototype.setTitle = function(title) {
351
        this._title = title;
352
    };
353
 
354
    /**
355
     * Set an X axis.
356
     *
357
     * Note that this will override any predefined axis without warning.
358
     *
359
     * @param {module:core/chart_axis} axis The axis.
360
     * @param {Number} [index=0] The index of the axis.
361
     */
362
    Base.prototype.setXAxis = function(axis, index) {
363
        index = typeof index === 'undefined' ? 0 : index;
364
        this._validateAxis('x', axis, index);
365
        this._xaxes[index] = axis;
366
    };
367
 
368
    /**
369
     * Set a Y axis.
370
     *
371
     * Note that this will override any predefined axis without warning.
372
     *
373
     * @param {module:core/chart_axis} axis The axis.
374
     * @param {Number} [index=0] The index of the axis.
375
     */
376
    Base.prototype.setYAxis = function(axis, index) {
377
        index = typeof index === 'undefined' ? 0 : index;
378
        this._validateAxis('y', axis, index);
379
        this._yaxes[index] = axis;
380
    };
381
 
382
    /**
383
     * Validate an axis.
384
     *
385
     * @protected
386
     * @param {String} xy X or Y axis.
387
     * @param {module:core/chart_axis} axis The axis to validate.
388
     * @param {Number} [index=0] The index of the axis.
389
     */
390
    Base.prototype._validateAxis = function(xy, axis, index) {
391
        index = typeof index === 'undefined' ? 0 : index;
392
        if (index > 0) {
393
            var axes = xy == 'x' ? this._xaxes : this._yaxes;
394
            if (typeof axes[index - 1] === 'undefined') {
395
                throw new Error('Missing ' + xy + ' axis at index lower than ' + index);
396
            }
397
        }
398
    };
399
 
400
    /**
401
     * Validate a series.
402
     *
403
     * @protected
404
     * @param {module:core/chart_series} series The series to validate.
405
     */
406
    Base.prototype._validateSeries = function(series) {
407
        if (this._series.length && this._series[0].getCount() != series.getCount()) {
408
            throw new Error('Series do not have an equal number of values.');
409
 
410
        } else if (this._labels.length && this._labels.length != series.getCount()) {
411
            throw new Error('Series must match label values.');
412
        }
413
    };
414
 
415
    return Base;
416
 
417
});