]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Split out data limits step of scales into it's own step. Wire up callbacks from the... 1794/head
authorEvert Timberg <evert.timberg@gmail.com>
Thu, 17 Dec 2015 14:21:04 +0000 (09:21 -0500)
committerEvert Timberg <evert.timberg@gmail.com>
Thu, 17 Dec 2015 14:21:04 +0000 (09:21 -0500)
docs/01-Scales.md
docs/07-Advanced.md
src/core/core.helpers.js
src/core/core.scale.js
src/scales/scale.linear.js
src/scales/scale.logarithmic.js
src/scales/scale.radialLinear.js
src/scales/scale.time.js
test/scale.linear.tests.js
test/scale.logarithmic.tests.js

index 5a4fcc63e5e19ea04ed8e2ef03349d4dbfe43f2d..06f406a433323a760d3a5ff265497ed5a64fba48 100644 (file)
@@ -16,6 +16,20 @@ Name | Type | Default | Description
 --- |:---:| --- | ---
 type | String | Chart specific. | Type of scale being employed. Custom scales can be created and registered with a string key. Options: ["category"](#scales-category-scale), ["linear"](#scales-linear-scale), ["logarithmic"](#scales-logarithmic-scale), ["time"](#scales-time-scale), ["radialLinear"](#scales-radial-linear-scale)
 display | Boolean | true | If true, show the scale including gridlines, ticks, and labels. Overrides *gridLines.display*, *scaleLabel.display*, and *ticks.display*.
+beforeUpdate | Function | undefined | Callback called before the update process starts. Passed a single argument, the scale instance.
+beforeSetDimensions | Function | undefined | Callback that runs before dimensions are set. Passed a single argument, the scale instance.
+afterSetDimensions | Function | undefined | Callback that runs after dimensions are set. Passed a single argument, the scale instance.
+beforeDataLimits | Function | undefined | Callback that runs before data limits are determined. Passed a single argument, the scale instance.
+afterDataLimits | Function | undefined | Callback that runs after data limits are determined. Passed a single argument, the scale instance.
+beforeBuildTicks | Function | undefined | Callback that runs before ticks are created. Passed a single argument, the scale instance.
+afterBuildTicks | Function | undefined | Callback that runs after ticks are created. Useful for filtering ticks. Passed a single argument, the scale instance.
+beforeTickToLabelConversion | Function | undefined | Callback that runs before ticks are converted into strings. Passed a single argument, the scale instance.
+afterTickToLabelConversion | Function | undefined | Callback that runs after ticks are converted into strings. Passed a single argument, the scale instance.
+beforeCalculateTickRotation | Function | undefined | Callback that runs before tick rotation is determined. Passed a single argument, the scale instance.
+afterCalculateTickRotation | Function | undefined | Callback that runs after tick rotation is determined. Passed a single argument, the scale instance.
+beforeFit | Function | undefined | Callback that runs before the scale fits to the canvas. Passed a single argument, the scale instance.
+afterFit | Function | undefined | Callback that runs after the scale fits to the canvas. Passed a single argument, the scale instance.
+afterUpdate | Function | undefined | Callback that runs at the end of the update process. Passed a single argument, the scale instance.
 **gridLines** | Array | - | Options for the grid lines that run perpendicular to the axis.
 *gridLines*.display | Boolean | true |
 *gridLines*.color | Color | "rgba(0, 0, 0, 0.1)" | Color of the grid lines.
index d040d4584ac20a866b415ff2112cec08ab19c7c0..f2ab317ad89ee752aace392e293b85b41d9d96a5 100644 (file)
@@ -229,6 +229,9 @@ To work with Chart.js, custom scale types must implement the following interface
 
 ```javascript
 {
+       // Determines the data limits. Should set this.min and this.max to be the data max/min
+       determineDataLimits: function() {},
+
        // Generate tick marks. this.chart is the chart instance. The data object can be accessed as this.chart.data
        // buildTicks() should create a ticks array on the scale instance, if you intend to use any of the implementations from the base class
        buildTicks: function() {},
index 177397a2065b9a23e32c19dcabc3f80f943b2a3f..19c242b8431659b4eea560e17051ca0064bd2bfb 100644 (file)
                isDatasetVisible = helpers.isDatasetVisible = function(dataset) {
                        return !dataset.hidden;
                };
+
+       helpers.callCallback = function(fn, args, _tArg) {
+               if (fn && typeof fn.call === 'function') {
+                       fn.apply(_tArg, args);
+               }
+       }
 }).call(this);
index 6f15033e69ae6a4b00fd1cbd4d51cfa14df70daa..f8b634230cb473af7e2380e34329f190b762c32b 100644 (file)
@@ -58,7 +58,9 @@
                // Any function defined here is inherited by all scale types.
                // Any function can be extended by the scale type
 
-               beforeUpdate: helpers.noop,
+               beforeUpdate: function() {
+                       helpers.callCallback(this.options.beforeUpdate, [this]);
+               },
                update: function(maxWidth, maxHeight, margins) {
 
                        // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
                        this.beforeSetDimensions();
                        this.setDimensions();
                        this.afterSetDimensions();
+
+                       // Data min/max
+                       this.beforeDataLimits();
+                       this.determineDataLimits();
+                       this.afterDataLimits();
+
                        // Ticks
                        this.beforeBuildTicks();
                        this.buildTicks();
                        return this.minSize;
 
                },
-               afterUpdate: helpers.noop,
+               afterUpdate: function() {
+                       helpers.callCallback(this.options.afterUpdate, [this]);
+               },
 
                //
 
-               beforeSetDimensions: helpers.noop,
+               beforeSetDimensions: function() {
+                       helpers.callCallback(this.options.beforeSetDimensions, [this]);
+               },
                setDimensions: function() {
                        // Set the unconstrained dimension before label rotation
                        if (this.isHorizontal()) {
                        this.paddingRight = 0;
                        this.paddingBottom = 0;
                },
-               afterSetDimensions: helpers.noop,
+               afterSetDimensions: function() {
+                       helpers.callCallback(this.options.afterSetDimensions, [this]);
+               },
 
-               //
+               // Data limits
+               beforeDataLimits: function() {
+                       helpers.callCallback(this.options.beforeDataLimits, [this]);
+               },
+               determineDataLimits: helpers.noop,
+               afterDataLimits: function() {
+                       helpers.callCallback(this.options.afterDataLimits, [this]);
+               },
 
-               beforeBuildTicks: helpers.noop,
+               //
+               beforeBuildTicks: function() {
+                       helpers.callCallback(this.options.beforeBuildTicks, [this]);
+               },
                buildTicks: helpers.noop,
-               afterBuildTicks: helpers.noop,
+               afterBuildTicks: function() {
+                       helpers.callCallback(this.options.afterBuildTicks, [this]);
+               },
 
-               beforeTickToLabelConversion: helpers.noop,
+               beforeTickToLabelConversion: function() {
+                       helpers.callCallback(this.options.beforeTickToLabelConversion, [this]);
+               },
                convertTicksToLabels: function() {
                        // Convert ticks to strings
                        this.ticks = this.ticks.map(function(numericalTick, index, ticks) {
                                },
                                this);
                },
-               afterTickToLabelConversion: helpers.noop,
+               afterTickToLabelConversion: function() {
+                       helpers.callCallback(this.options.afterTickToLabelConversion, [this]);
+               },
 
                //
 
-               beforeCalculateTickRotation: helpers.noop,
+               beforeCalculateTickRotation: function() {
+                       helpers.callCallback(this.options.beforeCalculateTickRotation, [this]);
+               },
                calculateTickRotation: function() {
                        //Get the width of each grid by calculating the difference
                        //between x offsets between 0 and 1.
                                this.paddingRight = Math.max(this.paddingRight, 0);
                        }
                },
-               afterCalculateTickRotation: helpers.noop,
+               afterCalculateTickRotation: function() {
+                       helpers.callCallback(this.options.afterCalculateTickRotation, [this]);
+               },
 
                //
 
-               beforeFit: helpers.noop,
+               beforeFit: function() {
+                       helpers.callCallback(this.options.beforeFit, [this]);
+               },
                fit: function() {
 
                        this.minSize = {
                        this.height = this.minSize.height;
 
                },
-               afterFit: helpers.noop,
+               afterFit: function() {
+                       helpers.callCallback(this.options.afterFit, [this]);
+               },
 
                // Shared Methods
                isHorizontal: function() {
index e21d10225571618ce545cc1a90a247b428316924..d453b2c7e6557ada8781f290f528cf5c2edbacb5 100644 (file)
@@ -36,8 +36,7 @@
        };
 
        var LinearScale = Chart.Scale.extend({
-               buildTicks: function() {
-
+               determineDataLimits: function() {
                        // First Calculate the range
                        this.min = null;
                        this.max = null;
                                }, this);
                        }
 
-                       // Then calulate the ticks
-                       this.ticks = [];
-
-                       // Figure out what the max number of ticks we can support it is based on the size of
-                       // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
-                       // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 
-                       // the graph
-
-                       var maxTicks;
-
-                       if (this.isHorizontal()) {
-                               maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
-                                                   Math.ceil(this.width / 50));
-                       } else {
-                               // The factor of 2 used to scale the font size has been experimentally determined.
-                               maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
-                                                   Math.ceil(this.height / (2 * this.options.ticks.fontSize)));
-                       }
-
-                       // Make sure we always have at least 2 ticks 
-                       maxTicks = Math.max(2, maxTicks);
-
-                       // To get a "nice" value for the tick spacing, we will use the appropriately named 
-                       // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
-                       // for details.
-
                        // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
                        // do nothing since that would make the chart weird. If the user really wants a weird chart
                        // axis, they can manually override it
                                this.min--;
                                this.max++;
                        }
+               },
+               buildTicks: function() {
+
+                       // Then calulate the ticks
+                       this.ticks = [];
+
+                       // Figure out what the max number of ticks we can support it is based on the size of
+                       // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
+                       // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 
+                       // the graph
+
+                       var maxTicks;
+
+                       if (this.isHorizontal()) {
+                               maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
+                                                   Math.ceil(this.width / 50));
+                       } else {
+                               // The factor of 2 used to scale the font size has been experimentally determined.
+                               maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
+                                                   Math.ceil(this.height / (2 * this.options.ticks.fontSize)));
+                       }
+
+                       // Make sure we always have at least 2 ticks 
+                       maxTicks = Math.max(2, maxTicks);
+
+                       // To get a "nice" value for the tick spacing, we will use the appropriately named 
+                       // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
+                       // for details.
 
                        var niceRange = helpers.niceNum(this.max - this.min, false);
                        var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
index 0de930ae6a57d8394bce1b839df5942c387c5890..10b406c18fdcdcf1fd84778652f51ee307a54cfb 100644 (file)
        };
 
        var LogarithmicScale = Chart.Scale.extend({
-               buildTicks: function() {
-
-                       // Calculate Range (we may break this out into it's own lifecycle function)
-
+               determineDataLimits: function() {
+                       // Calculate Range
                        this.min = null;
                        this.max = null;
 
                                        this.max = 10;
                                }
                        }
-
-
+               },
+               buildTicks: function() {
                        // Reset the ticks array. Later on, we will draw a grid line at these positions
                        // The array simply contains the numerical value of the spots where ticks will be
                        this.tickValues = [];
                                tickVal = significand * Math.pow(10, exp);
                        }
 
-                       /*var minExponent = Math.floor(helpers.log10(this.min));
-                       var maxExponent = Math.ceil(helpers.log10(this.max));
-
-                       for (var exponent = minExponent; exponent < maxExponent; ++exponent) {
-                               for (var i = 1; i < 10; ++i) {
-                                       if (this.options.ticks.min) {
-
-                                       } else {
-                                               this.tickValues.push(i * Math.pow(10, exponent));
-                                       }
-                               }
-                       }*/
-
                        var lastTick = this.options.ticks.max !== undefined ? this.options.ticks.max : tickVal;
                        this.tickValues.push(lastTick);
 
index d9f327cf551ce3f3527822f156d4f369324699f9..77eb9a790351a404ada4821233a6e6cc22a543c0 100644 (file)
@@ -63,7 +63,7 @@
                        var minSize = helpers.min([this.height, this.width]);
                        this.drawingArea = (this.options.display) ? (minSize / 2) - (this.options.ticks.fontSize / 2 + this.options.ticks.backdropPaddingY) : (minSize / 2);
                },
-               buildTicks: function() {
+               determineDataLimits: function() {
                        this.min = null;
                        this.max = null;
 
                                this.max++;
                        }
 
-                       this.ticks = [];
-
-                       // Figure out what the max number of ticks we can support it is based on the size of
-                       // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
-                       // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 
-                       // the graph
-                       var maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
-                                               Math.ceil(this.drawingArea / (1.5 * this.options.ticks.fontSize)));
-                       maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks 
-
-                       // To get a "nice" value for the tick spacing, we will use the appropriately named 
-                       // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
-                       // for details.
-
                        // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
                        // do nothing since that would make the chart weird. If the user really wants a weird chart
                        // axis, they can manually override it
                                        this.min = 0;
                                }
                        }
+               },
+               buildTicks: function() {
+                       
+
+                       this.ticks = [];
+
+                       // Figure out what the max number of ticks we can support it is based on the size of
+                       // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
+                       // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 
+                       // the graph
+                       var maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
+                                               Math.ceil(this.drawingArea / (1.5 * this.options.ticks.fontSize)));
+                       maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks 
+
+                       // To get a "nice" value for the tick spacing, we will use the appropriately named 
+                       // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
+                       // for details.
 
                        var niceRange = helpers.niceNum(this.max - this.min, false);
                        var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
index 8c957beb0551c1d40cd69ffc0479ae76067dd8a8..a1ce6a35edc5c8c249f3971c1cd735295dc1bbeb 100644 (file)
@@ -71,8 +71,9 @@
                getLabelMoment: function(datasetIndex, index) {
                        return this.labelMoments[datasetIndex][index];
                },
-
-               buildLabelMoments: function() {
+               determineDataLimits: function() {
+                       this.labelMoments = [];
+                       
                        // Only parse these once. If the dataset does not have data as x,y pairs, we will use
                        // these 
                        var scaleLabelMoments = [];
                        this.firstTick = this.firstTick.clone();
                        this.lastTick = this.lastTick.clone();
                },
-
                buildTicks: function(index) {
 
                        this.ticks = [];
-                       this.labelMoments = [];
                        this.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step 
 
-                       this.buildLabelMoments();
-
                        // Set unit override if applicable
                        if (this.options.time.unit) {
                                this.tickUnit = this.options.time.unit || 'day';
index ef1b2cbb95df19b46962e170c7b6aaebf94ecbf5..65b7444da98374787d9db44c260f8fa54bb2c009 100644 (file)
@@ -82,7 +82,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
-               scale.buildTicks();
+               scale.determineDataLimits();
                expect(scale.min).toBe(-100);
                expect(scale.max).toBe(150);
        });
@@ -121,7 +121,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
-               scale.buildTicks();
+               scale.determineDataLimits();
                expect(scale.min).toBe(-100);
                expect(scale.max).toBe(150);
        });
@@ -161,6 +161,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.min).toBe(-100);
                expect(scale.max).toBe(80);
@@ -204,6 +205,7 @@ describe('Linear Scale', function() {
                verticalScale.width = 50;
                verticalScale.height = 400;
 
+               verticalScale.determineDataLimits();
                verticalScale.buildTicks();
                expect(verticalScale.min).toBe(0);
                expect(verticalScale.max).toBe(100);
@@ -223,6 +225,7 @@ describe('Linear Scale', function() {
                horizontalScale.width = 400;
                horizontalScale.height = 50;
 
+               horizontalScale.determineDataLimits();
                horizontalScale.buildTicks();
                expect(horizontalScale.min).toBe(-20);
                expect(horizontalScale.max).toBe(100);
@@ -267,6 +270,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.min).toBe(-150);
                expect(scale.max).toBe(200);
@@ -309,6 +313,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.min).toBe(-150);
                expect(scale.max).toBe(200);
@@ -336,6 +341,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.min).toBe(-1);
                expect(scale.max).toBe(1);
@@ -369,6 +375,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.min).toBe(-10);
                expect(scale.max).toBe(10);
@@ -402,6 +409,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.min).toBe(-1010);
                expect(scale.max).toBe(1010);
@@ -436,18 +444,22 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.ticks).toEqual([50, 45, 40, 35, 30, 25, 20]);
 
                config.ticks.beginAtZero = true;
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.ticks).toEqual([50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0]);
 
                mockData.datasets[0].data = [-20, -30, -40, -50];
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.ticks).toEqual([0, -5, -10, -15, -20, -25, -30, -35, -40, -45, -50]);
 
                config.ticks.beginAtZero = false;
+               scale.determineDataLimits();
                scale.buildTicks();
                expect(scale.ticks).toEqual([-20, -25, -30, -35, -40, -45, -50]);
        });
@@ -477,6 +489,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
 
                // Counts down because the lines are drawn top to bottom
@@ -511,6 +524,7 @@ describe('Linear Scale', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
 
                // Reverse mode makes this count up
index 1f05388c682f8d60624655905465a4d8b56273dd..2a94008521d63b1f1555a973c37e3f3e8fd662d5 100644 (file)
@@ -389,6 +389,7 @@ describe('Logarithmic Scale tests', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
 
                // Counts down because the lines are drawn top to bottom
@@ -424,6 +425,7 @@ describe('Logarithmic Scale tests', function() {
                scale.width = 50;
                scale.height = 400;
 
+               scale.determineDataLimits();
                scale.buildTicks();
 
                // Counts down because the lines are drawn top to bottom