]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Bar chart now uses the rectangular canvas & element controllers.
authorEvert Timberg <evert.timberg@gmail.com>
Sun, 14 Jun 2015 19:26:34 +0000 (15:26 -0400)
committerEvert Timberg <evert.timberg@gmail.com>
Sun, 14 Jun 2015 19:26:34 +0000 (15:26 -0400)
src/charts/chart.bar.js
src/controllers/controller.canvas.rectangular.js
src/controllers/controller.elements.rectangular.js

index 1cba8b9b56ef1f7b893932f8206010d2ae91cf8f..4d9990b113f90e82cea9ca41b635cc91d29bf303 100644 (file)
                name: "Bar",
                defaults: defaultConfig,
                initialize: function() {
-
-                       var _this = this;
-
-                       // Events
-                       helpers.bindEvents(this, this.options.events, this.events);
+                       this.elementController = new Chart.RectangularElementController(this);
+                       this.canvasController = new Chart.RectangularCanvasController(this, this.elementController);
 
                        //Create a new bar for each piece of data
                        helpers.each(this.data.datasets, function(dataset, datasetIndex) {
-                               dataset.metaData = [];
                                helpers.each(dataset.data, function(dataPoint, index) {
-                                       dataset.metaData.push(new Chart.Rectangle({
-                                               _chart: this.chart,
-                                               _datasetIndex: datasetIndex,
-                                               _index: index,
-                                       }));
+                                       this.elementController.addRectangle(dataset, datasetIndex, index);
                                }, this);
 
-                               // The bar chart only supports a single x axis because the x axis is always a dataset axis
+                               // The bar chart only supports a single x axis because the x axis is always a category axis
                                dataset.xAxisID = this.options.scales.xAxes[0].id;
 
                                if (!dataset.yAxisID) {
                                }
                        }, this);
 
-                       // Build and fit the scale. Needs to happen after the axis IDs have been set
-                       this.buildScale();
-
-                       // Create tooltip instance exclusively for this chart with some defaults.
-                       this.tooltip = new Chart.Tooltip({
-                               _chart: this.chart,
-                               _data: this.data,
-                               _options: this.options,
-                       }, this);
-
-                       // Need to fit scales before we reset elements. 
-                       Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
-
-                       // So that we animate from the baseline
-                       this.resetElements();
-
-                       // Update the chart with the latest data.
-                       this.update();
-               },
-               resetElements: function() {
-                       // Update the points
-                       this.eachElement(function(bar, index, dataset, datasetIndex) {
-                               var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
-                               var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
-
-                               var yScalePoint;
-
-                               if (yScale.min < 0 && yScale.max < 0) {
-                                       // all less than 0. use the top
-                                       yScalePoint = yScale.getPixelForValue(yScale.max);
-                               } else if (yScale.min > 0 && yScale.max > 0) {
-                                       yScalePoint = yScale.getPixelForValue(yScale.min);
-                               } else {
-                                       yScalePoint = yScale.getPixelForValue(0);
-                               }
-
-                               helpers.extend(bar, {
-                                       // Utility
-                                       _chart: this.chart,
-                                       _xScale: xScale,
-                                       _yScale: yScale,
-                                       _datasetIndex: datasetIndex,
-                                       _index: index,
-
-                                       // Desired view properties
-                                       _model: {
-                                               x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
-                                               y: yScalePoint,
-
-                                               // Appearance
-                                               base: yScale.calculateBarBase(datasetIndex, index),
-                                               width: xScale.calculateBarWidth(this.data.datasets.length),
-                                               backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.rectangle.backgroundColor),
-                                               borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.rectangle.borderColor),
-                                               borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.rectangle.borderWidth),
-
-                                               // Tooltip
-                                               label: this.data.labels[index],
-                                               datasetLabel: this.data.datasets[datasetIndex].label,
-                                       },
-                               });
-                               bar.pivot();
-                       }, this);
-               },
-               update: function(animationDuration) {
-                       // Update the scale sizes
-                       Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
-
-                       // Update the points
-                       this.eachElement(function(bar, index, dataset, datasetIndex) {
-                               var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
-                               var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
-
-                               helpers.extend(bar, {
-                                       // Utility
-                                       _chart: this.chart,
-                                       _xScale: xScale,
-                                       _yScale: yScale,
-                                       _datasetIndex: datasetIndex,
-                                       _index: index,
-
-                                       // Desired view properties
-                                       _model: {
-                                               x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
-                                               y: yScale.calculateBarY(datasetIndex, index),
-
-                                               // Appearance
-                                               base: yScale.calculateBarBase(datasetIndex, index),
-                                               width: xScale.calculateBarWidth(this.data.datasets.length),
-                                               backgroundColor: bar.custom && bar.custom.backgroundColor ? bar.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].backgroundColor, index, this.options.elements.rectangle.backgroundColor),
-                                               borderColor: bar.custom && bar.custom.borderColor ? bar.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderColor, index, this.options.elements.rectangle.borderColor),
-                                               borderWidth: bar.custom && bar.custom.borderWidth ? bar.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].borderWidth, index, this.options.elements.rectangle.borderWidth),
-
-                                               // Tooltip
-                                               label: this.data.labels[index],
-                                               datasetLabel: this.data.datasets[datasetIndex].label,
-                                       },
-                               });
-                               bar.pivot();
-                       }, this);
-
-
-                       this.render(animationDuration);
-               },
-               buildScale: function(labels) {
-                       var self = this;
-
-                       // Map of scale ID to scale object so we can lookup later 
-                       this.scales = {};
-
-                       // Build the x axis. The line chart only supports a single x axis
-                       var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scales.xAxes[0].type);
-                       var xScale = new ScaleClass({
-                               ctx: this.chart.ctx,
-                               options: this.options.scales.xAxes[0],
-                               id: this.options.scales.xAxes[0].id,
-                               data: this.data,
-                       });
-                       this.scales[xScale.id] = xScale;
-
-                       // Build up all the y scales
-                       helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
-                               var ScaleClass = Chart.scaleService.getScaleConstructor(yAxisOptions.type);
-                               var scale = new ScaleClass({
-                                       ctx: this.chart.ctx,
-                                       options: yAxisOptions,
-                                       data: this.data,
-                                       id: yAxisOptions.id,
-                               });
-
-                               this.scales[scale.id] = scale;
-                       }, this);
+                       this.canvasController.initialize();
                },
                draw: function(ease) {
 
                        // Finally draw the tooltip
                        this.tooltip.transition(easingDecimal).draw();
                },
-               events: function(e) {
-
-
-
-                       this.lastActive = this.lastActive || [];
-
-                       // Find Active Elements
-                       if (e.type == 'mouseout') {
-                               this.active = [];
-                       } else {
-                               this.active = function() {
-                                       switch (this.options.hover.mode) {
-                                               case 'single':
-                                                       return this.getElementAtEvent(e);
-                                               case 'label':
-                                                       return this.getElementsAtEvent(e);
-                                               case 'dataset':
-                                                       return this.getDatasetAtEvent(e);
-                                               default:
-                                                       return e;
-                                       }
-                               }.call(this);
-                       }
-
-                       // On Hover hook
-                       if (this.options.hover.onHover) {
-                               this.options.hover.onHover.call(this, this.active);
-                       }
-
-                       if (e.type == 'mouseup' || e.type == 'click') {
-                               if (this.options.onClick) {
-                                       this.options.onClick.call(this, e, this.active);
-                               }
-                       }
-
-                       var dataset;
-                       var index;
-                       // Remove styling for last active (even if it may still be active)
-                       if (this.lastActive.length) {
-                               switch (this.options.hover.mode) {
-                                       case 'single':
-                                               dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
-                                               index = this.lastActive[0]._index;
-
-                                               this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.rectangle.backgroundColor);
-                                               this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.rectangle.borderColor);
-                                               this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.rectangle.borderWidth);
-                                               break;
-                                       case 'label':
-                                               for (var i = 0; i < this.lastActive.length; i++) {
-                                                       dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
-                                                       index = this.lastActive[i]._index;
-
-                                                       this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.rectangle.backgroundColor);
-                                                       this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.rectangle.borderColor);
-                                                       this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.rectangle.borderWidth);
-                                               }
-                                               break;
-                                       case 'dataset':
-                                               break;
-                                       default:
-                                               // Don't change anything
-                               }
-                       }
-
-                       // Built in hover styling
-                       if (this.active.length && this.options.hover.mode) {
-                               switch (this.options.hover.mode) {
-                                       case 'single':
-                                               dataset = this.data.datasets[this.active[0]._datasetIndex];
-                                               index = this.active[0]._index;
-
-                                               this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
-                                               this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
-                                               this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[0]._model.borderWidth);
-                                               break;
-                                       case 'label':
-                                               for (var i = 0; i < this.active.length; i++) {
-                                                       dataset = this.data.datasets[this.active[i]._datasetIndex];
-                                                       index = this.active[i]._index;
-
-                                                       this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
-                                                       this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
-                                                       this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.active[i]._model.borderWidth);
-                                               }
-                                               break;
-                                       case 'dataset':
-                                               break;
-                                       default:
-                                               // Don't change anything
-                               }
-                       }
-
-
-                       // Built in Tooltips
-                       if (this.options.tooltips.enabled) {
-
-                               // The usual updates
-                               this.tooltip.initialize();
-
-                               // Active
-                               if (this.active.length) {
-                                       this.tooltip._model.opacity = 1;
-
-                                       helpers.extend(this.tooltip, {
-                                               _active: this.active,
-                                       });
-
-                                       this.tooltip.update();
-                               } else {
-                                       // Inactive
-                                       this.tooltip._model.opacity = 0;
-                               }
-                       }
-
-
-                       this.tooltip.pivot();
-
-                       // Hover animations
-                       if (!this.animating) {
-                               var changed;
-
-                               helpers.each(this.active, function(element, index) {
-                                       if (element !== this.lastActive[index]) {
-                                               changed = true;
-                                       }
-                               }, this);
-
-                               // If entering, leaving, or changing elements, animate the change via pivot
-                               if ((!this.lastActive.length && this.active.length) ||
-                                       (this.lastActive.length && !this.active.length) ||
-                                       (this.lastActive.length && this.active.length && changed)) {
-
-                                       this.stop();
-                                       this.render(this.options.hoverAnimationDuration);
-                               }
-                       }
-
-                       // Remember Last Active
-                       this.lastActive = this.active;
-                       return this;
-               },
        });
 
 
index 22caa778ccf72766b625ce885f028fba5f077804..a4a75cef07b9d0ba740a8b6ab665dd6df3ddc33d 100644 (file)
                if (this.lastActive.length) {
                        switch (this.chartInstance.options.hover.mode) {
                                case 'single':
-                                       this.elementController.updatePointElementAppearance(this.lastActive[0], this.lastActive[0]._datasetIndex, this.lastActive[0]._index);
+                                       this.elementController.resetElementAppearance(this.lastActive[0], this.lastActive[0]._datasetIndex, this.lastActive[0]._index);
                                        break;
                                case 'label':
                                        for (var i = 0; i < this.lastActive.length; i++) {
-                                               this.elementController.updatePointElementAppearance(this.lastActive[i], this.lastActive[i]._datasetIndex, this.lastActive[i]._index);
+                                               this.elementController.resetElementAppearance(this.lastActive[i], this.lastActive[i]._datasetIndex, this.lastActive[i]._index);
                                        }
                                        break;
                                case 'dataset':
                if (this.active.length && this.chartInstance.options.hover.mode) {
                        switch (this.chartInstance.options.hover.mode) {
                                case 'single':
-                                       this.elementController.setPointHoverStyle(this.active[0]);
+                                       this.elementController.setElementHoverStyle(this.active[0]);
                                        break;
                                case 'label':
                                        for (var i = 0; i < this.active.length; i++) {
-                                               this.elementController.setPointHoverStyle(this.active[i]);
+                                               this.elementController.setElementHoverStyle(this.active[i]);
                                        }
                                        break;
                                case 'dataset':
index a23949b9d9fa4d6900659f447cfb00a8aa8c0bec..469aa0188744cc88b3a124946534d99265bb3244 100644 (file)
 
        Chart.RectangularElementController.prototype.eachLine = function eachLine(callback) {
                helpers.each(this.chartInstance.data.datasets, function(dataset, datasetIndex) {
-                       callback.call(this, dataset, datasetIndex)
+                       if (dataset.metaDataset && dataset.metaDataset instanceof Chart.Line) {
+                               callback.call(this, dataset, datasetIndex)
+                       }
                }, this);
        };
 
-       Chart.RectangularElementController.prototype.eachPoint = function eachPoint(callback) {
+       Chart.RectangularElementController.prototype.eachRectangle = function(callback) {
                helpers.each(this.chartInstance.data.datasets, function(dataset, datasetIndex) {
-                       helpers.each(dataset.metaData, callback, this, dataset.metaData, datasetIndex);
+                       helpers.each(dataset.metaData, function(element, index) {
+                               if (element instanceof Chart.Rectangle) {
+                                       callback(element, index, dataset, datasetIndex);
+                               }
+                       }, this);
                }, this);
        };
 
                }
        };
 
+       Chart.RectangularElementController.prototype.addRectangle = function(dataset, datasetIndex, index) {
+               if (dataset) {
+                       dataset.metaData = dataset.metaData || new Array(this.chartInstance.data.datasets[datasetIndex].data.length);
+
+                       if (index < dataset.metaData.length) {
+                               dataset.metaData[index] = new Chart.Rectangle({
+                                       _chart: this.chartInstance.chart,
+                                       _datasetIndex: datasetIndex,
+                                       _index: index,
+                               });
+                       }
+               }
+       };
+
        Chart.RectangularElementController.prototype.resetElements = function resetElements() {
-               // Update the points
-               this.resetPoints();
+               helpers.each(this.chartInstance.data.datasets, function(dataset, datasetIndex) {
+                       // All elements must be the same type for the given dataset so we are fine to check just the first one
+                       if (dataset.metaData[0] instanceof Chart.Point) {
+                               // Have points. Update all of them
+                               this.resetDatasetPoints(dataset, datasetIndex);
+                       } else if (dataset.metaData[0] instanceof Chart.Rectangle) {
+                               // Have rectangles (bars)
+                               this.resetDatasetRectangles(dataset, datasetIndex);
+                       }
+               }, this);
        };
        
-       Chart.RectangularElementController.prototype.resetPoints = function() {
-               this.eachPoint(function(point, index, dataset, datasetIndex) {
+       Chart.RectangularElementController.prototype.resetDatasetPoints = function resetDatasetPoints(dataset, datasetIndex) {
+               helpers.each(dataset.metaData, function(point, index){
                        var xScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].xAxisID);
                        var yScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].yAxisID);
 
                        this.updatePointElementAppearance(point, datasetIndex, index);
                }, this);
 
-               this.updateBezierControlPoints();
+               this.updateBezierControlPoints(dataset);
+       };
+
+       Chart.RectangularElementController.prototype.resetDatasetRectangles = function resetDatasetRectangles(dataset, datasetIndex) {
+               helpers.each(dataset.metaData, function(rectangle, index) {
+                       var xScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].xAxisID);
+                       var yScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].yAxisID);
+
+                       var yScalePoint;
+
+                       if (yScale.min < 0 && yScale.max < 0) {
+                               // all less than 0. use the top
+                               yScalePoint = yScale.getPixelForValue(yScale.max);
+                       } else if (yScale.min > 0 && yScale.max > 0) {
+                               yScalePoint = yScale.getPixelForValue(yScale.min);
+                       } else {
+                               yScalePoint = yScale.getPixelForValue(0);
+                       }
+
+                       helpers.extend(rectangle, {
+                               // Utility
+                               _chart: this.chartInstance.chart,
+                               _xScale: xScale,
+                               _yScale: yScale,
+                               _datasetIndex: datasetIndex,
+                               _index: index,
+
+                               // Desired view properties
+                               _model: {
+                                       x: xScale.calculateBarX(this.chartInstance.data.datasets.length, datasetIndex, index),
+                                       y: yScalePoint,
+
+                                       // Tooltip
+                                       label: this.chartInstance.data.labels[index],
+                                       datasetLabel: this.chartInstance.data.datasets[datasetIndex].label,
+
+                                       // Appearance
+                                       base: yScale.calculateBarBase(datasetIndex, index),
+                                       width: xScale.calculateBarWidth(this.chartInstance.data.datasets.length),
+                               },
+                       });
+
+                       this.updateRectangleElementAppearance(rectangle, datasetIndex, index);
+                       rectangle.pivot();
+               }, this);
+       };
+
+       Chart.RectangularElementController.prototype.resetElementAppearance = function(element, datasetIndex, index) {
+               if (element instanceof Chart.Point) {
+                       this.updatePointElementAppearance(element, datasetIndex, index);
+               } else if (element instanceof Chart.Rectangle) {
+                       this.updateRectangleElementAppearance(element, datasetIndex, index);
+               }
        };
 
        Chart.RectangularElementController.prototype.updateElements = function updateElements() {
                // Update the lines
                this.updateLines();
 
-               // Update the points
-               this.updatePoints();
+               helpers.each(this.chartInstance.data.datasets, function(dataset, datasetIndex) {
+                       // All elements must be the same type for the given dataset so we are fine to check just the first one
+                       if (dataset.metaData[0] instanceof Chart.Point) {
+                               // Have points. Update all of them
+                               this.updatePoints(dataset, datasetIndex);
+                       } else if (dataset.metaData[0] instanceof Chart.Rectangle) {
+                               // Have rectangles (bars)
+                               this.updateRectangles(dataset, datasetIndex);
+                       }
+               }, this);
        };
 
        Chart.RectangularElementController.prototype.updateLines = function updateLines() {
                });
        };
 
-       Chart.RectangularElementController.prototype.updatePoints = function() {
-               this.eachPoint(function(point, index, dataset, datasetIndex) {
+       Chart.RectangularElementController.prototype.updatePoints = function updatePoints(dataset, datasetIndex) {
+               helpers.each(dataset.metaData, function(point, index) {
                        var xScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].xAxisID);
                        var yScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].yAxisID);
 
                        this.updatePointElementAppearance(point, datasetIndex, index);
                }, this);
 
-               this.updateBezierControlPoints();
+               this.updateBezierControlPoints(dataset);
        };
 
-       Chart.RectangularElementController.prototype.updatePointElementAppearance = function(point, datasetIndex, index) {
+       Chart.RectangularElementController.prototype.updatePointElementAppearance = function updatePointElementAppearance(point, datasetIndex, index) {
                helpers.extend(point._model, {
                        // Appearance
                        tension: point.custom && point.custom.tension ? point.custom.tension : this.chartInstance.options.elements.line.tension,
                });
        };
 
-       Chart.RectangularElementController.prototype.setPointHoverStyle = function(point) {
+       Chart.RectangularElementController.prototype.updateRectangles = function updateRectangles(dataset, datasetIndex) {
+               helpers.each(dataset.metaData, function(rectangle, index){
+                       var xScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].xAxisID);
+                       var yScale = this.getScaleForId(this.chartInstance.data.datasets[datasetIndex].yAxisID);
+
+                       helpers.extend(rectangle, {
+                               // Utility
+                               _chart: this.chartInstance.chart,
+                               _xScale: xScale,
+                               _yScale: yScale,
+                               _datasetIndex: datasetIndex,
+                               _index: index,
+
+                               // Desired view properties
+                               _model: {
+                                       x: xScale.calculateBarX(this.chartInstance.data.datasets.length, datasetIndex, index),
+                                       y: yScale.calculateBarY(datasetIndex, index),
+
+                                       // Appearance
+                                       base: yScale.calculateBarBase(datasetIndex, index),
+                                       width: xScale.calculateBarWidth(this.chartInstance.data.datasets.length),
+
+                                       // Tooltip
+                                       label: this.chartInstance.data.labels[index],
+                                       datasetLabel: this.chartInstance.data.datasets[datasetIndex].label,
+                               },
+                       });
+
+                       this.updateRectangleElementAppearance(rectangle, datasetIndex, index);
+                       rectangle.pivot();
+               }, this);
+       };
+
+       Chart.RectangularElementController.prototype.updateRectangleElementAppearance = function updateRectangleElementAppearance(rectangle, datasetIndex, index) {
+               helpers.extend(rectangle._model, {
+                       // Appearance
+                       backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].backgroundColor, index, this.chartInstance.options.elements.rectangle.backgroundColor),
+                       borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].borderColor, index, this.chartInstance.options.elements.rectangle.borderColor),
+                       borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.chartInstance.data.datasets[datasetIndex].borderWidth, index, this.chartInstance.options.elements.rectangle.borderWidth),
+               });
+       };
+
+       Chart.RectangularElementController.prototype.setElementHoverStyle = function setElementHoverStyle(element) {
+               if (element instanceof Chart.Point) {
+                       this.setPointHoverStyle(element);
+               } else if (element instanceof Chart.Rectangle) {
+                       this.setRectangleHoverStyle(element);
+               }
+       };
+
+       Chart.RectangularElementController.prototype.setPointHoverStyle = function setPointHoverStyle(point) {
                var dataset = this.chartInstance.data.datasets[point._datasetIndex];
                var index = point._index;
 
                point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, point._model.borderWidth);
        };
 
-       Chart.RectangularElementController.prototype.updateBezierControlPoints = function updateBezierControlPoints() {
+       Chart.RectangularElementController.prototype.setRectangleHoverStyle = function(rectangle) {
+               var dataset = this.chartInstance.data.datasets[rectangle._datasetIndex];
+               var index = rectangle._index;
+
+               rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.hoverBackgroundColor ? rectangle.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(rectangle._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
+               rectangle._model.borderColor = rectangle.custom && rectangle.custom.hoverBorderColor ? rectangle.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(rectangle._model.borderColor).saturate(0.5).darken(0.1).rgbString());
+               rectangle._model.borderWidth = rectangle.custom && rectangle.custom.hoverBorderWidth ? rectangle.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangle._model.borderWidth);
+       };
+
+       Chart.RectangularElementController.prototype.updateBezierControlPoints = function updateBezierControlPoints(dataset) {
                // Update control points for the bezier curve
-               this.eachPoint(function(point, index, dataset, datasetIndex) {
+               helpers.each(dataset.metaData, function(point, index) {
                        var controlPoints = helpers.splineCurve(
-                               this.previousPoint(dataset, index)._model,
+                               this.previousPoint(dataset.metaData, index)._model,
                                point._model,
-                               this.nextPoint(dataset, index)._model,
+                               this.nextPoint(dataset.metaData, index)._model,
                                point._model.tension
                        );