From: etimberg Date: Mon, 18 May 2015 18:19:34 +0000 (-0400) Subject: Initial config merge. Handles special cases for objects and arrays and merges smarter... X-Git-Tag: v2.0-alpha~8^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=676765e8085c297021b241e026e9846b41e3af7c;p=thirdparty%2FChart.js.git Initial config merge. Handles special cases for objects and arrays and merges smarter than simply replacing the default object. Tested by overriding the vertical axis 0 point colour. Also implemented dataset axis binding. If nothing is specified, the dataset is bound to the first x and y axis. --- diff --git a/samples/scatter.html b/samples/scatter.html index 56c23fa72..ecc259f2f 100644 --- a/samples/scatter.html +++ b/samples/scatter.html @@ -88,7 +88,14 @@ var ctx = document.getElementById("canvas").getContext("2d"); window.myScatter = new Chart(ctx).Scatter(scatterChartData, { responsive: true, - hoverMode: 'single' + hoverMode: 'single', + scales: { + xAxes: [{ + gridLines: { + zeroLineColor: "rgba(0,0,0,1)" + } + }] + } }); }; diff --git a/src/Chart.Core.js b/src/Chart.Core.js index e5bfd2af3..f86f0ac0d 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -239,6 +239,37 @@ args.unshift({}); return extend.apply(null, args); }, + // Need a special merge function to chart configs since they are now grouped + configMerge = helpers.configMerge = function(base) { + helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) { + helpers.each(extension, function(value, key) { + if (extension.hasOwnProperty(key)) { + if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) { + // In this case we have an array of objects replacing another array. Rather than doing a strict replace, + // merge. This allows easy scale option merging + var baseArray = base[key]; + + helpers.each(value, function(valueObj, index) { + if (index < baseArray.length) { + baseArray[index] = helpers.configMerge(baseArray[index], valueObj); + } else { + baseArray.push(valueObj); // nothing to merge + } + }); + } + else if (base.hasOwnProperty(key) && typeof base[key] == "object" && typeof value == "object") { + // If we are overwriting an object with an object, do a merge of the properties. + base[key] = helpers.configMerge(base[key], value); + } else { + // can just overwrite the value in this case + base[key] = value; + } + } + }); + }); + + return base; + }, indexOf = helpers.indexOf = function(arrayToSearch, item) { if (Array.prototype.indexOf) { return arrayToSearch.indexOf(item); @@ -1126,13 +1157,13 @@ var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {}; - Chart.defaults[chartName] = extend(baseDefaults, extensions.defaults); + Chart.defaults[chartName] = helpers.configMerge(baseDefaults, extensions.defaults); Chart.types[chartName] = ChartType; //Register this new chart type in the Chart prototype Chart.prototype[chartName] = function(data, options) { - var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {}); + var config = helpers.configMerge(Chart.defaults.global, Chart.defaults[chartName], options || {}); return new ChartType(data, config, this); }; } else { diff --git a/src/Chart.Scatter.js b/src/Chart.Scatter.js index 3a364e30a..10aa6757c 100644 --- a/src/Chart.Scatter.js +++ b/src/Chart.Scatter.js @@ -7,20 +7,72 @@ var defaultConfig = { - ///Boolean - Whether grid lines are shown across the chart - scaleShowGridLines: true, - - //String - Colour of the grid lines - scaleGridLineColor: "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth: 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, + scales: { + xAxes: [{ + scaleType: "linear", // scatter should not use a dataset axis + show: true, + position: "bottom", + horizontal: true, + id: "x-axis-1", // need an ID so datasets can reference the scale + + // grid line settings + gridLines: { + show: true, + color: "rgba(0, 0, 0, 0.05)", + lineWidth: 1, + drawOnChartArea: true, + zeroLineWidth: 1, + zeroLineColor: "rgba(0,0,0,0.25)", + }, + + // scale numbers + beginAtZero: false, + integersOnly: false, + override: null, + + // label settings + labels: { + show: true, + template: "<%=value%>", + fontSize: 12, + fontStyle: "normal", + fontColor: "#666", + fontFamily: "Helvetica Neue", + }, + }], + yAxes: [{ + scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance + show: true, + position: "left", + horizontal: false, + id: "y-axis-1", + + // grid line settings + gridLines: { + show: true, + color: "rgba(0, 0, 0, 0.05)", + lineWidth: 1, + drawOnChartArea: true, + zeroLineWidth: 1, + zeroLineColor: "rgba(0,0,0,0.25)", + }, + + // scale numbers + beginAtZero: false, + integersOnly: false, + override: null, + + // label settings + labels: { + show: true, + template: "<%=value%>", + fontSize: 12, + fontStyle: "normal", + fontColor: "#666", + fontFamily: "Helvetica Neue", + } + }], + }, //Number - Tension of the bezier curve between points tension: 0.4, @@ -40,9 +92,6 @@ //String - A legend template legendTemplate: "", - //Boolean - Whether to horizontally center the label and point dot inside the grid - offsetGridLines: false - }; @@ -66,7 +115,7 @@ helpers.bindEvents(this, this.options.tooltipEvents, this.onHover); // Build Scale - this.buildScale(this.data.labels); + this.buildScale(); Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height); //Create a new line and its points for each dataset and piece of data @@ -76,6 +125,15 @@ helpers.each(dataset.data, function(dataPoint, index) { dataset.metaData.push(new this.PointClass()); }, this); + + // Make sure each dataset is bound to an x and a y axis + if (!dataset.xAxisID) { + dataset.xAxisID = this.options.scales.xAxes[0].id; + } + + if (!dataset.yAxisID) { + dataset.yAxisID = this.options.scales.yAxes[0].id; + } }, this); // Set defaults for lines @@ -92,8 +150,10 @@ // Set defaults for points this.eachElement(function(point, index, dataset, datasetIndex) { + var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID]; + helpers.extend(point, { - x: this.xScale.getPixelForValue(index), + x: xScale.getPixelForValue(index), y: this.chartArea.bottom, _datasetIndex: datasetIndex, _index: index, @@ -266,9 +326,12 @@ // Update the points this.eachElement(function(point, index, dataset, datasetIndex) { + var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID]; + var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID]; + helpers.extend(point, { - x: this.xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x), - y: this.yScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].y), + x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x), + y: yScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].y), value: this.data.datasets[datasetIndex].data[index].y, label: this.data.datasets[datasetIndex].data[index].x, datasetLabel: this.data.datasets[datasetIndex].label, @@ -323,137 +386,84 @@ this.render(); }, - buildScale: function(labels) { + buildScale: function() { var self = this; - var dataTotal = function() { - var values = []; - self.eachValue(function(value) { - values.push(value); - }); + var calculateXRange = function() { + this.min = null; + this.max = null; + + helpers.each(self.data.datasets, function(dataset) { + // Only set the scale range for datasets that actually use this axis + if (dataset.xAxisID === this.id) { + helpers.each(dataset.data, function(value) { + if (this.min === null) { + this.min = value.x; + } else if (value.x < this.min) { + this.min = value.x; + } + + if (this.max === null) { + this.max = value.x; + } else if (value.x > this.max) { + this.max = value.x; + } + }, this); + } + }, this); + }; - return values; + var calculateYRange = function() { + this.min = null; + this.max = null; + + helpers.each(self.data.datasets, function(dataset) { + if (dataset.yAxisID === this.id) { + helpers.each(dataset.data, function(value) { + if (this.min === null) { + this.min = value.y; + } else if (value.y < this.min) { + this.min = value.y; + } + + if (this.max === null) { + this.max = value.y; + } else if (value.y > this.max) { + this.max = value.y; + } + }, this); + } + }, this); }; - var XScaleClass = Chart.scales.getScaleConstructor("linear"); - var YScaleClass = Chart.scales.getScaleConstructor("linear"); - - this.xScale = new XScaleClass({ - ctx: this.chart.ctx, - }); - - // Eventually this will be referenced from the user supplied config options. - this.xScale.options = { - scaleType: "dataset", // default options are 'dataset', 'linear'. - show: true, - position: "bottom", - horizontal: true, - - // grid line settings - gridLines: { - show: true, - color: "rgba(0, 0, 0, 0.05)", - lineWidth: 1, - drawOnChartArea: true, - zeroLineWidth: 1, - zeroLineColor: "rgba(0,0,0,0.25)", - }, - - // scale numbers - beginAtZero: false, - integersOnly: false, - override: null, - - // label settings - labels: { - show: true, - template: "<%=value%>", - fontSize: 12, - fontStyle: "normal", - fontColor: "#666", - fontFamily: "Helvetica Neue", - }, - }; - this.yScale = new YScaleClass({ - ctx: this.chart.ctx, - }); - this.yScale.options = { - scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance - show: true, - position: "left", - horizontal: false, - - // grid line settings - gridLines: { - show: true, - color: "rgba(0, 0, 0, 0.05)", - lineWidth: 1, - drawOnChartArea: true, - zeroLineWidth: 1, - zeroLineColor: "rgba(0,0,0,0.25)", - }, - - // scale numbers - beginAtZero: false, - integersOnly: false, - override: null, - - // label settings - labels: { - show: true, - template: "<%=value%>", - fontSize: 12, - fontStyle: "normal", - fontColor: "#666", - fontFamily: "Helvetica Neue", - }, - }; - - this.xScale.calculateRange = function() { - this.min = null; - this.max = null; - - helpers.each(self.data.datasets, function(dataset) { - helpers.each(dataset.data, function(value) { - if (this.min === null) { - this.min = value.x; - } else if (value.x < this.min) { - this.min = value.x; - } - - if (this.max === null) { - this.max = value.x; - } else if (value.x > this.max) { - this.max = value.x; - } - }, this); - }, this); - }; - - this.yScale.calculateRange = function() { - this.min = null; - this.max = null; - - helpers.each(self.data.datasets, function(dataset) { - helpers.each(dataset.data, function(value) { - if (this.min === null) { - this.min = value.y; - } else if (value.y < this.min) { - this.min = value.y; - } - - if (this.max === null) { - this.max = value.y; - } else if (value.y > this.max) { - this.max = value.y; - } - }, this); - }, this); - }; - - // Register the axes with the scale service - Chart.scaleService.registerChartScale(this, this.xScale); - Chart.scaleService.registerChartScale(this, this.yScale); + // Map of scale ID to scale object so we can lookup later + this.scales = {}; + + helpers.each(this.options.scales.xAxes, function(xAxisOptions) { + var ScaleClass = Chart.scales.getScaleConstructor(xAxisOptions.scaleType); + var scale = new ScaleClass({ + ctx: this.chart.ctx, + options: xAxisOptions, + calculateRange: calculateXRange, + id: xAxisOptions.id, + }); + + this.scales[scale.id] = scale; + Chart.scaleService.registerChartScale(this, scale); + }, this); + + helpers.each(this.options.scales.yAxes, function(yAxisOptions) { + var ScaleClass = Chart.scales.getScaleConstructor(yAxisOptions.scaleType); + var scale = new ScaleClass({ + ctx: this.chart.ctx, + options: yAxisOptions, + calculateRange: calculateYRange, + id: yAxisOptions.id, + }); + + this.scales[scale.id] = scale; + Chart.scaleService.registerChartScale(this, scale); + }, this); }, redraw: function() {