diff --git a/docs/configuration/legend.md b/docs/configuration/legend.md index f8ce7eb5a17..5caef61759d 100644 --- a/docs/configuration/legend.md +++ b/docs/configuration/legend.md @@ -82,7 +82,10 @@ Items passed to the legend `onClick` function are the ones returned from `labels strokeStyle: Color, // Point style of the legend box (only used if usePointStyle is true) - pointStyle: string + pointStyle: string | Image, + + // Rotation of the point in degrees (only used if usePointStyle is true) + rotation: number } ``` diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 7130a9d0905..0736cf6e1e0 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -5,8 +5,6 @@ var defaults = require('../core/core.defaults'); var elements = require('../elements/index'); var helpers = require('../helpers/index'); -var resolve = helpers.options.resolve; - defaults._set('bar', { hover: { mode: 'label' @@ -120,6 +118,16 @@ module.exports = DatasetController.extend({ dataElementType: elements.Rectangle, + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderSkipped', + 'borderWidth' + ], + initialize: function() { var me = this; var meta; @@ -147,7 +155,7 @@ module.exports = DatasetController.extend({ var me = this; var meta = me.getMeta(); var dataset = me.getDataset(); - var options = me._resolveElementOptions(rectangle, index); + var options = me._resolveDataElementOptions(rectangle, index); rectangle._xScale = me.getScaleForId(meta.xAxisID); rectangle._yScale = me.getScaleForId(meta.yAxisID); @@ -372,46 +380,5 @@ module.exports = DatasetController.extend({ } helpers.canvas.unclipArea(chart.ctx); - }, - - /** - * @private - */ - _resolveElementOptions: function(rectangle, index) { - var me = this; - var chart = me.chart; - var datasets = chart.data.datasets; - var dataset = datasets[me.index]; - var datasetOpts = me._config; - var custom = rectangle.custom || {}; - var options = chart.options.elements.rectangle; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderSkipped', - 'borderWidth' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[key], - options[key] - ], context, index); - } - - return values; } }); diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index d724432f446..8882969d48a 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -47,6 +47,22 @@ module.exports = DatasetController.extend({ */ dataElementType: elements.Point, + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + 'hoverRadius', + 'hitRadius', + 'pointStyle', + 'rotation' + ], + /** * @protected */ @@ -70,7 +86,7 @@ module.exports = DatasetController.extend({ var custom = point.custom || {}; var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); - var options = me._resolveElementOptions(point, index); + var options = me._resolveDataElementOptions(point, index); var data = me.getDataset().data[index]; var dsIndex = me.index; @@ -122,17 +138,13 @@ module.exports = DatasetController.extend({ /** * @private */ - _resolveElementOptions: function(point, index) { + _resolveDataElementOptions: function(point, index) { var me = this; var chart = me.chart; - var datasets = chart.data.datasets; - var dataset = datasets[me.index]; - var datasetOpts = me._config; + var dataset = me.getDataset(); var custom = point.custom || {}; - var options = chart.options.elements.point; - var data = dataset.data[index]; - var values = {}; - var i, ilen, key; + var data = dataset.data[index] || {}; + var values = DatasetController.prototype._resolveDataElementOptions.apply(me, arguments); // Scriptable options var context = { @@ -142,34 +154,12 @@ module.exports = DatasetController.extend({ datasetIndex: me.index }; - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - 'hoverRadius', - 'hitRadius', - 'pointStyle', - 'rotation' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[key], - options[key] - ], context, index); - } - // Custom radius resolution values.radius = resolve([ custom.radius, - data ? data.r : undefined, - dataset.radius, - options.radius + data.r, + me._config.radius, + chart.options.elements.point.radius ], context, index); return values; diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index daa1ed1bdfd..de418e7edbc 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -5,7 +5,6 @@ var defaults = require('../core/core.defaults'); var elements = require('../elements/index'); var helpers = require('../helpers/index'); -var resolve = helpers.options.resolve; var valueOrDefault = helpers.valueOrDefault; var PI = Math.PI; @@ -50,20 +49,14 @@ defaults._set('doughnut', { if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); - var ds = data.datasets[0]; - var arc = meta.data[i]; - var custom = arc && arc.custom || {}; - var arcOpts = chart.options.elements.arc; - var fill = resolve([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i); - var stroke = resolve([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i); - var bw = resolve([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i); + var style = meta.controller.getStyle(i); return { text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i @@ -131,6 +124,19 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'borderAlign', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + ], + // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly getRingIndex: function(datasetIndex) { var ringIndex = 0; @@ -184,7 +190,7 @@ module.exports = DatasetController.extend({ } for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arcs[i]._options = me._resolveElementOptions(arcs[i], i); + arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); } chart.borderWidth = me.getMaxBorderWidth(); @@ -317,7 +323,7 @@ module.exports = DatasetController.extend({ arc = arcs[i]; if (controller) { controller._configure(); - options = controller._resolveElementOptions(arc, i); + options = controller._resolveDataElementOptions(arc, i); } else { options = arc._options; } @@ -351,49 +357,6 @@ module.exports = DatasetController.extend({ model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth); }, - /** - * @private - */ - _resolveElementOptions: function(arc, index) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var datasetOpts = me._config; - var custom = arc.custom || {}; - var options = chart.options.elements.arc; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'borderAlign', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[key], - options[key] - ], context, index); - } - - return values; - }, - /** * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly * @private diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 759db4c7e72..b7f06816d2b 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -35,6 +35,38 @@ module.exports = DatasetController.extend({ dataElementType: elements.Point, + /** + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderCapStyle', + 'borderColor', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'borderWidth', + 'cubicInterpolationMode', + 'fill' + ], + + /** + * @private + */ + _dataElementOptions: { + backgroundColor: 'pointBackgroundColor', + borderColor: 'pointBorderColor', + borderWidth: 'pointBorderWidth', + hitRadius: 'pointHitRadius', + hoverBackgroundColor: 'pointHoverBackgroundColor', + hoverBorderColor: 'pointHoverBorderColor', + hoverBorderWidth: 'pointHoverBorderWidth', + hoverRadius: 'pointHoverRadius', + pointStyle: 'pointStyle', + radius: 'pointRadius', + rotation: 'pointRotation' + }, + update: function(reset) { var me = this; var meta = me.getMeta(); @@ -61,7 +93,7 @@ module.exports = DatasetController.extend({ // Data line._children = points; // Model - line._model = me._resolveLineOptions(line); + line._model = me._resolveDatasetElementOptions(line); line.pivot(); } @@ -93,7 +125,7 @@ module.exports = DatasetController.extend({ var lineModel = meta.dataset._model; var x, y; - var options = me._resolvePointOptions(point, index); + var options = me._resolveDataElementOptions(point, index); x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); @@ -127,101 +159,20 @@ module.exports = DatasetController.extend({ /** * @private */ - _resolvePointOptions: function(element, index) { - var me = this; - var chart = me.chart; - var dataset = chart.data.datasets[me.index]; - var datasetOpts = me._config; - var custom = element.custom || {}; - var options = chart.options.elements.point; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var ELEMENT_OPTIONS = { - backgroundColor: 'pointBackgroundColor', - borderColor: 'pointBorderColor', - borderWidth: 'pointBorderWidth', - hitRadius: 'pointHitRadius', - hoverBackgroundColor: 'pointHoverBackgroundColor', - hoverBorderColor: 'pointHoverBorderColor', - hoverBorderWidth: 'pointHoverBorderWidth', - hoverRadius: 'pointHoverRadius', - pointStyle: 'pointStyle', - radius: 'pointRadius', - rotation: 'pointRotation' - }; - var keys = Object.keys(ELEMENT_OPTIONS); - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[ELEMENT_OPTIONS[key]], - datasetOpts[key], - options[key] - ], context, index); - } - - return values; - }, - - /** - * @private - */ - _resolveLineOptions: function(element) { + _resolveDatasetElementOptions: function(element) { var me = this; - var chart = me.chart; - var datasetIndex = me.index; - var dataset = chart.data.datasets[datasetIndex]; var datasetOpts = me._config; var custom = element.custom || {}; - var options = chart.options; - var elementOptions = options.elements.line; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataset: dataset, - datasetIndex: datasetIndex - }; - - var keys = [ - 'backgroundColor', - 'borderCapStyle', - 'borderColor', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'borderWidth', - 'cubicInterpolationMode', - 'fill' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[key], - elementOptions[key] - ], context); - } + var options = me.chart.options; + var lineOptions = options.elements.line; + var values = DatasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); // The default behavior of lines is to break at null values, according // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 // This option gives lines the ability to span gaps values.spanGaps = valueOrDefault(datasetOpts.spanGaps, options.spanGaps); - values.tension = valueOrDefault(datasetOpts.lineTension, elementOptions.tension); - values.steppedLine = resolve([custom.steppedLine, datasetOpts.steppedLine, elementOptions.stepped]); + values.tension = valueOrDefault(datasetOpts.lineTension, lineOptions.tension); + values.steppedLine = resolve([custom.steppedLine, datasetOpts.steppedLine, lineOptions.stepped]); return values; }, diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 582a0a78bf7..1e0e30ba2dd 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -59,20 +59,14 @@ defaults._set('polarArea', { if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); - var ds = data.datasets[0]; - var arc = meta.data[i]; - var custom = arc.custom || {}; - var arcOpts = chart.options.elements.arc; - var fill = resolve([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i); - var stroke = resolve([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i); - var bw = resolve([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i); + var style = meta.controller.getStyle(i); return { text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i @@ -116,6 +110,19 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'borderAlign', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + ], + update: function(reset) { var me = this; var dataset = me.getDataset(); @@ -138,7 +145,7 @@ module.exports = DatasetController.extend({ } for (i = 0, ilen = arcs.length; i < ilen; ++i) { - arcs[i]._options = me._resolveElementOptions(arcs[i], i); + arcs[i]._options = me._resolveDataElementOptions(arcs[i], i); me.updateElement(arcs[i], i, reset); } }, @@ -241,49 +248,6 @@ module.exports = DatasetController.extend({ model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth); }, - /** - * @private - */ - _resolveElementOptions: function(arc, index) { - var me = this; - var chart = me.chart; - var dataset = me.getDataset(); - var datasetOpts = me._config; - var custom = arc.custom || {}; - var options = chart.options.elements.arc; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'borderAlign', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[key], - options[key] - ], context, index); - } - - return values; - }, - /** * @private */ diff --git a/src/controllers/controller.radar.js b/src/controllers/controller.radar.js index 1e32dd0a424..d83e472a739 100644 --- a/src/controllers/controller.radar.js +++ b/src/controllers/controller.radar.js @@ -6,7 +6,6 @@ var elements = require('../elements/index'); var helpers = require('../helpers/index'); var valueOrDefault = helpers.valueOrDefault; -var resolve = helpers.options.resolve; defaults._set('radar', { scale: { @@ -33,6 +32,37 @@ module.exports = DatasetController.extend({ linkScales: helpers.noop, + /** + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderWidth', + 'borderColor', + 'borderCapStyle', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'fill' + ], + + /** + * @private + */ + _dataElementOptions: { + backgroundColor: 'pointBackgroundColor', + borderColor: 'pointBorderColor', + borderWidth: 'pointBorderWidth', + hitRadius: 'pointHitRadius', + hoverBackgroundColor: 'pointHoverBackgroundColor', + hoverBorderColor: 'pointHoverBorderColor', + hoverBorderWidth: 'pointHoverBorderWidth', + hoverRadius: 'pointHoverRadius', + pointStyle: 'pointStyle', + radius: 'pointRadius', + rotation: 'pointRotation' + }, + update: function(reset) { var me = this; var meta = me.getMeta(); @@ -54,7 +84,7 @@ module.exports = DatasetController.extend({ line._children = points; line._loop = true; // Model - line._model = me._resolveLineOptions(line); + line._model = me._resolveDatasetElementOptions(line); line.pivot(); @@ -78,7 +108,7 @@ module.exports = DatasetController.extend({ var dataset = me.getDataset(); var scale = me.chart.scale; var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); - var options = me._resolvePointOptions(point, index); + var options = me._resolveDataElementOptions(point, index); var lineModel = me.getMeta().dataset._model; var x = reset ? scale.xCenter : pointPosition.x; var y = reset ? scale.yCenter : pointPosition.y; @@ -111,86 +141,11 @@ module.exports = DatasetController.extend({ /** * @private */ - _resolvePointOptions: function(element, index) { - var me = this; - var chart = me.chart; - var dataset = chart.data.datasets[me.index]; - var datasetOpts = me._config; - var custom = element.custom || {}; - var options = chart.options.elements.point; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; - - var ELEMENT_OPTIONS = { - backgroundColor: 'pointBackgroundColor', - borderColor: 'pointBorderColor', - borderWidth: 'pointBorderWidth', - hitRadius: 'pointHitRadius', - hoverBackgroundColor: 'pointHoverBackgroundColor', - hoverBorderColor: 'pointHoverBorderColor', - hoverBorderWidth: 'pointHoverBorderWidth', - hoverRadius: 'pointHoverRadius', - pointStyle: 'pointStyle', - radius: 'pointRadius', - rotation: 'pointRotation' - }; - var keys = Object.keys(ELEMENT_OPTIONS); - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[ELEMENT_OPTIONS[key]], - datasetOpts[key], - options[key] - ], context, index); - } - - return values; - }, - - /** - * @private - */ - _resolveLineOptions: function(element) { + _resolveDatasetElementOptions: function() { var me = this; - var chart = me.chart; - var dataset = chart.data.datasets[me.index]; - var datasetOpts = me._config; - var custom = element.custom || {}; - var options = chart.options.elements.line; - var values = {}; - var i, ilen, key; - - var keys = [ - 'backgroundColor', - 'borderWidth', - 'borderColor', - 'borderCapStyle', - 'borderDash', - 'borderDashOffset', - 'borderJoinStyle', - 'fill' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve([ - custom[key], - datasetOpts[key], - options[key] - ]); - } + var values = DatasetController.prototype._resolveDatasetElementOptions.apply(me, arguments); - values.tension = valueOrDefault(dataset.lineTension, options.tension); + values.tension = valueOrDefault(me._config.lineTension, me.chart.options.elements.line.tension); return values; }, diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 45a2e894236..2f7826a7674 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -94,6 +94,35 @@ helpers.extend(DatasetController.prototype, { */ dataElementType: null, + /** + * Dataset element option keys to be resolved in _resolveDatasetElementOptions. + * A derived controller may override this to resolve controller-specific options. + * The keys defined here are for backward compatibility for legend styles. + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderCapStyle', + 'borderColor', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'borderWidth' + ], + + /** + * Data element option keys to be resolved in _resolveDataElementOptions. + * A derived controller may override this to resolve controller-specific options. + * The keys defined here are for backward compatibility for legend styles. + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'pointStyle' + ], + initialize: function(chart, datasetIndex) { var me = this; me.chart = chart; @@ -293,6 +322,111 @@ helpers.extend(DatasetController.prototype, { } }, + /** + * Returns a set of predefined style properties that should be used to represent the dataset + * or the data if the index is specified + * @param {number} index - data index + * @return {IStyleInterface} style object + */ + getStyle: function(index) { + var me = this; + var meta = me.getMeta(); + var dataset = meta.dataset; + var style; + + me._configure(); + if (dataset && index === undefined) { + style = me._resolveDatasetElementOptions(dataset || {}); + } else { + index = index || 0; + style = me._resolveDataElementOptions(meta.data[index] || {}, index); + } + + if (style.fill === false || style.fill === null) { + style.backgroundColor = 'rgba(0,0,0,0)'; + } + + return style; + }, + + /** + * @private + */ + _resolveDatasetElementOptions: function(element) { + var me = this; + var chart = me.chart; + var datasetOpts = me._config; + var custom = element.custom || {}; + var options = chart.options.elements[me.datasetElementType.prototype._type] || {}; + var elementOptions = me._datasetElementOptions; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataset: me.getDataset(), + datasetIndex: me.index + }; + + for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { + key = elementOptions[i]; + values[key] = resolve([ + custom[key], + datasetOpts[key], + options[key] + ], context); + } + + return values; + }, + + /** + * @private + */ + _resolveDataElementOptions: function(element, index) { + var me = this; + var chart = me.chart; + var datasetOpts = me._config; + var custom = element.custom || {}; + var options = chart.options.elements[me.dataElementType.prototype._type] || {}; + var elementOptions = me._dataElementOptions; + var values = {}; + var keys, i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: me.getDataset(), + datasetIndex: me.index + }; + + if (helpers.isArray(elementOptions)) { + for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { + key = elementOptions[i]; + values[key] = resolve([ + custom[key], + datasetOpts[key], + options[key] + ], context, index); + } + } else { + keys = Object.keys(elementOptions); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve([ + custom[key], + datasetOpts[elementOptions[key]], + datasetOpts[key], + options[key] + ], context, index); + } + } + + return values; + }, + removeHoverStyle: function(element) { helpers.merge(element._model, element.$previousStyle || {}); delete element.$previousStyle; diff --git a/src/core/core.element.js b/src/core/core.element.js index ecfec2c994d..e4dc4feee31 100644 --- a/src/core/core.element.js +++ b/src/core/core.element.js @@ -58,6 +58,7 @@ var Element = function(configuration) { }; helpers.extend(Element.prototype, { + _type: undefined, initialize: function() { this.hidden = false; diff --git a/src/elements/element.arc.js b/src/elements/element.arc.js index e3b8875b4dd..3dab9ad6277 100644 --- a/src/elements/element.arc.js +++ b/src/elements/element.arc.js @@ -92,6 +92,8 @@ function drawBorder(ctx, vm, arc) { } module.exports = Element.extend({ + _type: 'arc', + inLabelRange: function(mouseX) { var vm = this._view; diff --git a/src/elements/element.line.js b/src/elements/element.line.js index 12c0f5a3664..8a801f6516f 100644 --- a/src/elements/element.line.js +++ b/src/elements/element.line.js @@ -26,6 +26,8 @@ defaults._set('global', { }); module.exports = Element.extend({ + _type: 'line', + draw: function() { var me = this; var vm = me._view; diff --git a/src/elements/element.point.js b/src/elements/element.point.js index 1e6b22357ec..9e1456a2d64 100644 --- a/src/elements/element.point.js +++ b/src/elements/element.point.js @@ -35,6 +35,8 @@ function yRange(mouseY) { } module.exports = Element.extend({ + _type: 'point', + inRange: function(mouseX, mouseY) { var vm = this._view; return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index 5e5a2eac459..d1ef58c13c3 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -131,6 +131,8 @@ function inRange(vm, x, y) { } module.exports = Element.extend({ + _type: 'rectangle', + draw: function() { var ctx = this._chart.ctx; var vm = this._view; diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index d3fd5e35f04..b6101561dc8 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -49,18 +49,25 @@ defaults._set('global', { // lineWidth : generateLabels: function(chart) { var data = chart.data; + var options = chart.options.legend || {}; + var usePointStyle = options.labels && options.labels.usePointStyle; + return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { + var meta = chart.getDatasetMeta(i); + var style = meta.controller.getStyle(usePointStyle ? 0 : undefined); + return { text: dataset.label, - fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), + fillStyle: style.backgroundColor, hidden: !chart.isDatasetVisible(i), - lineCap: dataset.borderCapStyle, - lineDash: dataset.borderDash, - lineDashOffset: dataset.borderDashOffset, - lineJoin: dataset.borderJoinStyle, - lineWidth: dataset.borderWidth, - strokeStyle: dataset.borderColor, - pointStyle: dataset.pointStyle, + lineCap: style.borderCapStyle, + lineDash: style.borderDash, + lineDashOffset: style.borderDashOffset, + lineJoin: style.borderJoinStyle, + lineWidth: style.borderWidth, + strokeStyle: style.borderColor, + pointStyle: style.pointStyle, + rotation: style.rotation, // Below is extra data used for toggling the datasets datasetIndex: i @@ -377,7 +384,7 @@ var Legend = Element.extend({ ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); } - if (opts.labels && opts.labels.usePointStyle) { + if (labelOpts && labelOpts.usePointStyle) { // Recalculate x and y for drawPoint() because its expecting // x and y to be center of figure (instead of top left) var radius = boxWidth * Math.SQRT2 / 2; @@ -385,13 +392,13 @@ var Legend = Element.extend({ var centerY = y + fontSize / 2; // Draw pointStyle as legend symbol - helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); + helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY, legendItem.rotation); } else { // Draw box as legend symbol + ctx.fillRect(x, y, boxWidth, fontSize); if (lineWidth !== 0) { ctx.strokeRect(x, y, boxWidth, fontSize); } - ctx.fillRect(x, y, boxWidth, fontSize); } ctx.restore(); diff --git a/test/specs/plugin.legend.tests.js b/test/specs/plugin.legend.tests.js index 18829cac04a..06c8b2cf4c4 100644 --- a/test/specs/plugin.legend.tests.js +++ b/test/specs/plugin.legend.tests.js @@ -24,7 +24,7 @@ describe('Legend block tests', function() { }); }); - it('should update correctly', function() { + it('should update bar chart correctly', function() { var chart = window.acquireChart({ type: 'bar', data: { @@ -55,29 +55,31 @@ describe('Legend block tests', function() { text: 'dataset1', fillStyle: '#f31', hidden: false, - lineCap: 'butt', - lineDash: [2, 2], - lineDashOffset: 5.5, + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, lineJoin: undefined, - lineWidth: undefined, - strokeStyle: undefined, + lineWidth: 0, + strokeStyle: 'rgba(0,0,0,0.1)', pointStyle: undefined, + rotation: undefined, datasetIndex: 0 }, { text: 'dataset2', - fillStyle: undefined, + fillStyle: 'rgba(0,0,0,0.1)', hidden: true, lineCap: undefined, lineDash: undefined, lineDashOffset: undefined, - lineJoin: 'miter', - lineWidth: undefined, - strokeStyle: undefined, + lineJoin: undefined, + lineWidth: 0, + strokeStyle: 'rgba(0,0,0,0.1)', pointStyle: undefined, + rotation: undefined, datasetIndex: 1 }, { text: 'dataset3', - fillStyle: undefined, + fillStyle: 'rgba(0,0,0,0.1)', hidden: false, lineCap: undefined, lineDash: undefined, @@ -85,7 +87,78 @@ describe('Legend block tests', function() { lineJoin: undefined, lineWidth: 10, strokeStyle: 'green', - pointStyle: 'crossRot', + pointStyle: undefined, + rotation: undefined, + datasetIndex: 2 + }]); + }); + + it('should update line chart correctly', function() { + var chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + label: 'dataset1', + backgroundColor: '#f31', + borderCapStyle: 'round', + borderDash: [2, 2], + borderDashOffset: 5.5, + data: [] + }, { + label: 'dataset2', + hidden: true, + borderJoinStyle: 'round', + data: [] + }, { + label: 'dataset3', + borderWidth: 10, + borderColor: 'green', + pointStyle: 'crossRot', + fill: false, + data: [] + }], + labels: [] + } + }); + + expect(chart.legend.legendItems).toEqual([{ + text: 'dataset1', + fillStyle: '#f31', + hidden: false, + lineCap: 'round', + lineDash: [2, 2], + lineDashOffset: 5.5, + lineJoin: 'miter', + lineWidth: 3, + strokeStyle: 'rgba(0,0,0,0.1)', + pointStyle: undefined, + rotation: undefined, + datasetIndex: 0 + }, { + text: 'dataset2', + fillStyle: 'rgba(0,0,0,0.1)', + hidden: true, + lineCap: 'butt', + lineDash: [], + lineDashOffset: 0, + lineJoin: 'round', + lineWidth: 3, + strokeStyle: 'rgba(0,0,0,0.1)', + pointStyle: undefined, + rotation: undefined, + datasetIndex: 1 + }, { + text: 'dataset3', + fillStyle: 'rgba(0,0,0,0)', + hidden: false, + lineCap: 'butt', + lineDash: [], + lineDashOffset: 0, + lineJoin: 'miter', + lineWidth: 10, + strokeStyle: 'green', + pointStyle: undefined, + rotation: undefined, datasetIndex: 2 }]); }); @@ -132,17 +205,18 @@ describe('Legend block tests', function() { text: 'dataset1', fillStyle: '#f31', hidden: false, - lineCap: 'butt', - lineDash: [2, 2], - lineDashOffset: 5.5, + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, lineJoin: undefined, - lineWidth: undefined, - strokeStyle: undefined, + lineWidth: 0, + strokeStyle: 'rgba(0,0,0,0.1)', pointStyle: undefined, + rotation: undefined, datasetIndex: 0 }, { text: 'dataset3', - fillStyle: undefined, + fillStyle: 'rgba(0,0,0,0.1)', hidden: false, lineCap: undefined, lineDash: undefined, @@ -150,7 +224,8 @@ describe('Legend block tests', function() { lineJoin: undefined, lineWidth: 10, strokeStyle: 'green', - pointStyle: 'crossRot', + pointStyle: undefined, + rotation: undefined, datasetIndex: 2 }]); }); @@ -217,6 +292,145 @@ describe('Legend block tests', function() { }); }); + it('should pick up the first item when the property is an array', function() { + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [{ + label: 'dataset1', + backgroundColor: ['#f31', '#666', '#14e'], + borderWidth: [5, 10, 15], + borderColor: ['red', 'green', 'blue'], + data: [] + }], + labels: [] + } + }); + + expect(chart.legend.legendItems).toEqual([{ + text: 'dataset1', + fillStyle: '#f31', + hidden: false, + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: 5, + strokeStyle: 'red', + pointStyle: undefined, + rotation: undefined, + datasetIndex: 0 + }]); + }); + + it('should use the value for the first item when the property is a function', function() { + var helpers = window.Chart.helpers; + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [{ + label: 'dataset1', + backgroundColor: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return helpers.color({r: value * 10, g: 0, b: 0}).rgbString(); + }, + borderWidth: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return value; + }, + borderColor: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return helpers.color({r: 255 - value * 10, g: 0, b: 0}).rgbString(); + }, + data: [5, 10, 15, 20] + }], + labels: ['A', 'B', 'C', 'D'] + } + }); + + expect(chart.legend.legendItems).toEqual([{ + text: 'dataset1', + fillStyle: 'rgb(50, 0, 0)', + hidden: false, + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: 5, + strokeStyle: 'rgb(205, 0, 0)', + pointStyle: undefined, + rotation: undefined, + datasetIndex: 0 + }]); + }); + + it('should draw correctly when usePointStyle is true', function() { + var chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + label: 'dataset1', + backgroundColor: '#f31', + borderCapStyle: 'butt', + borderDash: [2, 2], + borderDashOffset: 5.5, + borderWidth: 0, + borderColor: '#f31', + pointStyle: 'crossRot', + pointBackgroundColor: 'rgba(0,0,0,0.1)', + pointBorderWidth: 5, + pointBorderColor: 'green', + data: [] + }, { + label: 'dataset2', + backgroundColor: '#f31', + borderJoinStyle: 'miter', + borderWidth: 2, + borderColor: '#f31', + pointStyle: 'crossRot', + pointRotation: 15, + data: [] + }], + labels: [] + }, + options: { + legend: { + labels: { + usePointStyle: true + } + } + } + }); + + expect(chart.legend.legendItems).toEqual([{ + text: 'dataset1', + fillStyle: 'rgba(0,0,0,0.1)', + hidden: false, + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: 5, + strokeStyle: 'green', + pointStyle: 'crossRot', + rotation: undefined, + datasetIndex: 0 + }, { + text: 'dataset2', + fillStyle: '#f31', + hidden: false, + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: 2, + strokeStyle: '#f31', + pointStyle: 'crossRot', + rotation: 15, + datasetIndex: 1 + }]); + }); + describe('config update', function() { it ('should update the options', function() { var chart = acquireChart({