]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Checkpoint 1, Pew!!!!
authorTanner Linsley <tannerlinsley@gmail.com>
Wed, 23 Sep 2015 01:31:26 +0000 (19:31 -0600)
committerTanner Linsley <tannerlinsley@gmail.com>
Wed, 23 Sep 2015 01:31:26 +0000 (19:31 -0600)
samples/bar-stacked.html
samples/bar.html
src/charts/Chart.Bar.js
src/controllers/controller.bar.js
src/controllers/controller.line.js
src/core/core.helpers.js
src/core/core.scale.js
src/core/core.scaleService.js [new file with mode: 0644]
src/scales/scale.category.js
src/scales/scale.linear.js
src/scales/scale.time.js

index 5c03e4aa1a0157c3b732366e3cc98119a4702e9f..3248365ecb2dadd6167acbe51b84daa621b07060 100644 (file)
     </div>
     <button id="randomizeData">Randomize Data</button>
     <script>
-    var randomScalingFactor = function() {
-        return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
-    };
-    var randomColorFactor = function() {
-        return Math.round(Math.random() * 255);
-    };
+        var randomScalingFactor = function() {
+            return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
+        };
+        var randomColorFactor = function() {
+            return Math.round(Math.random() * 255);
+        };
 
-    var barChartData = {
-        labels: ["January", "February", "March", "April", "May", "June", "July"],
-        datasets: [{
-            label: 'Dataset 1',
-            backgroundColor: "rgba(220,220,220,0.5)",
-            data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
-        }, {
-            label: 'Dataset 2',
-            backgroundColor: "rgba(151,187,205,0.5)",
-            data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
-        }, {
-            label: 'Dataset 3',
-            backgroundColor: "rgba(151,187,205,0.5)",
-            data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
-        }]
+        var barChartData = {
+            labels: ["January", "February", "March", "April", "May", "June", "July"],
+            datasets: [{
+                label: 'Dataset 1',
+                backgroundColor: "rgba(220,220,220,0.5)",
+                data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
+            }, {
+                label: 'Dataset 2',
+                backgroundColor: "rgba(151,187,205,0.5)",
+                data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
+            }, {
+                label: 'Dataset 3',
+                backgroundColor: "rgba(151,187,205,0.5)",
+                data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
+            }]
 
-    };
-    window.onload = function() {
-        var ctx = document.getElementById("canvas").getContext("2d");
-        window.myBar = new Chart(ctx, {
-            type: 'bar',
-            data: barChartData,
-            options: {
-                responsive: true,
-                scales: {
-                    xAxes: [{
-                        stacked: true,
-                    }],
-                    yAxes: [{
-                        stacked: true
-                    }]
+        };
+        window.onload = function() {
+            var ctx = document.getElementById("canvas").getContext("2d");
+            window.myBar = new Chart(ctx, {
+                type: 'bar',
+                data: barChartData,
+                options: {
+                    responsive: true,
+                    scales: {
+                        xAxes: [{
+                            stacked: true,
+                        }],
+                        yAxes: [{
+                            stacked: true
+                        }]
+                    }
                 }
-            }
-        });
-    };
+            });
+        };
 
-    $('#randomizeData').click(function() {
-        $.each(barChartData.datasets, function(i, dataset) {
-            dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
-            dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
+        $('#randomizeData').click(function() {
+            $.each(barChartData.datasets, function(i, dataset) {
+                dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
+                dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
 
+            });
+            window.myBar.update();
         });
-        window.myBar.update();
-    });
     </script>
 </body>
 
index 526fd8e5a2d076d4737a7d0f0a6c5198cc05d63e..d3d864c0b3233d64843119bf95757f543e8d6df1 100644 (file)
@@ -13,7 +13,7 @@
 </head>
 
 <body>
-    <div id="container" style="width: 50%; height: 25%; display:none;">
+    <div id="container" style="width: 50%; height: 25%;">
         <canvas id="canvas" height="450" width="600"></canvas>
     </div>
     <button id="randomizeData">Randomize Data</button>
                 data: barChartData,
                 options: {
                     responsive: true,
-                    scales: {
-                        xAxes: [{
-                            // So that bars fill the entire width of the grid
-                            categorySpacing: 10,
-                            spacing: 0
-                        }]
-                    }
                 }
             });
 
index 26db4ada008af2b7675691bc7da22e948b685e81..314d45182aa55a4c9dbaf30297f613604bdfb6b6 100644 (file)
@@ -9,6 +9,6 @@
                config.type = 'bar';
 
                return new Chart(context, config);
-       }
-       
+       };
+
 }).call(this);
index 6f9724e8b51361431f91f138bf3f98ba166687f6..80b0ff9d6d0d811a9745df77535f1d17c97def96 100644 (file)
                scales: {
                        xAxes: [{
                                type: "category",
-                               categorySpacing: 10,
-                               spacing: 1,
+
+                               // Specific to Bar Controller
+                               categoryPercentage: 0.8,
+                               barPercentage: 0.9,
 
                                // grid line settings
                                gridLines: {
@@ -57,7 +59,7 @@
                        return this.chart.data.datasets[this.index];
                },
 
-               getScaleForId: function(scaleID) {
+               getScaleForID: function(scaleID) {
                        return this.chart.scales[scaleID];
                },
 
                        // Make sure that we handle number of datapoints changing
                        if (numData < numRectangles) {
                                // Remove excess bars for data points that have been removed
-                               this.getDataset().metaData.splice(numData, numRectangles - numData)
+                               this.getDataset().metaData.splice(numData, numRectangles - numData);
                        } else if (numData > numRectangles) {
                                // Add new elements
                                for (var index = numRectangles; index < numData; ++index) {
                },
 
                updateElement: function updateElement(rectangle, index, reset, numBars) {
-                       var xScale = this.getScaleForId(this.getDataset().xAxisID);
-                       var yScale = this.getScaleForId(this.getDataset().yAxisID);
+
+                       var xScale = this.getScaleForID(this.getDataset().xAxisID);
+                       var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
                        var yScalePoint;
 
                        if (yScale.min < 0 && yScale.max < 0) {
 
                                // Desired view properties
                                _model: {
-                                       x: xScale.calculateBarX(numBars, this.index, index),
-                                       y: reset ? yScalePoint : yScale.calculateBarY(this.index, index),
+                                       x: this.calculateBarX(this.index, index),
+                                       y: reset ? yScalePoint : this.calculateBarY(this.index, index),
 
                                        // Tooltip
                                        label: this.chart.data.labels[index],
                                        datasetLabel: this.getDataset().label,
 
                                        // Appearance
-                                       base: yScale.calculateBarBase(this.index, index),
-                                       width: xScale.calculateBarWidth(numBars),
+                                       base: this.calculateBarBase(this.index, index),
+                                       width: this.calculateBarWidth(numBars),
                                        backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
                                        borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
                                        borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth),
                        rectangle.pivot();
                },
 
+               calculateBarBase: function(datasetIndex, index) {
+
+                       var xScale = this.getScaleForID(this.getDataset().xAxisID);
+                       var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
+                       var base = 0;
+
+                       if (yScale.options.stacked) {
+
+                               var value = this.chart.data.datasets[datasetIndex].data[index];
+
+                               if (value < 0) {
+                                       for (var i = 0; i < datasetIndex; i++) {
+                                               if (this.chart.data.datasets[i].yAxisID === yScale.id) {
+                                                       base += this.chart.data.datasets[i].data[index] < 0 ? this.chart.data.datasets[i].data[index] : 0;
+                                               }
+                                       }
+                               } else {
+                                       for (var j = 0; j < datasetIndex; j++) {
+                                               if (this.chart.data.datasets[j].yAxisID === yScale.id) {
+                                                       base += this.chart.data.datasets[j].data[index] > 0 ? this.chart.data.datasets[j].data[index] : 0;
+                                               }
+                                       }
+                               }
+
+                               return yScale.getPixelForValue(base);
+                       }
+
+                       base = yScale.getPixelForValue(yScale.min);
+
+                       if (yScale.beginAtZero || ((yScale.min <= 0 && yScale.max >= 0) || (yScale.min >= 0 && yScale.max <= 0))) {
+                               base = yScale.getPixelForValue(0);
+                               base += yScale.options.gridLines.lineWidth;
+                       } else if (yScale.min < 0 && yScale.max < 0) {
+                               // All values are negative. Use the top as the base
+                               base = yScale.getPixelForValue(yScale.max);
+                       }
+
+                       return base;
+
+               },
+
+               calculateBarWidth: function() {
+
+                       var xScale = this.getScaleForID(this.getDataset().xAxisID);
+                       var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
+                       if (xScale.options.stacked) {
+                               return xScale.ruler.categoryWidth;
+                       }
+
+                       return xScale.ruler.barWidth;
+
+               },
+
+
+               calculateBarX: function(datasetIndex, elementIndex) {
+
+                       var xScale = this.getScaleForID(this.getDataset().xAxisID);
+                       var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
+                       var leftTick = xScale.getPixelFromTickIndex(elementIndex);
+
+                       if (yScale.options.stacked) {
+                               return leftTick + (xScale.ruler.categoryWidth / 2) + xScale.ruler.categorySpacing;
+                       }
+
+                       return leftTick +
+                               (xScale.ruler.barWidth / 2) +
+                               xScale.ruler.categorySpacing +
+                               (xScale.ruler.barWidth * datasetIndex) +
+                               (xScale.ruler.barSpacing / 2) +
+                               (xScale.ruler.barSpacing * datasetIndex);
+               },
+
+               calculateBarY: function(datasetIndex, index) {
+
+                       var xScale = this.getScaleForID(this.getDataset().xAxisID);
+                       var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
+                       var value = this.getDataset().data[index];
+
+                       if (yScale.options.stacked) {
+
+                               var sumPos = 0,
+                                       sumNeg = 0;
+
+                               for (var i = 0; i < datasetIndex; i++) {
+                                       if (this.chart.data.datasets[i].data[index] < 0) {
+                                               sumNeg += this.chart.data.datasets[i].data[index] || 0;
+                                       } else {
+                                               sumPos += this.chart.data.datasets[i].data[index] || 0;
+                                       }
+                               }
+
+                               if (value < 0) {
+                                       return yScale.getPixelForValue(sumNeg + value);
+                               } else {
+                                       return yScale.getPixelForValue(sumPos + value);
+                               }
+
+                               return yScale.getPixelForValue(value);
+                       }
+
+                       return yScale.getPixelForValue(value);
+               },
+
                draw: function(ease) {
                        var easingDecimal = ease || 1;
                        helpers.each(this.getDataset().metaData, function(rectangle, index) {
index 608b5d704bf621aed33d0dd0c3f65ab31f301968..c56e2e2ac9b543aae254878d1fe379996478bfdb 100644 (file)
 
                                // Desired view properties
                                _model: {
-                                       x: xScale.getPointPixelForValue(this.getDataset().data[index], index, this.index),
-                                       y: reset ? scaleBase : yScale.getPointPixelForValue(this.getDataset().data[index], index, this.index),
+                                       x: xScale.getPixelForValue(this.getDataset().data[index], index, this.index),
+                                       y: reset ? scaleBase : yScale.getPixelForValue(this.getDataset().data[index], index, this.index),
                                        // Appearance
                                        tension: point.custom && point.custom.tension ? point.custom.tension : (this.getDataset().tension || this.chart.options.elements.line.tension),
                                        radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius),
index c4ea7940f7ec045ab59776a1b7564ffff3e56c1e..fa51ad68f22c8d0950e66ca1d130cbaa84bcfc1a 100644 (file)
 
                        return base;
                },
+               extendDeep = helpers.extendDeep = function(_base) {
+                       return _extendDeep.apply(this, arguments);
+
+                       function _extendDeep(dst) {
+                               helpers.each(arguments, function(obj) {
+                                       if (obj !== dst) {
+                                               helpers.each(obj, function(value, key) {
+                                                       if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
+                                                               _extendDeep(dst[key], value);
+                                                       } else {
+                                                               dst[key] = value;
+                                                       }
+                                               });
+                                       }
+                               });
+                               return dst;
+                       }
+               },
                scaleMerge = helpers.scaleMerge = function(_base, extension) {
                        var base = clone(_base);
 
                getMaximumWidth = helpers.getMaximumWidth = function(domNode) {
                        var container = domNode.parentNode;
                        var padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right'));
-                       
+
                        var w = container.clientWidth - padding;
                        var cw = getConstraintWidth(domNode);
                        if (cw !== undefined) {
                getMaximumHeight = helpers.getMaximumHeight = function(domNode) {
                        var container = domNode.parentNode;
                        var padding = parseInt(getStyle(container, 'padding-top')) + parseInt(getStyle(container, 'padding-bottom'));
-                       
+
                        var h = container.clientHeight - padding;
                        var ch = getConstraintHeight(domNode);
                        if (ch !== undefined) {
index 1159a90502917d86c237ad43c9b034a16aec720b..b9fdbbb4720e490ee217d3a3828febae5457a484 100644 (file)
                Chart = root.Chart,
                helpers = Chart.helpers;
 
-       // The scale service is used to resize charts along with all of their axes. We make this as
-       // a service where scales are registered with their respective charts so that changing the 
-       // scales does not require 
-       Chart.scaleService = {
-               // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
-               // use the new chart options to grab the correct scale
-               constructors: {},
-               // Use a registration function so that we can move to an ES6 map when we no longer need to support
-               // old browsers
-               // Scale config defaults
-               defaults: {},
-               registerScaleType: function(type, scaleConstructor, defaults) {
-                       this.constructors[type] = scaleConstructor;
-                       this.defaults[type] = defaults;
+       Chart.defaults.scale = {
+               display: true,
+
+               // grid line settings
+               gridLines: {
+                       show: true,
+                       color: "rgba(0, 0, 0, 0.1)",
+                       lineWidth: 1,
+                       drawOnChartArea: true,
+                       drawTicks: true,
+                       zeroLineWidth: 1,
+                       zeroLineColor: "rgba(0,0,0,0.25)",
+                       offsetGridLines: false,
                },
-               getScaleConstructor: function(type) {
-                       return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
+
+               // label settings
+               ticks: {
+                       show: true,
+                       minRotation: 20,
+                       maxRotation: 90,
+                       template: "<%=value%>",
+                       fontSize: 12,
+                       fontStyle: "normal",
+                       fontColor: "#666",
+                       fontFamily: "Helvetica Neue",
                },
-               getScaleDefaults: function(type) {
-                       return this.defaults.hasOwnProperty(type) ? this.defaults[type] : {};
+       };
+
+       Chart.Scale = Chart.Element.extend({
+               isHorizontal: function() {
+                       return this.options.position == "top" || this.options.position == "bottom";
                },
-               // The interesting function
-               fitScalesForChart: function(chartInstance, width, height) {
-                       var xPadding = width > 30 ? 5 : 2;
-                       var yPadding = height > 30 ? 5 : 2;
-
-                       if (chartInstance) {
-                               var leftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
-                                       return scaleInstance.options.position == "left";
-                               });
-                               var rightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
-                                       return scaleInstance.options.position == "right";
-                               });
-                               var topScales = helpers.where(chartInstance.scales, function(scaleInstance) {
-                                       return scaleInstance.options.position == "top";
-                               });
-                               var bottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
-                                       return scaleInstance.options.position == "bottom";
-                               });
-
-                               // Essentially we now have any number of scales on each of the 4 sides.
-                               // Our canvas looks like the following.
-                               // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and 
-                               // B1 is the bottom axis
-                               // |------------------------------------------------------|
-                               // |          |             T1                      |     |
-                               // |----|-----|-------------------------------------|-----|
-                               // |    |     |                                     |     |
-                               // | L1 |  L2 |         Chart area                  |  R1 |
-                               // |    |     |                                     |     |
-                               // |    |     |                                     |     |
-                               // |----|-----|-------------------------------------|-----|
-                               // |          |             B1                      |     |
-                               // |          |                                     |     |
-                               // |------------------------------------------------------|
-
-                               // What we do to find the best sizing, we do the following
-                               // 1. Determine the minimum size of the chart area. 
-                               // 2. Split the remaining width equally between each vertical axis
-                               // 3. Split the remaining height equally between each horizontal axis
-                               // 4. Give each scale the maximum size it can be. The scale will return it's minimum size
-                               // 5. Adjust the sizes of each axis based on it's minimum reported size. 
-                               // 6. Refit each axis
-                               // 7. Position each axis in the final location
-                               // 8. Tell the chart the final location of the chart area
-
-                               // Step 1
-                               var chartWidth = width / 2; // min 50%
-                               var chartHeight = height / 2; // min 50%
-
-                               chartWidth -= (2 * xPadding);
-                               chartHeight -= (2 * yPadding);
-
-
-                               // Step 2
-                               var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
-
-                               // Step 3
-                               var horizontalScaleHeight = (height - chartHeight) / (topScales.length + bottomScales.length);
-
-                               // Step 4;
-                               var minimumScaleSizes = [];
-
-                               var verticalScaleMinSizeFunction = function(scaleInstance) {
-                                       var minSize = scaleInstance.fit(verticalScaleWidth, chartHeight);
-                                       minimumScaleSizes.push({
-                                               horizontal: false,
-                                               minSize: minSize,
-                                               scale: scaleInstance,
-                                       });
-                               };
-
-                               var horizontalScaleMinSizeFunction = function(scaleInstance) {
-                                       var minSize = scaleInstance.fit(chartWidth, horizontalScaleHeight);
-                                       minimumScaleSizes.push({
-                                               horizontal: true,
-                                               minSize: minSize,
-                                               scale: scaleInstance,
-                                       });
-                               };
-
-                               // vertical scales
-                               helpers.each(leftScales, verticalScaleMinSizeFunction);
-                               helpers.each(rightScales, verticalScaleMinSizeFunction);
-
-                               // horizontal scales
-                               helpers.each(topScales, horizontalScaleMinSizeFunction);
-                               helpers.each(bottomScales, horizontalScaleMinSizeFunction);
-
-                               // Step 5
-                               var maxChartHeight = height - (2 * yPadding);
-                               var maxChartWidth = width - (2 * xPadding);
-
-                               helpers.each(minimumScaleSizes, function(wrapper) {
-                                       if (wrapper.horizontal) {
-                                               maxChartHeight -= wrapper.minSize.height;
-                                       } else {
-                                               maxChartWidth -= wrapper.minSize.width;
-                                       }
-                               });
+               calculateTickRotation: function(maxHeight, margins) {
+                       //Get the width of each grid by calculating the difference
+                       //between x offsets between 0 and 1.
+                       var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
+                       this.ctx.font = labelFont;
+
+                       var firstWidth = this.ctx.measureText(this.ticks[0]).width;
+                       var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
+                       var firstRotated;
+                       var lastRotated;
+
+                       this.paddingRight = lastWidth / 2 + 3;
+                       this.paddingLeft = firstWidth / 2 + 3;
+
+                       this.labelRotation = 0;
 
-                               // At this point, maxChartHeight and maxChartWidth are the size the chart area could
-                               // be if the axes are drawn at their minimum sizes.
+                       if (this.options.display) {
+                               var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
+                               var cosRotation;
+                               var sinRotation;
+                               var firstRotatedWidth;
 
-                               // Step 6
-                               var verticalScaleFitFunction = function(scaleInstance) {
-                                       var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
-                                               return wrapper.scale === scaleInstance;
-                                       });
+                               this.labelWidth = originalLabelWidth;
 
-                                       if (wrapper) {
-                                               scaleInstance.fit(wrapper.minSize.width, maxChartHeight);
+                               //Allow 3 pixels x2 padding either side for label readability
+                               // only the index matters for a dataset scale, but we want a consistent interface between scales
+
+                               var tickWidth = this.ruler.tick - 6;
+
+                               //Max label rotation can be set or default to 90 - also act as a loop counter
+                               while (this.labelWidth > tickWidth && this.labelRotation <= this.options.ticks.maxRotation) {
+                                       cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
+                                       sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
+
+                                       firstRotated = cosRotation * firstWidth;
+                                       lastRotated = cosRotation * lastWidth;
+
+                                       // We're right aligning the text now.
+                                       if (firstRotated + this.options.ticks.fontSize / 2 > this.yLabelWidth) {
+                                               this.paddingLeft = firstRotated + this.options.ticks.fontSize / 2;
                                        }
-                               };
-
-                               var horizontalScaleFitFunction = function(scaleInstance) {
-                                       var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
-                                               return wrapper.scale === scaleInstance;
-                                       });
-
-                                       var scaleMargin = {
-                                               left: totalLeftWidth,
-                                               right: totalRightWidth,
-                                               top: 0,
-                                               bottom: 0,
-                                       };
-
-                                       if (wrapper) {
-                                               scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
+
+                                       this.paddingRight = this.options.ticks.fontSize / 2;
+
+                                       if (sinRotation * originalLabelWidth > maxHeight) {
+                                               // go back one step
+                                               this.labelRotation--;
+                                               break;
                                        }
-                               };
-
-                               var totalLeftWidth = xPadding;
-                               var totalRightWidth = xPadding;
-                               var totalTopHeight = yPadding;
-                               var totalBottomHeight = yPadding;
-
-                               helpers.each(leftScales, verticalScaleFitFunction);
-                               helpers.each(rightScales, verticalScaleFitFunction);
-
-                               // Figure out how much margin is on the left and right of the horizontal axes
-                               helpers.each(leftScales, function(scaleInstance) {
-                                       totalLeftWidth += scaleInstance.width;
-                               });
-
-                               helpers.each(rightScales, function(scaleInstance) {
-                                       totalRightWidth += scaleInstance.width;
-                               });
-
-                               helpers.each(topScales, horizontalScaleFitFunction);
-                               helpers.each(bottomScales, horizontalScaleFitFunction);
-
-                               helpers.each(topScales, function(scaleInstance) {
-                                       totalTopHeight += scaleInstance.height;
-                               });
-                               helpers.each(bottomScales, function(scaleInstance) {
-                                       totalBottomHeight += scaleInstance.height;
-                               });
-
-                               // Let the left scale know the final margin
-                               helpers.each(leftScales, function(scaleInstance) {
-                                       var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
-                                               return wrapper.scale === scaleInstance;
-                                       });
-
-                                       var scaleMargin = {
-                                               left: 0,
-                                               right: 0,
-                                               top: totalTopHeight,
-                                               bottom: totalBottomHeight
-                                       };
-
-                                       if (wrapper) {
-                                               scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
+
+                                       this.labelRotation++;
+                                       this.labelRotation = Math.max(this.labelRotation, this.options.ticks.minRotation);
+                                       this.labelWidth = cosRotation * originalLabelWidth;
+
+                               }
+                       } else {
+                               this.labelWidth = 0;
+                               this.paddingRight = 0;
+                               this.paddingLeft = 0;
+                       }
+
+                       if (margins) {
+                               this.paddingLeft -= margins.left;
+                               this.paddingRight -= margins.right;
+
+                               this.paddingLeft = Math.max(this.paddingLeft, 0);
+                               this.paddingRight = Math.max(this.paddingRight, 0);
+                       }
+
+               },
+               getPixelForValue: function(value, index, datasetIndex, includeOffset) {
+                       // This must be called after fit has been run so that 
+                       //      this.left, this.top, this.right, and this.bottom have been defined
+                       if (this.isHorizontal()) {
+                               var isRotated = (this.labelRotation > 0);
+                               var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
+                               var valueWidth = innerWidth / Math.max((this.data.labels.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
+                               var valueOffset = (valueWidth * index) + this.paddingLeft;
+
+                               if (this.options.gridLines.offsetGridLines && includeOffset) {
+                                       valueOffset += (valueWidth / 2);
+                               }
+
+                               return this.left + Math.round(valueOffset);
+                       } else {
+                               return this.top + (index * (this.height / this.labels.length));
+                       }
+               },
+               getPixelFromTickIndex: function(index, includeOffset) {
+                       // This must be called after fit has been run so that 
+                       //      this.left, this.top, this.right, and this.bottom have been defined
+                       if (this.isHorizontal()) {
+                               var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
+                               var tickWidth = innerWidth / Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
+                               var pixel = (tickWidth * index) + this.paddingLeft;
+
+                               if (includeOffset) {
+                                       pixel += tickWidth / 2;
+                               }
+                               return this.left + Math.round(pixel);
+                       } else {
+                               return this.top + (index * (this.height / this.ticks.length));
+                       }
+               },
+               getPixelFromDecimal: function(decimal, includeOffset) {
+                       // This must be called after fit has been run so that 
+                       //      this.left, this.top, this.right, and this.bottom have been defined
+                       if (this.isHorizontal()) {
+                               var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
+                               var valueOffset = (innerWidth * decimal) + this.paddingLeft;
+
+                               return this.left + Math.round(valueOffset);
+                       } else {
+                               return this.top + (decimal * (this.height / this.ticks.length));
+                       }
+               },
+               // Fit this axis to the given size
+               // @param {number} maxWidth : the max width the axis can be
+               // @param {number} maxHeight: the max height the axis can be
+               // @return {object} minSize : the minimum size needed to draw the axis
+               fit: function(maxWidth, maxHeight, margins) {
+                       // Set the unconstrained dimension before label rotation
+                       if (this.isHorizontal()) {
+                               this.width = maxWidth;
+                       } else {
+                               this.height = maxHeight;
+                       }
+
+                       this.buildTicks();
+                       this.buildRuler();
+                       this.calculateTickRotation(maxHeight, margins);
+
+                       var minSize = {
+                               width: 0,
+                               height: 0,
+                       };
+
+                       var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
+                       var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
+
+                       // Width
+                       if (this.isHorizontal()) {
+                               minSize.width = maxWidth;
+                       } else if (this.options.display) {
+                               var labelWidth = this.options.ticks.show ? longestLabelWidth + 6 : 0;
+                               minSize.width = Math.min(labelWidth, maxWidth);
+                       }
+
+                       // Height
+                       if (this.isHorizontal() && this.options.display) {
+                               var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.ticks.fontSize;
+                               minSize.height = Math.min(this.options.ticks.show ? labelHeight : 0, maxHeight);
+                       } else if (this.options.display) {
+                               minSize.height = maxHeight;
+                       }
+
+
+                       this.width = minSize.width;
+                       this.height = minSize.height;
+                       return minSize;
+               },
+               // Actualy draw the scale on the canvas
+               // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
+               draw: function(chartArea) {
+                       if (this.options.display) {
+
+                               var setContextLineSettings;
+
+                               // Make sure we draw text in the correct color
+                               this.ctx.fillStyle = this.options.ticks.fontColor;
+
+                               if (this.isHorizontal()) {
+                                       setContextLineSettings = true;
+                                       var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 10;
+                                       var yTickEnd = this.options.position == "bottom" ? this.top + 10 : this.bottom;
+                                       var isRotated = this.labelRotation !== 0;
+                                       var skipRatio = false;
+
+                                       if ((this.options.ticks.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
+                                               skipRatio = 1 + Math.floor(((this.options.ticks.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
                                        }
-                               });
-
-                               helpers.each(rightScales, function(scaleInstance) {
-                                       var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
-                                               return wrapper.scale === scaleInstance;
-                                       });
-
-                                       var scaleMargin = {
-                                               left: 0,
-                                               right: 0,
-                                               top: totalTopHeight,
-                                               bottom: totalBottomHeight
-                                       };
-
-                                       if (wrapper) {
-                                               scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
+
+                                       helpers.each(this.ticks, function(label, index) {
+                                               // Blank ticks
+                                               if ((skipRatio > 1 && index % skipRatio > 0) || (label === undefined || label === null)) {
+                                                       return;
+                                               }
+                                               var xLineValue = this.getPixelFromTickIndex(index); // xvalues for grid lines
+                                               var xLabelValue = this.getPixelFromTickIndex(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option)
+
+                                               if (this.options.gridLines.show) {
+                                                       if (index === 0) {
+                                                               // Draw the first index specially
+                                                               this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
+                                                               this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
+                                                               setContextLineSettings = true; // reset next time
+                                                       } else if (setContextLineSettings) {
+                                                               this.ctx.lineWidth = this.options.gridLines.lineWidth;
+                                                               this.ctx.strokeStyle = this.options.gridLines.color;
+                                                               setContextLineSettings = false;
+                                                       }
+
+                                                       xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
+
+                                                       // Draw the label area
+                                                       this.ctx.beginPath();
+
+                                                       if (this.options.gridLines.drawTicks) {
+                                                               this.ctx.moveTo(xLineValue, yTickStart);
+                                                               this.ctx.lineTo(xLineValue, yTickEnd);
+                                                       }
+
+                                                       // Draw the chart area
+                                                       if (this.options.gridLines.drawOnChartArea) {
+                                                               this.ctx.moveTo(xLineValue, chartArea.top);
+                                                               this.ctx.lineTo(xLineValue, chartArea.bottom);
+                                                       }
+
+                                                       // Need to stroke in the loop because we are potentially changing line widths & colours
+                                                       this.ctx.stroke();
+                                               }
+
+                                               if (this.options.ticks.show) {
+                                                       this.ctx.save();
+                                                       this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.top + 8);
+                                                       this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
+                                                       this.ctx.font = this.font;
+                                                       this.ctx.textAlign = (isRotated) ? "right" : "center";
+                                                       this.ctx.textBaseline = (isRotated) ? "middle" : "top";
+                                                       this.ctx.fillText(label, 0, 0);
+                                                       this.ctx.restore();
+                                               }
+                                       }, this);
+                               } else {
+                                       // TODO Vertical
+                                       if (this.options.gridLines.show) {}
+
+                                       if (this.options.ticks.show) {
+                                               // Draw the ticks
                                        }
-                               });
-
-                               // Recalculate because the size of each scale might have changed slightly due to the margins (label rotation for instance)
-                               totalLeftWidth = xPadding;
-                               totalRightWidth = xPadding;
-                               totalTopHeight = yPadding;
-                               totalBottomHeight = yPadding;
-
-                               helpers.each(leftScales, function(scaleInstance) {
-                                       totalLeftWidth += scaleInstance.width;
-                               });
-
-                               helpers.each(rightScales, function(scaleInstance) {
-                                       totalRightWidth += scaleInstance.width;
-                               });
-
-                               helpers.each(topScales, function(scaleInstance) {
-                                       totalTopHeight += scaleInstance.height;
-                               });
-                               helpers.each(bottomScales, function(scaleInstance) {
-                                       totalBottomHeight += scaleInstance.height;
-                               });
-
-                               // Figure out if our chart area changed. This would occur if the dataset scale label rotation
-                               // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
-                               // without calling `fit` again
-                               var newMaxChartHeight = height - totalTopHeight - totalBottomHeight;
-                               var newMaxChartWidth = width - totalLeftWidth - totalRightWidth;
-
-                               if (newMaxChartWidth !== maxChartWidth || newMaxChartHeight !== maxChartHeight) {
-                                       helpers.each(leftScales, function(scale) {
-                                               scale.height = newMaxChartHeight;
-                                       });
-
-                                       helpers.each(rightScales, function(scale) {
-                                               scale.height = newMaxChartHeight;
-                                       });
-
-                                       helpers.each(topScales, function(scale) {
-                                               scale.width = newMaxChartWidth;
-                                       });
-
-                                       helpers.each(bottomScales, function(scale) {
-                                               scale.width = newMaxChartWidth;
-                                       });
-
-                                       maxChartHeight = newMaxChartHeight;
-                                       maxChartWidth = newMaxChartWidth;
                                }
-
-                               // Step 7 
-                               // Position the scales
-                               var left = xPadding;
-                               var top = yPadding;
-                               var right = 0;
-                               var bottom = 0;
-
-                               var verticalScalePlacer = function(scaleInstance) {
-                                       scaleInstance.left = left;
-                                       scaleInstance.right = left + scaleInstance.width;
-                                       scaleInstance.top = totalTopHeight;
-                                       scaleInstance.bottom = totalTopHeight + maxChartHeight;
-
-                                       // Move to next point
-                                       left = scaleInstance.right;
-                               };
-
-                               var horizontalScalePlacer = function(scaleInstance) {
-                                       scaleInstance.left = totalLeftWidth;
-                                       scaleInstance.right = totalLeftWidth + maxChartWidth;
-                                       scaleInstance.top = top;
-                                       scaleInstance.bottom = top + scaleInstance.height;
-
-                                       // Move to next point 
-                                       top = scaleInstance.bottom;
-                               };
-
-                               helpers.each(leftScales, verticalScalePlacer);
-                               helpers.each(topScales, horizontalScalePlacer);
-
-                               // Account for chart width and height
-                               left += maxChartWidth;
-                               top += maxChartHeight;
-
-                               helpers.each(rightScales, verticalScalePlacer);
-                               helpers.each(bottomScales, horizontalScalePlacer);
-
-                               // Step 8
-                               chartInstance.chartArea = {
-                                       left: totalLeftWidth,
-                                       top: totalTopHeight,
-                                       right: totalLeftWidth + maxChartWidth,
-                                       bottom: totalTopHeight + maxChartHeight,
-                               };
                        }
                }
-       };
+       });
+
 }).call(this);
diff --git a/src/core/core.scaleService.js b/src/core/core.scaleService.js
new file mode 100644 (file)
index 0000000..75b76ff
--- /dev/null
@@ -0,0 +1,319 @@
+(function() {
+       "use strict";
+
+       var root = this,
+               Chart = root.Chart,
+               helpers = Chart.helpers;
+
+       // The scale service is used to resize charts along with all of their axes. We make this as
+       // a service where scales are registered with their respective charts so that changing the 
+       // scales does not require 
+       Chart.scaleService = {
+               // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
+               // use the new chart options to grab the correct scale
+               constructors: {},
+               // Use a registration function so that we can move to an ES6 map when we no longer need to support
+               // old browsers
+               // Scale config defaults
+               defaults: {},
+               registerScaleType: function(type, scaleConstructor, defaults) {
+                       this.constructors[type] = scaleConstructor;
+                       this.defaults[type] = helpers.extendDeep({}, Chart.defaults.scale, defaults);
+               },
+               getScaleConstructor: function(type) {
+                       return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
+               },
+               getScaleDefaults: function(type) {
+                       return this.defaults.hasOwnProperty(type) ? this.defaults[type] : {};
+               },
+               // The interesting function
+               fitScalesForChart: function(chartInstance, width, height) {
+                       var xPadding = width > 30 ? 5 : 2;
+                       var yPadding = height > 30 ? 5 : 2;
+
+                       if (chartInstance) {
+                               var leftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
+                                       return scaleInstance.options.position == "left";
+                               });
+                               var rightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
+                                       return scaleInstance.options.position == "right";
+                               });
+                               var topScales = helpers.where(chartInstance.scales, function(scaleInstance) {
+                                       return scaleInstance.options.position == "top";
+                               });
+                               var bottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
+                                       return scaleInstance.options.position == "bottom";
+                               });
+
+                               // Essentially we now have any number of scales on each of the 4 sides.
+                               // Our canvas looks like the following.
+                               // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and 
+                               // B1 is the bottom axis
+                               // |------------------------------------------------------|
+                               // |          |             T1                      |     |
+                               // |----|-----|-------------------------------------|-----|
+                               // |    |     |                                     |     |
+                               // | L1 |  L2 |         Chart area                  |  R1 |
+                               // |    |     |                                     |     |
+                               // |    |     |                                     |     |
+                               // |----|-----|-------------------------------------|-----|
+                               // |          |             B1                      |     |
+                               // |          |                                     |     |
+                               // |------------------------------------------------------|
+
+                               // What we do to find the best sizing, we do the following
+                               // 1. Determine the minimum size of the chart area. 
+                               // 2. Split the remaining width equally between each vertical axis
+                               // 3. Split the remaining height equally between each horizontal axis
+                               // 4. Give each scale the maximum size it can be. The scale will return it's minimum size
+                               // 5. Adjust the sizes of each axis based on it's minimum reported size. 
+                               // 6. Refit each axis
+                               // 7. Position each axis in the final location
+                               // 8. Tell the chart the final location of the chart area
+
+                               // Step 1
+                               var chartWidth = width / 2; // min 50%
+                               var chartHeight = height / 2; // min 50%
+
+                               chartWidth -= (2 * xPadding);
+                               chartHeight -= (2 * yPadding);
+
+
+                               // Step 2
+                               var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
+
+                               // Step 3
+                               var horizontalScaleHeight = (height - chartHeight) / (topScales.length + bottomScales.length);
+
+                               // Step 4;
+                               var minimumScaleSizes = [];
+
+                               var verticalScaleMinSizeFunction = function(scaleInstance) {
+                                       var minSize = scaleInstance.fit(verticalScaleWidth, chartHeight);
+                                       minimumScaleSizes.push({
+                                               horizontal: false,
+                                               minSize: minSize,
+                                               scale: scaleInstance,
+                                       });
+                               };
+
+                               var horizontalScaleMinSizeFunction = function(scaleInstance) {
+                                       var minSize = scaleInstance.fit(chartWidth, horizontalScaleHeight);
+                                       minimumScaleSizes.push({
+                                               horizontal: true,
+                                               minSize: minSize,
+                                               scale: scaleInstance,
+                                       });
+                               };
+
+                               // vertical scales
+                               helpers.each(leftScales, verticalScaleMinSizeFunction);
+                               helpers.each(rightScales, verticalScaleMinSizeFunction);
+
+                               // horizontal scales
+                               helpers.each(topScales, horizontalScaleMinSizeFunction);
+                               helpers.each(bottomScales, horizontalScaleMinSizeFunction);
+
+                               // Step 5
+                               var maxChartHeight = height - (2 * yPadding);
+                               var maxChartWidth = width - (2 * xPadding);
+
+                               helpers.each(minimumScaleSizes, function(wrapper) {
+                                       if (wrapper.horizontal) {
+                                               maxChartHeight -= wrapper.minSize.height;
+                                       } else {
+                                               maxChartWidth -= wrapper.minSize.width;
+                                       }
+                               });
+
+                               // At this point, maxChartHeight and maxChartWidth are the size the chart area could
+                               // be if the axes are drawn at their minimum sizes.
+
+                               // Step 6
+                               var verticalScaleFitFunction = function(scaleInstance) {
+                                       var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
+                                               return wrapper.scale === scaleInstance;
+                                       });
+
+                                       if (wrapper) {
+                                               scaleInstance.fit(wrapper.minSize.width, maxChartHeight);
+                                       }
+                               };
+
+                               var horizontalScaleFitFunction = function(scaleInstance) {
+                                       var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
+                                               return wrapper.scale === scaleInstance;
+                                       });
+
+                                       var scaleMargin = {
+                                               left: totalLeftWidth,
+                                               right: totalRightWidth,
+                                               top: 0,
+                                               bottom: 0,
+                                       };
+
+                                       if (wrapper) {
+                                               scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
+                                       }
+                               };
+
+                               var totalLeftWidth = xPadding;
+                               var totalRightWidth = xPadding;
+                               var totalTopHeight = yPadding;
+                               var totalBottomHeight = yPadding;
+
+                               helpers.each(leftScales, verticalScaleFitFunction);
+                               helpers.each(rightScales, verticalScaleFitFunction);
+
+                               // Figure out how much margin is on the left and right of the horizontal axes
+                               helpers.each(leftScales, function(scaleInstance) {
+                                       totalLeftWidth += scaleInstance.width;
+                               });
+
+                               helpers.each(rightScales, function(scaleInstance) {
+                                       totalRightWidth += scaleInstance.width;
+                               });
+
+                               helpers.each(topScales, horizontalScaleFitFunction);
+                               helpers.each(bottomScales, horizontalScaleFitFunction);
+
+                               helpers.each(topScales, function(scaleInstance) {
+                                       totalTopHeight += scaleInstance.height;
+                               });
+                               helpers.each(bottomScales, function(scaleInstance) {
+                                       totalBottomHeight += scaleInstance.height;
+                               });
+
+                               // Let the left scale know the final margin
+                               helpers.each(leftScales, function(scaleInstance) {
+                                       var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
+                                               return wrapper.scale === scaleInstance;
+                                       });
+
+                                       var scaleMargin = {
+                                               left: 0,
+                                               right: 0,
+                                               top: totalTopHeight,
+                                               bottom: totalBottomHeight
+                                       };
+
+                                       if (wrapper) {
+                                               scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
+                                       }
+                               });
+
+                               helpers.each(rightScales, function(scaleInstance) {
+                                       var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
+                                               return wrapper.scale === scaleInstance;
+                                       });
+
+                                       var scaleMargin = {
+                                               left: 0,
+                                               right: 0,
+                                               top: totalTopHeight,
+                                               bottom: totalBottomHeight
+                                       };
+
+                                       if (wrapper) {
+                                               scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
+                                       }
+                               });
+
+                               // Recalculate because the size of each scale might have changed slightly due to the margins (label rotation for instance)
+                               totalLeftWidth = xPadding;
+                               totalRightWidth = xPadding;
+                               totalTopHeight = yPadding;
+                               totalBottomHeight = yPadding;
+
+                               helpers.each(leftScales, function(scaleInstance) {
+                                       totalLeftWidth += scaleInstance.width;
+                               });
+
+                               helpers.each(rightScales, function(scaleInstance) {
+                                       totalRightWidth += scaleInstance.width;
+                               });
+
+                               helpers.each(topScales, function(scaleInstance) {
+                                       totalTopHeight += scaleInstance.height;
+                               });
+                               helpers.each(bottomScales, function(scaleInstance) {
+                                       totalBottomHeight += scaleInstance.height;
+                               });
+
+                               // Figure out if our chart area changed. This would occur if the dataset scale label rotation
+                               // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
+                               // without calling `fit` again
+                               var newMaxChartHeight = height - totalTopHeight - totalBottomHeight;
+                               var newMaxChartWidth = width - totalLeftWidth - totalRightWidth;
+
+                               if (newMaxChartWidth !== maxChartWidth || newMaxChartHeight !== maxChartHeight) {
+                                       helpers.each(leftScales, function(scale) {
+                                               scale.height = newMaxChartHeight;
+                                       });
+
+                                       helpers.each(rightScales, function(scale) {
+                                               scale.height = newMaxChartHeight;
+                                       });
+
+                                       helpers.each(topScales, function(scale) {
+                                               scale.width = newMaxChartWidth;
+                                       });
+
+                                       helpers.each(bottomScales, function(scale) {
+                                               scale.width = newMaxChartWidth;
+                                       });
+
+                                       maxChartHeight = newMaxChartHeight;
+                                       maxChartWidth = newMaxChartWidth;
+                               }
+
+                               // Step 7 
+                               // Position the scales
+                               var left = xPadding;
+                               var top = yPadding;
+                               var right = 0;
+                               var bottom = 0;
+
+                               var verticalScalePlacer = function(scaleInstance) {
+                                       scaleInstance.left = left;
+                                       scaleInstance.right = left + scaleInstance.width;
+                                       scaleInstance.top = totalTopHeight;
+                                       scaleInstance.bottom = totalTopHeight + maxChartHeight;
+
+                                       // Move to next point
+                                       left = scaleInstance.right;
+                               };
+
+                               var horizontalScalePlacer = function(scaleInstance) {
+                                       scaleInstance.left = totalLeftWidth;
+                                       scaleInstance.right = totalLeftWidth + maxChartWidth;
+                                       scaleInstance.top = top;
+                                       scaleInstance.bottom = top + scaleInstance.height;
+
+                                       // Move to next point 
+                                       top = scaleInstance.bottom;
+                               };
+
+                               helpers.each(leftScales, verticalScalePlacer);
+                               helpers.each(topScales, horizontalScalePlacer);
+
+                               // Account for chart width and height
+                               left += maxChartWidth;
+                               top += maxChartHeight;
+
+                               helpers.each(rightScales, verticalScalePlacer);
+                               helpers.each(bottomScales, horizontalScalePlacer);
+
+                               // Step 8
+                               chartInstance.chartArea = {
+                                       left: totalLeftWidth,
+                                       top: totalTopHeight,
+                                       right: totalLeftWidth + maxChartWidth,
+                                       bottom: totalTopHeight + maxChartHeight,
+                               };
+                       }
+               }
+       };
+
+
+}).call(this);
index 939d32404d0d2abe383a8dd4d36ee4087ef1c7d7..6a305193bf9103d5ddf9aa06aec60b66eb1986ec 100644 (file)
 
     // Default config for a category scale
     var defaultConfig = {
-        display: true,
         position: "bottom",
-
-        // grid line settings
-        gridLines: {
-            show: true,
-            color: "rgba(0, 0, 0, 0.1)",
-            lineWidth: 1,
-            drawOnChartArea: true,
-            drawTicks: true,
-            zeroLineWidth: 1,
-            zeroLineColor: "rgba(0,0,0,0.25)",
-            offsetGridLines: false,
-        },
-
-        // scale label
-        scaleLabel: {
-            fontColor: '#666',
-            fontFamily: 'Helvetica Neue',
-            fontSize: 12,
-            fontStyle: 'normal',
-
-            // actual label
-            labelString: '',
-
-            // display property
-            show: false,
-        },
-
-        // label settings
-        labels: {
-            show: true,
-            maxRotation: 90,
-            template: "<%=value%>",
-            fontSize: 12,
-            fontStyle: "normal",
-            fontColor: "#666",
-            fontFamily: "Helvetica Neue",
-        },
     };
 
-    var DatasetScale = Chart.Element.extend({
-        isHorizontal: function() {
-            return this.options.position == "top" || this.options.position == "bottom";
-        },
-        buildLabels: function(index) {
-            this.labels = [];
+    var DatasetScale = Chart.Scale.extend({
+        buildTicks: function(index) {
+            this.ticks = [];
 
-            if (this.options.labels.userCallback) {
+            if (this.options.ticks.userCallback) {
                 this.data.labels.forEach(function(labelString, index) {
-                    this.labels.push(this.options.labels.userCallback(labelString, index));
+                    this.ticks.push(this.options.ticks.userCallback(labelString, index));
                 }, this);
             } else {
-                this.labels = this.data.labels;
-            }
-        },
-        getPixelForValue: function(value, index, datasetIndex, includeOffset) {
-            // This must be called after fit has been run so that 
-            //      this.left, this.top, this.right, and this.bottom have been defined
-            if (this.isHorizontal()) {
-                var isRotated = (this.labelRotation > 0);
-                var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
-                var valueWidth = innerWidth / Math.max((this.labels.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
-                var valueOffset = (valueWidth * index) + this.paddingLeft;
-
-                if (this.options.gridLines.offsetGridLines && includeOffset) {
-                    valueOffset += (valueWidth / 2);
-                }
-
-                return this.left + Math.round(valueOffset);
-            } else {
-                return this.top + (index * (this.height / this.labels.length));
-            }
-        },
-        getPointPixelForValue: function(value, index, datasetIndex) {
-            return this.getPixelForValue(value, index, datasetIndex, true);
-        },
-
-        // Functions needed for bar charts
-        calculateBaseWidth: function() {
-            var base = this.getPixelForValue(null, 1, 0, true) - this.getPixelForValue(null, 0, 0, true);
-            var spacing = 2 * this.options.categorySpacing;
-            if (base < spacing * 2) {
-                var mod = Math.min((spacing * 2) / base, 1.5);
-                base = (base / 2) * mod;
-                return base;
-            }
-            return base - spacing;
-        },
-        calculateBarWidth: function(barDatasetCount) {
-            //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
-            var baseWidth = Math.max(this.calculateBaseWidth() - (this.options.stacked ? 0 : (barDatasetCount - 1) * this.options.spacing), 1);
-
-            if (this.options.stacked) {
-                return Math.max(baseWidth, 1);
-            }
-            return Math.max((baseWidth / barDatasetCount), 1);
-        },
-        calculateBarX: function(barDatasetCount, datasetIndex, elementIndex) {
-            var xWidth = this.calculateBaseWidth(),
-                xAbsolute = this.getPixelForValue(null, elementIndex, datasetIndex, true) - (xWidth / 2),
-                barWidth = this.calculateBarWidth(barDatasetCount);
-
-            if (this.options.stacked) {
-                return xAbsolute + barWidth / 2;
-            }
-
-            return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * this.options.spacing) + barWidth / 2;
-        },
-
-        calculateLabelRotation: function(maxHeight, margins) {
-            //Get the width of each grid by calculating the difference
-            //between x offsets between 0 and 1.
-            var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
-            this.ctx.font = labelFont;
-
-            var firstWidth = this.ctx.measureText(this.labels[0]).width;
-            var lastWidth = this.ctx.measureText(this.labels[this.labels.length - 1]).width;
-            var firstRotated;
-            var lastRotated;
-
-            this.paddingRight = lastWidth / 2 + 3;
-            this.paddingLeft = firstWidth / 2 + 3;
-
-            this.labelRotation = 0;
-
-            if (this.options.display) {
-                var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.labels);
-                var cosRotation;
-                var sinRotation;
-                var firstRotatedWidth;
-
-                this.labelWidth = originalLabelWidth;
-
-                //Allow 3 pixels x2 padding either side for label readability
-                // only the index matters for a dataset scale, but we want a consistent interface between scales
-
-                var datasetWidth = Math.floor(this.getPixelForValue(0, 1) - this.getPixelForValue(0, 0)) - 6;
-
-                //Max label rotation can be set or default to 90 - also act as a loop counter
-                while (this.labelWidth > datasetWidth && this.labelRotation <= this.options.labels.maxRotation) {
-                    cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
-                    sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
-
-                    firstRotated = cosRotation * firstWidth;
-                    lastRotated = cosRotation * lastWidth;
-
-                    // We're right aligning the text now.
-                    if (firstRotated + this.options.labels.fontSize / 2 > this.yLabelWidth) {
-                        this.paddingLeft = firstRotated + this.options.labels.fontSize / 2;
-                    }
-
-                    this.paddingRight = this.options.labels.fontSize / 2;
-
-                    if (sinRotation * originalLabelWidth > maxHeight) {
-                        // go back one step
-                        this.labelRotation--;
-                        break;
-                    }
-
-                    this.labelRotation++;
-                    this.labelWidth = cosRotation * originalLabelWidth;
-
-                }
-            } else {
-                this.labelWidth = 0;
-                this.paddingRight = 0;
-                this.paddingLeft = 0;
+                this.ticks = this.data.labels;
             }
-
-            if (margins) {
-                this.paddingLeft -= margins.left;
-                this.paddingRight -= margins.right;
-
-                this.paddingLeft = Math.max(this.paddingLeft, 0);
-                this.paddingRight = Math.max(this.paddingRight, 0);
-            }
-
         },
-        // Fit this axis to the given size
-        // @param {number} maxWidth : the max width the axis can be
-        // @param {number} maxHeight: the max height the axis can be
-        // @return {object} minSize : the minimum size needed to draw the axis
-        fit: function(maxWidth, maxHeight, margins) {
-            // Set the unconstrained dimension before label rotation
-            if (this.isHorizontal()) {
-                this.width = maxWidth;
-            } else {
-                this.height = maxHeight;
-            }
-
-            this.buildLabels();
-            this.calculateLabelRotation(maxHeight, margins);
-
-            var minSize = {
-                width: 0,
-                height: 0,
-            };
-
-            // Are we showing a label for the scale?
-            if (this.options.scaleLabel.show) {
-                if (this.isHorizontal()) {
-                    minSize.height += (this.options.scaleLabel.fontSize * 1.5);
-                } else {
-                    minSize.width += (this.options.scaleLabel.fontSize * 1.5);
-                }
-            }
-
-            var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
-            var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.labels);
-
-            // Width
-            if (this.isHorizontal()) {
-                minSize.width = maxWidth;
-            } else if (this.options.display) {
-                var labelWidth = this.options.labels.show ? longestLabelWidth + 6 : 0;
-                minSize.width += labelWidth;
-                minSize.width = Math.min(minSize.width, maxWidth);
-            }
-
-            // Height
-            if (this.isHorizontal() && this.options.display) {
-                var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.labels.fontSize;
-                minSize.height += this.options.labels.show ? labelHeight : 0;
-                minSize.height = Math.min(minSize.height, maxHeight);
-            } else if (this.options.display) {
-                minSize.height = maxHeight;
-            }
+        buildRuler: function() {
+            var datasetCount = this.data.datasets.length;
 
-            this.width = minSize.width;
-            this.height = minSize.height;
-            return minSize;
+            this.ruler = {};
+            this.ruler.tickWidth = this.getPixelFromTickIndex(1) - this.getPixelFromTickIndex(0) + 3; // TODO: Why is 2 needed here to make it take the full width?
+            this.ruler.categoryWidth = this.ruler.tickWidth * this.options.categoryPercentage;
+            this.ruler.categorySpacing = (this.ruler.tickWidth - (this.ruler.tickWidth * this.options.categoryPercentage)) / 2;
+            this.ruler.allBarsWidth = ((this.ruler.tickWidth - (this.ruler.categorySpacing * 2)) / datasetCount);
+            this.ruler.barWidth = this.ruler.allBarsWidth * this.options.barPercentage;
+            this.ruler.barSpacing = this.ruler.allBarsWidth - (this.ruler.allBarsWidth * this.options.barPercentage);
         },
-        // Actualy draw the scale on the canvas
-        // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
-        draw: function(chartArea) {
-            if (this.options.display) {
-
-                var setContextLineSettings;
-
-                // Make sure we draw text in the correct color
-                this.ctx.fillStyle = this.options.labels.fontColor;
-
-                if (this.isHorizontal()) {
-                    setContextLineSettings = true;
-                    var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 10;
-                    var yTickEnd = this.options.position == "bottom" ? this.top + 10 : this.bottom;
-                    var isRotated = this.labelRotation !== 0;
-                    var skipRatio = false;
-
-                    if ((this.options.labels.fontSize + 4) * this.labels.length > (this.width - (this.paddingLeft + this.paddingRight))) {
-                        skipRatio = 1 + Math.floor(((this.options.labels.fontSize + 4) * this.labels.length) / (this.width - (this.paddingLeft + this.paddingRight)));
-                    }
-
-                    helpers.each(this.labels, function(label, index) {
-                        // Blank labels
-                        if ((skipRatio > 1 && index % skipRatio > 0) || (label === undefined || label === null)) {
-                            return;
-                        }
-                        var xLineValue = this.getPixelForValue(label, index, null, false); // xvalues for grid lines
-                        var xLabelValue = this.getPixelForValue(label, index, null, true); // x values for labels (need to consider offsetLabel option)
-
-                        if (this.options.gridLines.show) {
-                            if (index === 0) {
-                                // Draw the first index specially
-                                this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
-                                this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
-                                setContextLineSettings = true; // reset next time
-                            } else if (setContextLineSettings) {
-                                this.ctx.lineWidth = this.options.gridLines.lineWidth;
-                                this.ctx.strokeStyle = this.options.gridLines.color;
-                                setContextLineSettings = false;
-                            }
-
-                            xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
-
-                            // Draw the label area
-                            this.ctx.beginPath();
-
-                            if (this.options.gridLines.drawTicks) {
-                                this.ctx.moveTo(xLineValue, yTickStart);
-                                this.ctx.lineTo(xLineValue, yTickEnd);
-                            }
 
-                            // Draw the chart area
-                            if (this.options.gridLines.drawOnChartArea) {
-                                this.ctx.moveTo(xLineValue, chartArea.top);
-                                this.ctx.lineTo(xLineValue, chartArea.bottom);
-                            }
-
-                            // Need to stroke in the loop because we are potentially changing line widths & colours
-                            this.ctx.stroke();
-                        }
-
-                        if (this.options.labels.show) {
-                            this.ctx.save();
-                            this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.top + 8);
-                            this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
-                            this.ctx.font = this.font;
-                            this.ctx.textAlign = (isRotated) ? "right" : "center";
-                            this.ctx.textBaseline = (isRotated) ? "middle" : "top";
-                            this.ctx.fillText(label, 0, 0);
-                            this.ctx.restore();
-                        }
-                    }, this);
-
-                    if (this.options.scaleLabel.show) {
-                        // Draw the scale label
-                        this.ctx.textAlign = "center";
-                        this.ctx.textBaseline = 'middle';
-                        this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-
-                        var scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
-                        var scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
-
-                        this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
-                    }
-                } else {
-                    // Vertical
-                    if (this.options.gridLines.show) {}
-
-                    if (this.options.labels.show) {
-                        // Draw the labels
-                    }
+    });
 
-                    if (this.options.scaleLabel.show) {
-                        // Draw the scale label
-                        var scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
-                        var scaleLabelY = this.top + ((this.bottom - this.top) / 2);
-                        var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
 
-                        this.ctx.save();
-                        this.ctx.translate(scaleLabelX, scaleLabelY);
-                        this.ctx.rotate(rotation);
-                        this.ctx.textAlign = "center";
-                        this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-                        this.ctx.textBaseline = 'middle';
-                        this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0);
-                        this.ctx.restore();
-                    }
-                }
-            }
-        }
-    });
     Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
 }).call(this);
index 2446be24652acf360e68b9d1069cae2408736d4a..5d3263d88f13cbf4fd0ebae6cdac9f803aef7788 100644 (file)
                beginAtZero: false,
                override: null,
 
-               // scale label
-               scaleLabel: {
-                       fontColor: '#666',
-                       fontFamily: 'Helvetica Neue',
-                       fontSize: 12,
-                       fontStyle: 'normal',
-
-                       // actual label
-                       labelString: '',
-
-                       // display property
-                       show: false,
-               },
-
                // label settings
-               labels: {
+               ticks: {
                        show: true,
                        mirror: false,
                        padding: 10,
                }
        };
 
-       var LinearScale = Chart.Element.extend({
-               isHorizontal: function() {
-                       return this.options.position == "top" || this.options.position == "bottom";
-               },
+       var LinearScale = Chart.Scale.extend({
                generateTicks: function(width, height) {
                        // We need to decide how many ticks we are going to have. Each tick draws a grid line.
                        // There are two possibilities. The first is that the user has manually overridden the scale
@@ -91,7 +74,7 @@
                                        maxTicks = Math.min(11, Math.ceil(width / 50));
                                } else {
                                        // The factor of 2 used to scale the font size has been experimentally determined.
-                                       maxTicks = Math.min(11, Math.ceil(height / (2 * this.options.labels.fontSize)));
+                                       maxTicks = Math.min(11, Math.ceil(height / (2 * this.options.ticks.fontSize)));
                                }
 
                                // Make sure we always have at least 2 ticks 
                                this.end = this.max;
                        }
                },
-               buildLabels: function() {
-                       // We assume that this has been run after ticks have been generated. We try to figure out
-                       // a label for each tick. 
-                       this.labels = [];
-
-                       helpers.each(this.ticks, function(tick, index, ticks) {
-                               var label;
-
-                               if (this.options.labels.userCallback) {
-                                       // If the user provided a callback for label generation, use that as first priority
-                                       label = this.options.labels.userCallback(tick, index, ticks);
-                               } else if (this.options.labels.template) {
-                                       // else fall back to the template string
-                                       label = helpers.template(this.options.labels.template, {
-                                               value: tick
-                                       });
-                               }
-
-                               this.labels.push(label ? label : ""); // empty string will not render so we're good
-                       }, this);
-               },
                // Get the correct value. If the value type is object get the x or y based on whether we are horizontal or not
                getRightValue: function(rawValue) {
-                       return (typeof (rawValue) === "object" && rawValue !== null) ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
+                       return (typeof(rawValue) === "object" && rawValue !== null) ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
                },
                getPixelForValue: function(value) {
                        // This must be called after fit has been run so that 
                fit: function(maxWidth, maxHeight, margins) {
                        this.calculateRange();
                        this.generateTicks(maxWidth, maxHeight);
-                       this.buildLabels();
 
                        var minSize = {
                                width: 0,
                                minSize.height = maxHeight; // fill all the height
                        }
 
-                       // Are we showing a label for the scale?
-                       if (this.options.scaleLabel.show) {
-                               if (this.isHorizontal()) {
-                                       minSize.height += (this.options.scaleLabel.fontSize * 1.5);
-                               } else {
-                                       minSize.width += (this.options.scaleLabel.fontSize * 1.5);
-                               }
-                       }
-
                        this.paddingLeft = 0;
                        this.paddingRight = 0;
                        this.paddingTop = 0;
                        this.paddingBottom = 0;
 
 
-                       if (this.options.labels.show && this.options.display) {
-                               // Don't bother fitting the labels if we are not showing them
-                               var labelFont = helpers.fontString(this.options.labels.fontSize,
-                                       this.options.labels.fontStyle, this.options.labels.fontFamily);
+                       if (this.options.ticks.show && this.options.display) {
+                               // Don't bother fitting the ticks if we are not showing them
+                               var labelFont = helpers.fontString(this.options.ticks.fontSize,
+                                       this.options.ticks.fontStyle, this.options.ticks.fontFamily);
 
                                if (this.isHorizontal()) {
                                        // A horizontal axis is more constrained by the height.
                                        var maxLabelHeight = maxHeight - minSize.height;
-                                       var labelHeight = 1.5 * this.options.labels.fontSize;
+                                       var labelHeight = 1.5 * this.options.ticks.fontSize;
                                        minSize.height = Math.min(maxHeight, minSize.height + labelHeight);
 
-                                       var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+                                       var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
                                        this.ctx.font = labelFont;
 
-                                       var firstLabelWidth = this.ctx.measureText(this.labels[0]).width;
-                                       var lastLabelWidth = this.ctx.measureText(this.labels[this.labels.length - 1]).width;
+                                       var firstLabelWidth = this.ctx.measureText(this.ticks[0]).width;
+                                       var lastLabelWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
 
-                                       // Ensure that our labels are always inside the canvas
+                                       // Ensure that our ticks are always inside the canvas
                                        this.paddingLeft = firstLabelWidth / 2;
                                        this.paddingRight = lastLabelWidth / 2;
                                } else {
                                        // A vertical axis is more constrained by the width. Labels are the dominant factor 
                                        // here, so get that length first
                                        var maxLabelWidth = maxWidth - minSize.width;
-                                       var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.labels);
+                                       var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
 
                                        if (largestTextWidth < maxLabelWidth) {
                                                // We don't need all the room
                                                minSize.width = maxWidth;
                                        }
 
-                                       this.paddingTop = this.options.labels.fontSize / 2;
-                                       this.paddingBottom = this.options.labels.fontSize / 2;
+                                       this.paddingTop = this.options.ticks.fontSize / 2;
+                                       this.paddingBottom = this.options.ticks.fontSize / 2;
                                }
                        }
 
                                var hasZero;
 
                                // Make sure we draw text in the correct color
-                               this.ctx.fillStyle = this.options.labels.fontColor;
+                               this.ctx.fillStyle = this.options.ticks.fontColor;
 
                                if (this.isHorizontal()) {
                                        if (this.options.gridLines.show) {
                                                }, this);
                                        }
 
-                                       if (this.options.labels.show) {
-                                               // Draw the labels
+                                       if (this.options.ticks.show) {
+                                               // Draw the ticks
 
                                                var labelStartY;
 
                                                }
 
                                                this.ctx.textAlign = "center";
-                                               this.ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+                                               this.ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
 
-                                               helpers.each(this.labels, function(label, index) {
+                                               helpers.each(this.ticks, function(label, index) {
                                                        var xValue = this.getPixelForValue(this.ticks[index]);
                                                        this.ctx.fillText(label, xValue, labelStartY);
                                                }, this);
                                        }
-
-                                       if (this.options.scaleLabel.show) {
-                                               // Draw the scale label
-                                               this.ctx.textAlign = "center";
-                                               this.ctx.textBaseline = 'middle';
-                                               this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-
-                                               var scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
-                                               var scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
-
-                                               this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
-                                       }
                                } else {
                                        // Vertical
                                        if (this.options.gridLines.show) {
                                                }, this);
                                        }
 
-                                       if (this.options.labels.show) {
-                                               // Draw the labels
+                                       if (this.options.ticks.show) {
+                                               // Draw the ticks
 
                                                var labelStartX;
 
                                                if (this.options.position == "left") {
-                                                       if (this.options.labels.mirror) {
-                                                               labelStartX = this.right + this.options.labels.padding;
+                                                       if (this.options.ticks.mirror) {
+                                                               labelStartX = this.right + this.options.ticks.padding;
                                                                this.ctx.textAlign = "left";
                                                        } else {
-                                                               labelStartX = this.right - this.options.labels.padding;
+                                                               labelStartX = this.right - this.options.ticks.padding;
                                                                this.ctx.textAlign = "right";
                                                        }
                                                } else {
                                                        // right side
-                                                       if (this.options.labels.mirror) {
-                                                               labelStartX = this.left - this.options.labels.padding;
+                                                       if (this.options.ticks.mirror) {
+                                                               labelStartX = this.left - this.options.ticks.padding;
                                                                this.ctx.textAlign = "right";
                                                        } else {
-                                                               labelStartX = this.left + this.options.labels.padding;
+                                                               labelStartX = this.left + this.options.ticks.padding;
                                                                this.ctx.textAlign = "left";
                                                        }
                                                }
 
                                                this.ctx.textBaseline = "middle";
-                                               this.ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+                                               this.ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
 
-                                               helpers.each(this.labels, function(label, index) {
+                                               helpers.each(this.ticks, function(label, index) {
                                                        var yValue = this.getPixelForValue(this.ticks[index]);
                                                        this.ctx.fillText(label, labelStartX, yValue);
                                                }, this);
                                        }
-
-                                       if (this.options.scaleLabel.show) {
-                                               // Draw the scale label
-                                               var scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
-                                               var scaleLabelY = this.top + ((this.bottom - this.top) / 2);
-                                               var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
-
-                                               this.ctx.save();
-                                               this.ctx.translate(scaleLabelX, scaleLabelY);
-                                               this.ctx.rotate(rotation);
-                                               this.ctx.textAlign = "center";
-                                               this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-                                               this.ctx.textBaseline = 'middle';
-                                               this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0);
-                                               this.ctx.restore();
-                                       }
                                }
                        }
                }
index de8b2cb8f2eec72973c407ed936477a2dbd1871b..5952f15ce37fee0d6e7ad8af2b8513d493a74cc0 100644 (file)
                        displayFormat: false, // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
                },
 
-               // scale label
-               scaleLabel: {
-                       fontColor: '#666',
-                       fontFamily: 'Helvetica Neue',
-                       fontSize: 12,
-                       fontStyle: 'normal',
-
-                       // actual label
-                       labelString: '',
-
-                       // display property
-                       show: false,
-               },
-
                // scale numbers
                reverse: false,
                override: null,
 
                // label settings
-               labels: {
+               ticks: {
                        show: true,
                        mirror: false,
                        padding: 10,
                }
        };
 
-       var TimeScale = Chart.Element.extend({
-               isHorizontal: function() {
-                       return this.options.position == "top" || this.options.position == "bottom";
-               },
+       var TimeScale = Chart.Scale.extend({
                parseTime: function(label) {
                        // Date objects
                        if (typeof label.getMonth === 'function' || typeof label == 'number') {
                        } else {
                                // Determine the smallest needed unit of the time
                                var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
-                               var labelCapacity = innerWidth / this.options.labels.fontSize + 4;
+                               var labelCapacity = innerWidth / this.options.ticks.fontSize + 4;
                                var buffer = this.options.tick.round ? 0 : 2;
 
                                this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, true) + buffer);
                                this.displayFormat = this.options.tick.displayFormat;
                        }
 
-                       // For every unit in between the first and last moment, create a moment and add it to the labels tick
-                       if (this.options.labels.userCallback) {
+                       // For every unit in between the first and last moment, create a moment and add it to the ticks tick
+                       if (this.options.ticks.userCallback) {
                                for (i = 0; i <= this.tickRange; i++) {
                                        this.ticks.push(
-                                               this.options.labels.userCallback(this.firstTick.clone()
+                                               this.options.ticks.userCallback(this.firstTick.clone()
                                                        .add(i, this.tickUnit)
                                                        .format(this.options.tick.displayFormat ? this.options.tick.displayFormat : time.unit[this.tickUnit].display)
                                                )
                        return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * this.options.spacing) + barWidth / 2;
                },
 
-               calculateTickRotation: function(maxHeight, margins) {
-                       //Get the width of each grid by calculating the difference
-                       //between x offsets between 0 and 1.
-                       var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
-                       this.ctx.font = labelFont;
+               // calculateTickRotation: function(maxHeight, margins) {
+               //      //Get the width of each grid by calculating the difference
+               //      //between x offsets between 0 and 1.
+               //      var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
+               //      this.ctx.font = labelFont;
 
-                       var firstWidth = this.ctx.measureText(this.ticks[0]).width;
-                       var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
-                       var firstRotated;
-                       var lastRotated;
+               //      var firstWidth = this.ctx.measureText(this.ticks[0]).width;
+               //      var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
+               //      var firstRotated;
+               //      var lastRotated;
 
-                       this.paddingRight = lastWidth / 2 + 3;
-                       this.paddingLeft = firstWidth / 2 + 3;
+               //      this.paddingRight = lastWidth / 2 + 3;
+               //      this.paddingLeft = firstWidth / 2 + 3;
 
-                       this.labelRotation = 0;
+               //      this.labelRotation = 0;
 
-                       if (this.options.display) {
-                               var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
-                               var cosRotation;
-                               var sinRotation;
-                               var firstRotatedWidth;
+               //      if (this.options.display) {
+               //              var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
+               //              var cosRotation;
+               //              var sinRotation;
+               //              var firstRotatedWidth;
 
-                               this.labelWidth = originalLabelWidth;
+               //              this.labelWidth = originalLabelWidth;
 
-                               //Allow 3 pixels x2 padding either side for label readability
-                               // only the index matters for a dataset scale, but we want a consistent interface between scales
+               //              //Allow 3 pixels x2 padding either side for label readability
+               //              // only the index matters for a dataset scale, but we want a consistent interface between scales
 
-                               var datasetWidth = Math.floor(this.getPixelForValue(null, 1 / this.ticks.length) - this.getPixelForValue(null, 0)) - 6;
+               //              var datasetWidth = Math.floor(this.getPixelForValue(null, 1 / this.ticks.length) - this.getPixelForValue(null, 0)) - 6;
 
-                               //Max label rotation can be set or default to 90 - also act as a loop counter
-                               while (this.labelWidth > datasetWidth && this.labelRotation <= this.options.labels.maxRotation) {
-                                       cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
-                                       sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
+               //              //Max label rotation can be set or default to 90 - also act as a loop counter
+               //              while (this.labelWidth > datasetWidth && this.labelRotation <= this.options.ticks.maxRotation) {
+               //                      cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
+               //                      sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
 
-                                       firstRotated = cosRotation * firstWidth;
-                                       lastRotated = cosRotation * lastWidth;
+               //                      firstRotated = cosRotation * firstWidth;
+               //                      lastRotated = cosRotation * lastWidth;
 
-                                       // We're right aligning the text now.
-                                       if (firstRotated + this.options.labels.fontSize / 2 > this.yLabelWidth) {
-                                               this.paddingLeft = firstRotated + this.options.labels.fontSize / 2;
-                                       }
+               //                      // We're right aligning the text now.
+               //                      if (firstRotated + this.options.ticks.fontSize / 2 > this.yLabelWidth) {
+               //                              this.paddingLeft = firstRotated + this.options.ticks.fontSize / 2;
+               //                      }
 
-                                       this.paddingRight = this.options.labels.fontSize / 2;
+               //                      this.paddingRight = this.options.ticks.fontSize / 2;
 
-                                       if (sinRotation * originalLabelWidth > maxHeight) {
-                                               // go back one step
-                                               this.labelRotation--;
-                                               break;
-                                       }
+               //                      if (sinRotation * originalLabelWidth > maxHeight) {
+               //                              // go back one step
+               //                              this.labelRotation--;
+               //                              break;
+               //                      }
 
-                                       this.labelRotation++;
-                                       this.labelWidth = cosRotation * originalLabelWidth;
+               //                      this.labelRotation++;
+               //                      this.labelWidth = cosRotation * originalLabelWidth;
 
 
-                               }
-                       } else {
-                               this.labelWidth = 0;
-                               this.paddingRight = 0;
-                               this.paddingLeft = 0;
-                       }
+               //              }
+               //      } else {
+               //              this.labelWidth = 0;
+               //              this.paddingRight = 0;
+               //              this.paddingLeft = 0;
+               //      }
 
-                       if (margins) {
-                               this.paddingLeft -= margins.left;
-                               this.paddingRight -= margins.right;
+               //      if (margins) {
+               //              this.paddingLeft -= margins.left;
+               //              this.paddingRight -= margins.right;
 
-                               this.paddingLeft = Math.max(this.paddingLeft, 0);
-                               this.paddingRight = Math.max(this.paddingRight, 0);
-                       }
+               //              this.paddingLeft = Math.max(this.paddingLeft, 0);
+               //              this.paddingRight = Math.max(this.paddingRight, 0);
+               //      }
 
-               },
+               // },
                // Fit this axis to the given size
                // @param {number} maxWidth : the max width the axis can be
                // @param {number} maxHeight: the max height the axis can be
                                height: 0,
                        };
 
-                        // Are we showing a label for the scale?
-            if (this.options.scaleLabel.show) {
-                if (this.isHorizontal()) {
-                    minSize.height += (this.options.scaleLabel.fontSize * 1.5);
-                } else {
-                    minSize.width += (this.options.scaleLabel.fontSize * 1.5);
-                }
-            }
-
-                       var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+                       var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
                        var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
 
                        // Width
                        if (this.isHorizontal()) {
                                minSize.width = maxWidth;
                        } else if (this.options.display) {
-                               var labelWidth = this.options.labels.show ? longestLabelWidth + 6 : 0;
-                               minSize.width += labelWidth;
-                minSize.width = Math.min(minSize.width, maxWidth);
+                               var labelWidth = this.options.ticks.show ? longestLabelWidth + 6 : 0;
+                               minSize.width = Math.min(labelWidth, maxWidth);
                        }
 
                        // Height
                        if (this.isHorizontal() && this.options.display) {
-                               var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.labels.fontSize;
-                               minSize.height += this.options.labels.show ? labelHeight : 0;
-                minSize.height = Math.min(minSize.height, maxHeight);
+                               var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.ticks.fontSize;
+                               minSize.height = Math.min(this.options.ticks.show ? labelHeight : 0, maxHeight);
                        } else if (this.options.display) {
                                minSize.height = maxHeight;
                        }
                                var setContextLineSettings;
 
                                // Make sure we draw text in the correct color
-                               this.ctx.fillStyle = this.options.labels.fontColor;
+                               this.ctx.fillStyle = this.options.ticks.fontColor;
 
                                if (this.isHorizontal()) {
                                        setContextLineSettings = true;
                                        var isRotated = this.labelRotation !== 0;
                                        var skipRatio = false;
 
-                                       if ((this.options.labels.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
-                                               skipRatio = 1 + Math.floor(((this.options.labels.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
+                                       if ((this.options.ticks.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
+                                               skipRatio = 1 + Math.floor(((this.options.ticks.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
                                        }
 
                                        helpers.each(this.ticks, function(tick, index) {
                                                        this.ctx.stroke();
                                                }
 
-                                               if (this.options.labels.show) {
+                                               if (this.options.ticks.show) {
                                                        this.ctx.save();
                                                        this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.top + 8);
                                                        this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
                                                        this.ctx.restore();
                                                }
                                        }, this);
-
-                                       if (this.options.scaleLabel.show) {
-                        // Draw the scale label
-                        this.ctx.textAlign = "center";
-                        this.ctx.textBaseline = 'middle';
-                        this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-
-                        var scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
-                        var scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
-
-                        this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
-                    }
                                } else {
                                        // Vertical
                                        if (this.options.gridLines.show) {}
 
-                                       if (this.options.labels.show) {
-                                               // Draw the labels
+                                       if (this.options.ticks.show) {
+                                               // Draw the ticks
                                        }
-
-                                       if (this.options.scaleLabel.show) {
-                        // Draw the scale label
-                        var scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
-                        var scaleLabelY = this.top + ((this.bottom - this.top) / 2);
-                        var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
-
-                        this.ctx.save();
-                        this.ctx.translate(scaleLabelX, scaleLabelY);
-                        this.ctx.rotate(rotation);
-                        this.ctx.textAlign = "center";
-                        this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-                        this.ctx.textBaseline = 'middle';
-                        this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0);
-                        this.ctx.restore();
-                    }
                                }
                        }
                }