]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Cleanup scales export for better import strategy (#5953)
authorSimon Brunel <simonbrunel@users.noreply.github.com>
Sat, 5 Jan 2019 13:37:54 +0000 (14:37 +0100)
committerGitHub <noreply@github.com>
Sat, 5 Jan 2019 13:37:54 +0000 (14:37 +0100)
Scales now export their class and associated defaults (`_defaults`), the registration being done globally in `src/chart.js`.

src/chart.js
src/controllers/index.js
src/scales/index.js [new file with mode: 0644]
src/scales/scale.category.js
src/scales/scale.linear.js
src/scales/scale.linearbase.js
src/scales/scale.logarithmic.js
src/scales/scale.radialLinear.js
src/scales/scale.time.js
test/specs/global.deprecations.tests.js

index acb57862c5083d9f1db31b9c60ca213e50b11b76..b60129f2cf5c90c855e06d18005603af37d24687 100644 (file)
@@ -26,12 +26,11 @@ Chart.Tooltip = require('./core/core.tooltip');
 
 require('./core/core.controller')(Chart);
 
-require('./scales/scale.linearbase')(Chart);
-require('./scales/scale.category')(Chart);
-require('./scales/scale.linear')(Chart);
-require('./scales/scale.logarithmic')(Chart);
-require('./scales/scale.radialLinear')(Chart);
-require('./scales/scale.time')(Chart);
+// Register built-in scales
+var scales = require('./scales');
+Chart.helpers.each(scales, function(scale, type) {
+       Chart.scaleService.registerScaleType(type, scale, scale._defaults);
+});
 
 // Loading built-in plugins
 var plugins = require('./plugins');
@@ -105,6 +104,15 @@ Chart.canvasHelpers = Chart.helpers.canvas;
  */
 Chart.layoutService = Chart.layouts;
 
+/**
+ * Provided for backward compatibility, not available anymore.
+ * @namespace Chart.LinearScaleBase
+ * @deprecated since version 2.8
+ * @todo remove at version 3
+ * @private
+ */
+Chart.LinearScaleBase = require('./scales/scale.linearbase');
+
 /**
  * Provided for backward compatibility, instead we should create a new Chart
  * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`).
index d2fa6c96e3b6464d661047e7ec48f8290dd295b4..b80faaab6905824d690808c0588facbdfafe889c 100644 (file)
@@ -1,18 +1,27 @@
 'use strict';
 
+var bar = require('./controller.bar');
+var bubble = require('./controller.bubble');
+var doughnut = require('./controller.doughnut');
+var horizontalBar = require('./controller.horizontalBar');
+var line = require('./controller.line');
+var polarArea = require('./controller.polarArea');
+var pie = require('./controller.pie');
+var radar = require('./controller.radar');
+var scatter = require('./controller.scatter');
+
 // NOTE export a map in which the key represents the controller type, not
 // the class, and so must be CamelCase in order to be correctly retrieved
 // by the controller in core.controller.js (`controllers[meta.type]`).
 
-/* eslint-disable global-require */
 module.exports = {
-       bar: require('./controller.bar'),
-       bubble: require('./controller.bubble'),
-       doughnut: require('./controller.doughnut'),
-       horizontalBar: require('./controller.horizontalBar'),
-       line: require('./controller.line'),
-       polarArea: require('./controller.polarArea'),
-       pie: require('./controller.pie'),
-       radar: require('./controller.radar'),
-       scatter: require('./controller.scatter')
+       bar: bar,
+       bubble: bubble,
+       doughnut: doughnut,
+       horizontalBar: horizontalBar,
+       line: line,
+       polarArea: polarArea,
+       pie: pie,
+       radar: radar,
+       scatter: scatter
 };
diff --git a/src/scales/index.js b/src/scales/index.js
new file mode 100644 (file)
index 0000000..6de575b
--- /dev/null
@@ -0,0 +1,15 @@
+'use strict';
+
+var category = require('./scale.category');
+var linear = require('./scale.linear');
+var logarithmic = require('./scale.logarithmic');
+var radialLinear = require('./scale.radialLinear');
+var time = require('./scale.time');
+
+module.exports = {
+       category: category,
+       linear: linear,
+       logarithmic: logarithmic,
+       radialLinear: radialLinear,
+       time: time
+};
index 33a91f3a2c6da3d132c0e4ee2a3b2609efd685fb..3a4d20645b317ca211d314a167a17023a1c1bac9 100644 (file)
 'use strict';
 
 var Scale = require('../core/core.scale');
-var scaleService = require('../core/core.scaleService');
-
-module.exports = function() {
-
-       // Default config for a category scale
-       var defaultConfig = {
-               position: 'bottom'
-       };
-
-       var DatasetScale = Scale.extend({
-               /**
-               * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
-               * else fall back to data.labels
-               * @private
-               */
-               getLabels: function() {
-                       var data = this.chart.data;
-                       return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
-               },
-
-               determineDataLimits: function() {
-                       var me = this;
-                       var labels = me.getLabels();
-                       me.minIndex = 0;
-                       me.maxIndex = labels.length - 1;
-                       var findIndex;
-
-                       if (me.options.ticks.min !== undefined) {
-                               // user specified min value
-                               findIndex = labels.indexOf(me.options.ticks.min);
-                               me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
-                       }
 
-                       if (me.options.ticks.max !== undefined) {
-                               // user specified max value
-                               findIndex = labels.indexOf(me.options.ticks.max);
-                               me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
-                       }
+var defaultConfig = {
+       position: 'bottom'
+};
 
-                       me.min = labels[me.minIndex];
-                       me.max = labels[me.maxIndex];
-               },
+module.exports = Scale.extend({
+       /**
+       * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
+       * else fall back to data.labels
+       * @private
+       */
+       getLabels: function() {
+               var data = this.chart.data;
+               return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
+       },
+
+       determineDataLimits: function() {
+               var me = this;
+               var labels = me.getLabels();
+               me.minIndex = 0;
+               me.maxIndex = labels.length - 1;
+               var findIndex;
+
+               if (me.options.ticks.min !== undefined) {
+                       // user specified min value
+                       findIndex = labels.indexOf(me.options.ticks.min);
+                       me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
+               }
 
-               buildTicks: function() {
-                       var me = this;
-                       var labels = me.getLabels();
-                       // If we are viewing some subset of labels, slice the original array
-                       me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
-               },
+               if (me.options.ticks.max !== undefined) {
+                       // user specified max value
+                       findIndex = labels.indexOf(me.options.ticks.max);
+                       me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
+               }
 
-               getLabelForIndex: function(index, datasetIndex) {
-                       var me = this;
-                       var data = me.chart.data;
-                       var isHorizontal = me.isHorizontal();
+               me.min = labels[me.minIndex];
+               me.max = labels[me.maxIndex];
+       },
 
-                       if (data.yLabels && !isHorizontal) {
-                               return me.getRightValue(data.datasets[datasetIndex].data[index]);
-                       }
-                       return me.ticks[index - me.minIndex];
-               },
-
-               // Used to get data value locations.  Value can either be an index or a numerical value
-               getPixelForValue: function(value, index) {
-                       var me = this;
-                       var offset = me.options.offset;
-                       // 1 is added because we need the length but we have the indexes
-                       var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);
-
-                       // If value is a data object, then index is the index in the data array,
-                       // not the index of the scale. We need to change that.
-                       var valueCategory;
-                       if (value !== undefined && value !== null) {
-                               valueCategory = me.isHorizontal() ? value.x : value.y;
-                       }
-                       if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
-                               var labels = me.getLabels();
-                               value = valueCategory || value;
-                               var idx = labels.indexOf(value);
-                               index = idx !== -1 ? idx : index;
-                       }
+       buildTicks: function() {
+               var me = this;
+               var labels = me.getLabels();
+               // If we are viewing some subset of labels, slice the original array
+               me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
+       },
 
-                       if (me.isHorizontal()) {
-                               var valueWidth = me.width / offsetAmt;
-                               var widthOffset = (valueWidth * (index - me.minIndex));
+       getLabelForIndex: function(index, datasetIndex) {
+               var me = this;
+               var data = me.chart.data;
+               var isHorizontal = me.isHorizontal();
 
-                               if (offset) {
-                                       widthOffset += (valueWidth / 2);
-                               }
+               if (data.yLabels && !isHorizontal) {
+                       return me.getRightValue(data.datasets[datasetIndex].data[index]);
+               }
+               return me.ticks[index - me.minIndex];
+       },
+
+       // Used to get data value locations.  Value can either be an index or a numerical value
+       getPixelForValue: function(value, index) {
+               var me = this;
+               var offset = me.options.offset;
+               // 1 is added because we need the length but we have the indexes
+               var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);
+
+               // If value is a data object, then index is the index in the data array,
+               // not the index of the scale. We need to change that.
+               var valueCategory;
+               if (value !== undefined && value !== null) {
+                       valueCategory = me.isHorizontal() ? value.x : value.y;
+               }
+               if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
+                       var labels = me.getLabels();
+                       value = valueCategory || value;
+                       var idx = labels.indexOf(value);
+                       index = idx !== -1 ? idx : index;
+               }
 
-                               return me.left + widthOffset;
-                       }
-                       var valueHeight = me.height / offsetAmt;
-                       var heightOffset = (valueHeight * (index - me.minIndex));
+               if (me.isHorizontal()) {
+                       var valueWidth = me.width / offsetAmt;
+                       var widthOffset = (valueWidth * (index - me.minIndex));
 
                        if (offset) {
-                               heightOffset += (valueHeight / 2);
+                               widthOffset += (valueWidth / 2);
                        }
 
-                       return me.top + heightOffset;
-               },
-               getPixelForTick: function(index) {
-                       return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
-               },
-               getValueForPixel: function(pixel) {
-                       var me = this;
-                       var offset = me.options.offset;
-                       var value;
-                       var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
-                       var horz = me.isHorizontal();
-                       var valueDimension = (horz ? me.width : me.height) / offsetAmt;
-
-                       pixel -= horz ? me.left : me.top;
+                       return me.left + widthOffset;
+               }
+               var valueHeight = me.height / offsetAmt;
+               var heightOffset = (valueHeight * (index - me.minIndex));
 
-                       if (offset) {
-                               pixel -= (valueDimension / 2);
-                       }
+               if (offset) {
+                       heightOffset += (valueHeight / 2);
+               }
 
-                       if (pixel <= 0) {
-                               value = 0;
-                       } else {
-                               value = Math.round(pixel / valueDimension);
-                       }
+               return me.top + heightOffset;
+       },
+
+       getPixelForTick: function(index) {
+               return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
+       },
 
-                       return value + me.minIndex;
-               },
-               getBasePixel: function() {
-                       return this.bottom;
+       getValueForPixel: function(pixel) {
+               var me = this;
+               var offset = me.options.offset;
+               var value;
+               var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
+               var horz = me.isHorizontal();
+               var valueDimension = (horz ? me.width : me.height) / offsetAmt;
+
+               pixel -= horz ? me.left : me.top;
+
+               if (offset) {
+                       pixel -= (valueDimension / 2);
                }
-       });
 
-       scaleService.registerScaleType('category', DatasetScale, defaultConfig);
-};
+               if (pixel <= 0) {
+                       value = 0;
+               } else {
+                       value = Math.round(pixel / valueDimension);
+               }
+
+               return value + me.minIndex;
+       },
+
+       getBasePixel: function() {
+               return this.bottom;
+       }
+});
+
+// INTERNAL: static default options, registered in src/chart.js
+module.exports._defaults = defaultConfig;
index 60a291e626f6daee8689ab91ca32683098458e54..41a4e4168a36ee1dcdf839ca4ae050d5a29c8a01 100644 (file)
 'use strict';
 
 var helpers = require('../helpers/index');
-var scaleService = require('../core/core.scaleService');
+var LinearScaleBase = require('./scale.linearbase');
 var Ticks = require('../core/core.ticks');
 
-module.exports = function(Chart) {
+var defaultConfig = {
+       position: 'left',
+       ticks: {
+               callback: Ticks.formatters.linear
+       }
+};
 
-       var defaultConfig = {
-               position: 'left',
-               ticks: {
-                       callback: Ticks.formatters.linear
+module.exports = LinearScaleBase.extend({
+       determineDataLimits: function() {
+               var me = this;
+               var opts = me.options;
+               var chart = me.chart;
+               var data = chart.data;
+               var datasets = data.datasets;
+               var isHorizontal = me.isHorizontal();
+               var DEFAULT_MIN = 0;
+               var DEFAULT_MAX = 1;
+
+               function IDMatches(meta) {
+                       return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
                }
-       };
-
-       var LinearScale = Chart.LinearScaleBase.extend({
-
-               determineDataLimits: function() {
-                       var me = this;
-                       var opts = me.options;
-                       var chart = me.chart;
-                       var data = chart.data;
-                       var datasets = data.datasets;
-                       var isHorizontal = me.isHorizontal();
-                       var DEFAULT_MIN = 0;
-                       var DEFAULT_MAX = 1;
-
-                       function IDMatches(meta) {
-                               return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
-                       }
-
-                       // First Calculate the range
-                       me.min = null;
-                       me.max = null;
-
-                       var hasStacks = opts.stacked;
-                       if (hasStacks === undefined) {
-                               helpers.each(datasets, function(dataset, datasetIndex) {
-                                       if (hasStacks) {
-                                               return;
-                                       }
-
-                                       var meta = chart.getDatasetMeta(datasetIndex);
-                                       if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
-                                               meta.stack !== undefined) {
-                                               hasStacks = true;
-                                       }
-                               });
-                       }
-
-                       if (opts.stacked || hasStacks) {
-                               var valuesPerStack = {};
-
-                               helpers.each(datasets, function(dataset, datasetIndex) {
-                                       var meta = chart.getDatasetMeta(datasetIndex);
-                                       var key = [
-                                               meta.type,
-                                               // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
-                                               ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
-                                               meta.stack
-                                       ].join('.');
-
-                                       if (valuesPerStack[key] === undefined) {
-                                               valuesPerStack[key] = {
-                                                       positiveValues: [],
-                                                       negativeValues: []
-                                               };
-                                       }
-
-                                       // Store these per type
-                                       var positiveValues = valuesPerStack[key].positiveValues;
-                                       var negativeValues = valuesPerStack[key].negativeValues;
-
-                                       if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
-                                               helpers.each(dataset.data, function(rawValue, index) {
-                                                       var value = +me.getRightValue(rawValue);
-                                                       if (isNaN(value) || meta.data[index].hidden) {
-                                                               return;
-                                                       }
-
-                                                       positiveValues[index] = positiveValues[index] || 0;
-                                                       negativeValues[index] = negativeValues[index] || 0;
-
-                                                       if (opts.relativePoints) {
-                                                               positiveValues[index] = 100;
-                                                       } else if (value < 0) {
-                                                               negativeValues[index] += value;
-                                                       } else {
-                                                               positiveValues[index] += value;
-                                                       }
-                                               });
-                                       }
-                               });
-
-                               helpers.each(valuesPerStack, function(valuesForType) {
-                                       var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
-                                       var minVal = helpers.min(values);
-                                       var maxVal = helpers.max(values);
-                                       me.min = me.min === null ? minVal : Math.min(me.min, minVal);
-                                       me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
-                               });
-
-                       } else {
-                               helpers.each(datasets, function(dataset, datasetIndex) {
-                                       var meta = chart.getDatasetMeta(datasetIndex);
-                                       if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
-                                               helpers.each(dataset.data, function(rawValue, index) {
-                                                       var value = +me.getRightValue(rawValue);
-                                                       if (isNaN(value) || meta.data[index].hidden) {
-                                                               return;
-                                                       }
-
-                                                       if (me.min === null) {
-                                                               me.min = value;
-                                                       } else if (value < me.min) {
-                                                               me.min = value;
-                                                       }
-
-                                                       if (me.max === null) {
-                                                               me.max = value;
-                                                       } else if (value > me.max) {
-                                                               me.max = value;
-                                                       }
-                                               });
-                                       }
-                               });
-                       }
-
-                       me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
-                       me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
-
-                       // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
-                       this.handleTickRangeOptions();
-               },
-               // Returns the maximum number of ticks based on the scale dimension
-               _computeTickLimit: function() {
-                       var me = this;
-                       var tickFont;
-
-                       if (me.isHorizontal()) {
-                               return Math.ceil(me.width / 40);
-                       }
-                       tickFont = helpers.options._parseFont(me.options.ticks);
-                       return Math.ceil(me.height / tickFont.lineHeight);
-               },
-               // Called after the ticks are built. We need
-               handleDirectionalChanges: function() {
-                       if (!this.isHorizontal()) {
-                               // We are in a vertical orientation. The top value is the highest. So reverse the array
-                               this.ticks.reverse();
-                       }
-               },
-               getLabelForIndex: function(index, datasetIndex) {
-                       return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
-               },
-               // Utils
-               getPixelForValue: function(value) {
-                       // This must be called after fit has been run so that
-                       // this.left, this.top, this.right, and this.bottom have been defined
-                       var me = this;
-                       var start = me.start;
-
-                       var rightValue = +me.getRightValue(value);
-                       var pixel;
-                       var range = me.end - start;
-
-                       if (me.isHorizontal()) {
-                               pixel = me.left + (me.width / range * (rightValue - start));
-                       } else {
-                               pixel = me.bottom - (me.height / range * (rightValue - start));
-                       }
-                       return pixel;
-               },
-               getValueForPixel: function(pixel) {
-                       var me = this;
-                       var isHorizontal = me.isHorizontal();
-                       var innerDimension = isHorizontal ? me.width : me.height;
-                       var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
-                       return me.start + ((me.end - me.start) * offset);
-               },
-               getPixelForTick: function(index) {
-                       return this.getPixelForValue(this.ticksAsNumbers[index]);
+
+               // First Calculate the range
+               me.min = null;
+               me.max = null;
+
+               var hasStacks = opts.stacked;
+               if (hasStacks === undefined) {
+                       helpers.each(datasets, function(dataset, datasetIndex) {
+                               if (hasStacks) {
+                                       return;
+                               }
+
+                               var meta = chart.getDatasetMeta(datasetIndex);
+                               if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
+                                       meta.stack !== undefined) {
+                                       hasStacks = true;
+                               }
+                       });
                }
-       });
 
-       scaleService.registerScaleType('linear', LinearScale, defaultConfig);
-};
+               if (opts.stacked || hasStacks) {
+                       var valuesPerStack = {};
+
+                       helpers.each(datasets, function(dataset, datasetIndex) {
+                               var meta = chart.getDatasetMeta(datasetIndex);
+                               var key = [
+                                       meta.type,
+                                       // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
+                                       ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
+                                       meta.stack
+                               ].join('.');
+
+                               if (valuesPerStack[key] === undefined) {
+                                       valuesPerStack[key] = {
+                                               positiveValues: [],
+                                               negativeValues: []
+                                       };
+                               }
+
+                               // Store these per type
+                               var positiveValues = valuesPerStack[key].positiveValues;
+                               var negativeValues = valuesPerStack[key].negativeValues;
+
+                               if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
+                                       helpers.each(dataset.data, function(rawValue, index) {
+                                               var value = +me.getRightValue(rawValue);
+                                               if (isNaN(value) || meta.data[index].hidden) {
+                                                       return;
+                                               }
+
+                                               positiveValues[index] = positiveValues[index] || 0;
+                                               negativeValues[index] = negativeValues[index] || 0;
+
+                                               if (opts.relativePoints) {
+                                                       positiveValues[index] = 100;
+                                               } else if (value < 0) {
+                                                       negativeValues[index] += value;
+                                               } else {
+                                                       positiveValues[index] += value;
+                                               }
+                                       });
+                               }
+                       });
+
+                       helpers.each(valuesPerStack, function(valuesForType) {
+                               var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
+                               var minVal = helpers.min(values);
+                               var maxVal = helpers.max(values);
+                               me.min = me.min === null ? minVal : Math.min(me.min, minVal);
+                               me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
+                       });
+
+               } else {
+                       helpers.each(datasets, function(dataset, datasetIndex) {
+                               var meta = chart.getDatasetMeta(datasetIndex);
+                               if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
+                                       helpers.each(dataset.data, function(rawValue, index) {
+                                               var value = +me.getRightValue(rawValue);
+                                               if (isNaN(value) || meta.data[index].hidden) {
+                                                       return;
+                                               }
+
+                                               if (me.min === null) {
+                                                       me.min = value;
+                                               } else if (value < me.min) {
+                                                       me.min = value;
+                                               }
+
+                                               if (me.max === null) {
+                                                       me.max = value;
+                                               } else if (value > me.max) {
+                                                       me.max = value;
+                                               }
+                                       });
+                               }
+                       });
+               }
+
+               me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
+               me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
+
+               // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
+               this.handleTickRangeOptions();
+       },
+
+       // Returns the maximum number of ticks based on the scale dimension
+       _computeTickLimit: function() {
+               var me = this;
+               var tickFont;
+
+               if (me.isHorizontal()) {
+                       return Math.ceil(me.width / 40);
+               }
+               tickFont = helpers.options._parseFont(me.options.ticks);
+               return Math.ceil(me.height / tickFont.lineHeight);
+       },
+
+       // Called after the ticks are built. We need
+       handleDirectionalChanges: function() {
+               if (!this.isHorizontal()) {
+                       // We are in a vertical orientation. The top value is the highest. So reverse the array
+                       this.ticks.reverse();
+               }
+       },
+
+       getLabelForIndex: function(index, datasetIndex) {
+               return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
+       },
+
+       // Utils
+       getPixelForValue: function(value) {
+               // This must be called after fit has been run so that
+               // this.left, this.top, this.right, and this.bottom have been defined
+               var me = this;
+               var start = me.start;
+
+               var rightValue = +me.getRightValue(value);
+               var pixel;
+               var range = me.end - start;
+
+               if (me.isHorizontal()) {
+                       pixel = me.left + (me.width / range * (rightValue - start));
+               } else {
+                       pixel = me.bottom - (me.height / range * (rightValue - start));
+               }
+               return pixel;
+       },
+
+       getValueForPixel: function(pixel) {
+               var me = this;
+               var isHorizontal = me.isHorizontal();
+               var innerDimension = isHorizontal ? me.width : me.height;
+               var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
+               return me.start + ((me.end - me.start) * offset);
+       },
+
+       getPixelForTick: function(index) {
+               return this.getPixelForValue(this.ticksAsNumbers[index]);
+       }
+});
+
+// INTERNAL: static default options, registered in src/chart.js
+module.exports._defaults = defaultConfig;
index 8dfe15a08cb61ab2e05c39bc4099871edd11b6b1..808f454407a6eeabac8dd6e9e152a07e8bd8faea 100644 (file)
@@ -3,6 +3,8 @@
 var helpers = require('../helpers/index');
 var Scale = require('../core/core.scale');
 
+var noop = helpers.noop;
+
 /**
  * Generate a set of linear ticks
  * @param generationOptions the options used to generate the ticks
@@ -74,156 +76,152 @@ function generateTicks(generationOptions, dataRange) {
        return ticks;
 }
 
-module.exports = function(Chart) {
-
-       var noop = helpers.noop;
-
-       Chart.LinearScaleBase = Scale.extend({
-               getRightValue: function(value) {
-                       if (typeof value === 'string') {
-                               return +value;
-                       }
-                       return Scale.prototype.getRightValue.call(this, value);
-               },
-
-               handleTickRangeOptions: function() {
-                       var me = this;
-                       var opts = me.options;
-                       var tickOpts = opts.ticks;
-
-                       // 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
-                       if (tickOpts.beginAtZero) {
-                               var minSign = helpers.sign(me.min);
-                               var maxSign = helpers.sign(me.max);
-
-                               if (minSign < 0 && maxSign < 0) {
-                                       // move the top up to 0
-                                       me.max = 0;
-                               } else if (minSign > 0 && maxSign > 0) {
-                                       // move the bottom down to 0
-                                       me.min = 0;
-                               }
+module.exports = Scale.extend({
+       getRightValue: function(value) {
+               if (typeof value === 'string') {
+                       return +value;
+               }
+               return Scale.prototype.getRightValue.call(this, value);
+       },
+
+       handleTickRangeOptions: function() {
+               var me = this;
+               var opts = me.options;
+               var tickOpts = opts.ticks;
+
+               // 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
+               if (tickOpts.beginAtZero) {
+                       var minSign = helpers.sign(me.min);
+                       var maxSign = helpers.sign(me.max);
+
+                       if (minSign < 0 && maxSign < 0) {
+                               // move the top up to 0
+                               me.max = 0;
+                       } else if (minSign > 0 && maxSign > 0) {
+                               // move the bottom down to 0
+                               me.min = 0;
                        }
+               }
 
-                       var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
-                       var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
-
-                       if (tickOpts.min !== undefined) {
-                               me.min = tickOpts.min;
-                       } else if (tickOpts.suggestedMin !== undefined) {
-                               if (me.min === null) {
-                                       me.min = tickOpts.suggestedMin;
-                               } else {
-                                       me.min = Math.min(me.min, tickOpts.suggestedMin);
-                               }
-                       }
+               var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
+               var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
 
-                       if (tickOpts.max !== undefined) {
-                               me.max = tickOpts.max;
-                       } else if (tickOpts.suggestedMax !== undefined) {
-                               if (me.max === null) {
-                                       me.max = tickOpts.suggestedMax;
-                               } else {
-                                       me.max = Math.max(me.max, tickOpts.suggestedMax);
-                               }
+               if (tickOpts.min !== undefined) {
+                       me.min = tickOpts.min;
+               } else if (tickOpts.suggestedMin !== undefined) {
+                       if (me.min === null) {
+                               me.min = tickOpts.suggestedMin;
+                       } else {
+                               me.min = Math.min(me.min, tickOpts.suggestedMin);
                        }
+               }
 
-                       if (setMin !== setMax) {
-                               // We set the min or the max but not both.
-                               // So ensure that our range is good
-                               // Inverted or 0 length range can happen when
-                               // ticks.min is set, and no datasets are visible
-                               if (me.min >= me.max) {
-                                       if (setMin) {
-                                               me.max = me.min + 1;
-                                       } else {
-                                               me.min = me.max - 1;
-                                       }
-                               }
+               if (tickOpts.max !== undefined) {
+                       me.max = tickOpts.max;
+               } else if (tickOpts.suggestedMax !== undefined) {
+                       if (me.max === null) {
+                               me.max = tickOpts.suggestedMax;
+                       } else {
+                               me.max = Math.max(me.max, tickOpts.suggestedMax);
                        }
+               }
 
-                       if (me.min === me.max) {
-                               me.max++;
-
-                               if (!tickOpts.beginAtZero) {
-                                       me.min--;
+               if (setMin !== setMax) {
+                       // We set the min or the max but not both.
+                       // So ensure that our range is good
+                       // Inverted or 0 length range can happen when
+                       // ticks.min is set, and no datasets are visible
+                       if (me.min >= me.max) {
+                               if (setMin) {
+                                       me.max = me.min + 1;
+                               } else {
+                                       me.min = me.max - 1;
                                }
                        }
-               },
+               }
 
-               getTickLimit: function() {
-                       var me = this;
-                       var tickOpts = me.options.ticks;
-                       var stepSize = tickOpts.stepSize;
-                       var maxTicksLimit = tickOpts.maxTicksLimit;
-                       var maxTicks;
+               if (me.min === me.max) {
+                       me.max++;
 
-                       if (stepSize) {
-                               maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1;
-                       } else {
-                               maxTicks = me._computeTickLimit();
-                               maxTicksLimit = maxTicksLimit || 11;
+                       if (!tickOpts.beginAtZero) {
+                               me.min--;
                        }
+               }
+       },
+
+       getTickLimit: function() {
+               var me = this;
+               var tickOpts = me.options.ticks;
+               var stepSize = tickOpts.stepSize;
+               var maxTicksLimit = tickOpts.maxTicksLimit;
+               var maxTicks;
+
+               if (stepSize) {
+                       maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1;
+               } else {
+                       maxTicks = me._computeTickLimit();
+                       maxTicksLimit = maxTicksLimit || 11;
+               }
 
-                       if (maxTicksLimit) {
-                               maxTicks = Math.min(maxTicksLimit, maxTicks);
-                       }
-
-                       return maxTicks;
-               },
-
-               _computeTickLimit: function() {
-                       return Number.POSITIVE_INFINITY;
-               },
-
-               handleDirectionalChanges: noop,
-
-               buildTicks: function() {
-                       var me = this;
-                       var opts = me.options;
-                       var tickOpts = opts.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 40
-                       // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
-                       // the graph. Make sure we always have at least 2 ticks
-                       var maxTicks = me.getTickLimit();
-                       maxTicks = Math.max(2, maxTicks);
-
-                       var numericGeneratorOptions = {
-                               maxTicks: maxTicks,
-                               min: tickOpts.min,
-                               max: tickOpts.max,
-                               precision: tickOpts.precision,
-                               stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
-                       };
-                       var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);
-
-                       me.handleDirectionalChanges();
+               if (maxTicksLimit) {
+                       maxTicks = Math.min(maxTicksLimit, maxTicks);
+               }
 
-                       // At this point, we need to update our max and min given the tick values since we have expanded the
-                       // range of the scale
-                       me.max = helpers.max(ticks);
-                       me.min = helpers.min(ticks);
+               return maxTicks;
+       },
+
+       _computeTickLimit: function() {
+               return Number.POSITIVE_INFINITY;
+       },
+
+       handleDirectionalChanges: noop,
+
+       buildTicks: function() {
+               var me = this;
+               var opts = me.options;
+               var tickOpts = opts.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 40
+               // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
+               // the graph. Make sure we always have at least 2 ticks
+               var maxTicks = me.getTickLimit();
+               maxTicks = Math.max(2, maxTicks);
+
+               var numericGeneratorOptions = {
+                       maxTicks: maxTicks,
+                       min: tickOpts.min,
+                       max: tickOpts.max,
+                       precision: tickOpts.precision,
+                       stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
+               };
+               var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);
+
+               me.handleDirectionalChanges();
+
+               // At this point, we need to update our max and min given the tick values since we have expanded the
+               // range of the scale
+               me.max = helpers.max(ticks);
+               me.min = helpers.min(ticks);
+
+               if (tickOpts.reverse) {
+                       ticks.reverse();
+
+                       me.start = me.max;
+                       me.end = me.min;
+               } else {
+                       me.start = me.min;
+                       me.end = me.max;
+               }
+       },
 
-                       if (tickOpts.reverse) {
-                               ticks.reverse();
+       convertTicksToLabels: function() {
+               var me = this;
+               me.ticksAsNumbers = me.ticks.slice();
+               me.zeroLineIndex = me.ticks.indexOf(0);
 
-                               me.start = me.max;
-                               me.end = me.min;
-                       } else {
-                               me.start = me.min;
-                               me.end = me.max;
-                       }
-               },
-               convertTicksToLabels: function() {
-                       var me = this;
-                       me.ticksAsNumbers = me.ticks.slice();
-                       me.zeroLineIndex = me.ticks.indexOf(0);
-
-                       Scale.prototype.convertTicksToLabels.call(me);
-               }
-       });
-};
+               Scale.prototype.convertTicksToLabels.call(me);
+       }
+});
index 240df0faf4606bcd612acef8b25f2ff8204ccf10..5dd5fe80e3073f5f5679df0c85bde52f5b15059e 100644 (file)
@@ -1,8 +1,8 @@
 'use strict';
 
+var defaults = require('../core/core.defaults');
 var helpers = require('../helpers/index');
 var Scale = require('../core/core.scale');
-var scaleService = require('../core/core.scaleService');
 var Ticks = require('../core/core.ticks');
 
 /**
@@ -52,294 +52,299 @@ function generateTicks(generationOptions, dataRange) {
        return ticks;
 }
 
+var defaultConfig = {
+       position: 'left',
 
-module.exports = function(Chart) {
-
-       var defaultConfig = {
-               position: 'left',
+       // label settings
+       ticks: {
+               callback: Ticks.formatters.logarithmic
+       }
+};
 
-               // label settings
-               ticks: {
-                       callback: Ticks.formatters.logarithmic
+module.exports = Scale.extend({
+       determineDataLimits: function() {
+               var me = this;
+               var opts = me.options;
+               var chart = me.chart;
+               var data = chart.data;
+               var datasets = data.datasets;
+               var isHorizontal = me.isHorizontal();
+               function IDMatches(meta) {
+                       return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
                }
-       };
-
-       var LogarithmicScale = Scale.extend({
-               determineDataLimits: function() {
-                       var me = this;
-                       var opts = me.options;
-                       var chart = me.chart;
-                       var data = chart.data;
-                       var datasets = data.datasets;
-                       var isHorizontal = me.isHorizontal();
-                       function IDMatches(meta) {
-                               return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
-                       }
 
-                       // Calculate Range
-                       me.min = null;
-                       me.max = null;
-                       me.minNotZero = null;
+               // Calculate Range
+               me.min = null;
+               me.max = null;
+               me.minNotZero = null;
 
-                       var hasStacks = opts.stacked;
-                       if (hasStacks === undefined) {
-                               helpers.each(datasets, function(dataset, datasetIndex) {
-                                       if (hasStacks) {
-                                               return;
-                                       }
+               var hasStacks = opts.stacked;
+               if (hasStacks === undefined) {
+                       helpers.each(datasets, function(dataset, datasetIndex) {
+                               if (hasStacks) {
+                                       return;
+                               }
 
-                                       var meta = chart.getDatasetMeta(datasetIndex);
-                                       if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
-                                               meta.stack !== undefined) {
-                                               hasStacks = true;
+                               var meta = chart.getDatasetMeta(datasetIndex);
+                               if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
+                                       meta.stack !== undefined) {
+                                       hasStacks = true;
+                               }
+                       });
+               }
+
+               if (opts.stacked || hasStacks) {
+                       var valuesPerStack = {};
+
+                       helpers.each(datasets, function(dataset, datasetIndex) {
+                               var meta = chart.getDatasetMeta(datasetIndex);
+                               var key = [
+                                       meta.type,
+                                       // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
+                                       ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
+                                       meta.stack
+                               ].join('.');
+
+                               if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
+                                       if (valuesPerStack[key] === undefined) {
+                                               valuesPerStack[key] = [];
                                        }
-                               });
-                       }
 
-                       if (opts.stacked || hasStacks) {
-                               var valuesPerStack = {};
-
-                               helpers.each(datasets, function(dataset, datasetIndex) {
-                                       var meta = chart.getDatasetMeta(datasetIndex);
-                                       var key = [
-                                               meta.type,
-                                               // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
-                                               ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
-                                               meta.stack
-                                       ].join('.');
-
-                                       if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
-                                               if (valuesPerStack[key] === undefined) {
-                                                       valuesPerStack[key] = [];
+                                       helpers.each(dataset.data, function(rawValue, index) {
+                                               var values = valuesPerStack[key];
+                                               var value = +me.getRightValue(rawValue);
+                                               // invalid, hidden and negative values are ignored
+                                               if (isNaN(value) || meta.data[index].hidden || value < 0) {
+                                                       return;
+                                               }
+                                               values[index] = values[index] || 0;
+                                               values[index] += value;
+                                       });
+                               }
+                       });
+
+                       helpers.each(valuesPerStack, function(valuesForType) {
+                               if (valuesForType.length > 0) {
+                                       var minVal = helpers.min(valuesForType);
+                                       var maxVal = helpers.max(valuesForType);
+                                       me.min = me.min === null ? minVal : Math.min(me.min, minVal);
+                                       me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
+                               }
+                       });
+
+               } else {
+                       helpers.each(datasets, function(dataset, datasetIndex) {
+                               var meta = chart.getDatasetMeta(datasetIndex);
+                               if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
+                                       helpers.each(dataset.data, function(rawValue, index) {
+                                               var value = +me.getRightValue(rawValue);
+                                               // invalid, hidden and negative values are ignored
+                                               if (isNaN(value) || meta.data[index].hidden || value < 0) {
+                                                       return;
                                                }
 
-                                               helpers.each(dataset.data, function(rawValue, index) {
-                                                       var values = valuesPerStack[key];
-                                                       var value = +me.getRightValue(rawValue);
-                                                       // invalid, hidden and negative values are ignored
-                                                       if (isNaN(value) || meta.data[index].hidden || value < 0) {
-                                                               return;
-                                                       }
-                                                       values[index] = values[index] || 0;
-                                                       values[index] += value;
-                                               });
-                                       }
-                               });
-
-                               helpers.each(valuesPerStack, function(valuesForType) {
-                                       if (valuesForType.length > 0) {
-                                               var minVal = helpers.min(valuesForType);
-                                               var maxVal = helpers.max(valuesForType);
-                                               me.min = me.min === null ? minVal : Math.min(me.min, minVal);
-                                               me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
-                                       }
-                               });
+                                               if (me.min === null) {
+                                                       me.min = value;
+                                               } else if (value < me.min) {
+                                                       me.min = value;
+                                               }
 
-                       } else {
-                               helpers.each(datasets, function(dataset, datasetIndex) {
-                                       var meta = chart.getDatasetMeta(datasetIndex);
-                                       if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
-                                               helpers.each(dataset.data, function(rawValue, index) {
-                                                       var value = +me.getRightValue(rawValue);
-                                                       // invalid, hidden and negative values are ignored
-                                                       if (isNaN(value) || meta.data[index].hidden || value < 0) {
-                                                               return;
-                                                       }
-
-                                                       if (me.min === null) {
-                                                               me.min = value;
-                                                       } else if (value < me.min) {
-                                                               me.min = value;
-                                                       }
-
-                                                       if (me.max === null) {
-                                                               me.max = value;
-                                                       } else if (value > me.max) {
-                                                               me.max = value;
-                                                       }
-
-                                                       if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
-                                                               me.minNotZero = value;
-                                                       }
-                                               });
-                                       }
-                               });
-                       }
+                                               if (me.max === null) {
+                                                       me.max = value;
+                                               } else if (value > me.max) {
+                                                       me.max = value;
+                                               }
 
-                       // Common base implementation to handle ticks.min, ticks.max
-                       this.handleTickRangeOptions();
-               },
-               handleTickRangeOptions: function() {
-                       var me = this;
-                       var opts = me.options;
-                       var tickOpts = opts.ticks;
-                       var valueOrDefault = helpers.valueOrDefault;
-                       var DEFAULT_MIN = 1;
-                       var DEFAULT_MAX = 10;
-
-                       me.min = valueOrDefault(tickOpts.min, me.min);
-                       me.max = valueOrDefault(tickOpts.max, me.max);
-
-                       if (me.min === me.max) {
-                               if (me.min !== 0 && me.min !== null) {
-                                       me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
-                                       me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
-                               } else {
-                                       me.min = DEFAULT_MIN;
-                                       me.max = DEFAULT_MAX;
-                               }
-                       }
-                       if (me.min === null) {
-                               me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1);
-                       }
-                       if (me.max === null) {
-                               me.max = me.min !== 0
-                                       ? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1)
-                                       : DEFAULT_MAX;
-                       }
-                       if (me.minNotZero === null) {
-                               if (me.min > 0) {
-                                       me.minNotZero = me.min;
-                               } else if (me.max < 1) {
-                                       me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max)));
-                               } else {
-                                       me.minNotZero = DEFAULT_MIN;
+                                               if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
+                                                       me.minNotZero = value;
+                                               }
+                                       });
                                }
-                       }
-               },
-               buildTicks: function() {
-                       var me = this;
-                       var opts = me.options;
-                       var tickOpts = opts.ticks;
-                       var reverse = !me.isHorizontal();
-
-                       var generationOptions = {
-                               min: tickOpts.min,
-                               max: tickOpts.max
-                       };
-                       var ticks = me.ticks = generateTicks(generationOptions, me);
-
-                       // At this point, we need to update our max and min given the tick values since we have expanded the
-                       // range of the scale
-                       me.max = helpers.max(ticks);
-                       me.min = helpers.min(ticks);
-
-                       if (tickOpts.reverse) {
-                               reverse = !reverse;
-                               me.start = me.max;
-                               me.end = me.min;
-                       } else {
-                               me.start = me.min;
-                               me.end = me.max;
-                       }
-                       if (reverse) {
-                               ticks.reverse();
-                       }
-               },
-               convertTicksToLabels: function() {
-                       this.tickValues = this.ticks.slice();
-
-                       Scale.prototype.convertTicksToLabels.call(this);
-               },
-               // Get the correct tooltip label
-               getLabelForIndex: function(index, datasetIndex) {
-                       return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
-               },
-               getPixelForTick: function(index) {
-                       return this.getPixelForValue(this.tickValues[index]);
-               },
-               /**
-                * Returns the value of the first tick.
-                * @param {Number} value - The minimum not zero value.
-                * @return {Number} The first tick value.
-                * @private
-                */
-               _getFirstTickValue: function(value) {
-                       var exp = Math.floor(helpers.log10(value));
-                       var significand = Math.floor(value / Math.pow(10, exp));
-
-                       return significand * Math.pow(10, exp);
-               },
-               getPixelForValue: function(value) {
-                       var me = this;
-                       var reverse = me.options.ticks.reverse;
-                       var log10 = helpers.log10;
-                       var firstTickValue = me._getFirstTickValue(me.minNotZero);
-                       var offset = 0;
-                       var innerDimension, pixel, start, end, sign;
-
-                       value = +me.getRightValue(value);
-                       if (reverse) {
-                               start = me.end;
-                               end = me.start;
-                               sign = -1;
+                       });
+               }
+
+               // Common base implementation to handle ticks.min, ticks.max
+               this.handleTickRangeOptions();
+       },
+
+       handleTickRangeOptions: function() {
+               var me = this;
+               var opts = me.options;
+               var tickOpts = opts.ticks;
+               var valueOrDefault = helpers.valueOrDefault;
+               var DEFAULT_MIN = 1;
+               var DEFAULT_MAX = 10;
+
+               me.min = valueOrDefault(tickOpts.min, me.min);
+               me.max = valueOrDefault(tickOpts.max, me.max);
+
+               if (me.min === me.max) {
+                       if (me.min !== 0 && me.min !== null) {
+                               me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
+                               me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
                        } else {
-                               start = me.start;
-                               end = me.end;
-                               sign = 1;
+                               me.min = DEFAULT_MIN;
+                               me.max = DEFAULT_MAX;
                        }
-                       if (me.isHorizontal()) {
-                               innerDimension = me.width;
-                               pixel = reverse ? me.right : me.left;
+               }
+               if (me.min === null) {
+                       me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1);
+               }
+               if (me.max === null) {
+                       me.max = me.min !== 0
+                               ? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1)
+                               : DEFAULT_MAX;
+               }
+               if (me.minNotZero === null) {
+                       if (me.min > 0) {
+                               me.minNotZero = me.min;
+                       } else if (me.max < 1) {
+                               me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max)));
                        } else {
-                               innerDimension = me.height;
-                               sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0)
-                               pixel = reverse ? me.top : me.bottom;
-                       }
-                       if (value !== start) {
-                               if (start === 0) { // include zero tick
-                                       offset = helpers.getValueOrDefault(
-                                               me.options.ticks.fontSize,
-                                               Chart.defaults.global.defaultFontSize
-                                       );
-                                       innerDimension -= offset;
-                                       start = firstTickValue;
-                               }
-                               if (value !== 0) {
-                                       offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start));
-                               }
-                               pixel += sign * offset;
+                               me.minNotZero = DEFAULT_MIN;
                        }
-                       return pixel;
-               },
-               getValueForPixel: function(pixel) {
-                       var me = this;
-                       var reverse = me.options.ticks.reverse;
-                       var log10 = helpers.log10;
-                       var firstTickValue = me._getFirstTickValue(me.minNotZero);
-                       var innerDimension, start, end, value;
-
-                       if (reverse) {
-                               start = me.end;
-                               end = me.start;
-                       } else {
-                               start = me.start;
-                               end = me.end;
+               }
+       },
+
+       buildTicks: function() {
+               var me = this;
+               var opts = me.options;
+               var tickOpts = opts.ticks;
+               var reverse = !me.isHorizontal();
+
+               var generationOptions = {
+                       min: tickOpts.min,
+                       max: tickOpts.max
+               };
+               var ticks = me.ticks = generateTicks(generationOptions, me);
+
+               // At this point, we need to update our max and min given the tick values since we have expanded the
+               // range of the scale
+               me.max = helpers.max(ticks);
+               me.min = helpers.min(ticks);
+
+               if (tickOpts.reverse) {
+                       reverse = !reverse;
+                       me.start = me.max;
+                       me.end = me.min;
+               } else {
+                       me.start = me.min;
+                       me.end = me.max;
+               }
+               if (reverse) {
+                       ticks.reverse();
+               }
+       },
+
+       convertTicksToLabels: function() {
+               this.tickValues = this.ticks.slice();
+
+               Scale.prototype.convertTicksToLabels.call(this);
+       },
+
+       // Get the correct tooltip label
+       getLabelForIndex: function(index, datasetIndex) {
+               return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
+       },
+
+       getPixelForTick: function(index) {
+               return this.getPixelForValue(this.tickValues[index]);
+       },
+
+       /**
+        * Returns the value of the first tick.
+        * @param {Number} value - The minimum not zero value.
+        * @return {Number} The first tick value.
+        * @private
+        */
+       _getFirstTickValue: function(value) {
+               var exp = Math.floor(helpers.log10(value));
+               var significand = Math.floor(value / Math.pow(10, exp));
+
+               return significand * Math.pow(10, exp);
+       },
+
+       getPixelForValue: function(value) {
+               var me = this;
+               var reverse = me.options.ticks.reverse;
+               var log10 = helpers.log10;
+               var firstTickValue = me._getFirstTickValue(me.minNotZero);
+               var offset = 0;
+               var innerDimension, pixel, start, end, sign;
+
+               value = +me.getRightValue(value);
+               if (reverse) {
+                       start = me.end;
+                       end = me.start;
+                       sign = -1;
+               } else {
+                       start = me.start;
+                       end = me.end;
+                       sign = 1;
+               }
+               if (me.isHorizontal()) {
+                       innerDimension = me.width;
+                       pixel = reverse ? me.right : me.left;
+               } else {
+                       innerDimension = me.height;
+                       sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0)
+                       pixel = reverse ? me.top : me.bottom;
+               }
+               if (value !== start) {
+                       if (start === 0) { // include zero tick
+                               offset = helpers.getValueOrDefault(
+                                       me.options.ticks.fontSize,
+                                       defaults.global.defaultFontSize
+                               );
+                               innerDimension -= offset;
+                               start = firstTickValue;
                        }
-                       if (me.isHorizontal()) {
-                               innerDimension = me.width;
-                               value = reverse ? me.right - pixel : pixel - me.left;
-                       } else {
-                               innerDimension = me.height;
-                               value = reverse ? pixel - me.top : me.bottom - pixel;
+                       if (value !== 0) {
+                               offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start));
                        }
-                       if (value !== start) {
-                               if (start === 0) { // include zero tick
-                                       var offset = helpers.getValueOrDefault(
-                                               me.options.ticks.fontSize,
-                                               Chart.defaults.global.defaultFontSize
-                                       );
-                                       value -= offset;
-                                       innerDimension -= offset;
-                                       start = firstTickValue;
-                               }
-                               value *= log10(end) - log10(start);
-                               value /= innerDimension;
-                               value = Math.pow(10, log10(start) + value);
+                       pixel += sign * offset;
+               }
+               return pixel;
+       },
+
+       getValueForPixel: function(pixel) {
+               var me = this;
+               var reverse = me.options.ticks.reverse;
+               var log10 = helpers.log10;
+               var firstTickValue = me._getFirstTickValue(me.minNotZero);
+               var innerDimension, start, end, value;
+
+               if (reverse) {
+                       start = me.end;
+                       end = me.start;
+               } else {
+                       start = me.start;
+                       end = me.end;
+               }
+               if (me.isHorizontal()) {
+                       innerDimension = me.width;
+                       value = reverse ? me.right - pixel : pixel - me.left;
+               } else {
+                       innerDimension = me.height;
+                       value = reverse ? pixel - me.top : me.bottom - pixel;
+               }
+               if (value !== start) {
+                       if (start === 0) { // include zero tick
+                               var offset = helpers.getValueOrDefault(
+                                       me.options.ticks.fontSize,
+                                       defaults.global.defaultFontSize
+                               );
+                               value -= offset;
+                               innerDimension -= offset;
+                               start = firstTickValue;
                        }
-                       return value;
+                       value *= log10(end) - log10(start);
+                       value /= innerDimension;
+                       value = Math.pow(10, log10(start) + value);
                }
-       });
+               return value;
+       }
+});
 
-       scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
-};
+// INTERNAL: static default options, registered in src/chart.js
+module.exports._defaults = defaultConfig;
index bc8129845ced3d655322539a6d0e55d5c6521164..a407ee0295ca24a31c428a75886edbee3160c6e8 100644 (file)
 
 var defaults = require('../core/core.defaults');
 var helpers = require('../helpers/index');
-var scaleService = require('../core/core.scaleService');
+var LinearScaleBase = require('./scale.linearbase');
 var Ticks = require('../core/core.ticks');
 
-module.exports = function(Chart) {
+var defaultConfig = {
+       display: true,
 
-       var defaultConfig = {
-               display: true,
-
-               // Boolean - Whether to animate scaling the chart from the centre
-               animate: true,
-               position: 'chartArea',
+       // Boolean - Whether to animate scaling the chart from the centre
+       animate: true,
+       position: 'chartArea',
 
-               angleLines: {
-                       display: true,
-                       color: 'rgba(0, 0, 0, 0.1)',
-                       lineWidth: 1,
-                       borderDash: [],
-                       borderDashOffset: 0.0
-               },
+       angleLines: {
+               display: true,
+               color: 'rgba(0, 0, 0, 0.1)',
+               lineWidth: 1,
+               borderDash: [],
+               borderDashOffset: 0.0
+       },
 
-               gridLines: {
-                       circular: false
-               },
+       gridLines: {
+               circular: false
+       },
 
-               // label settings
-               ticks: {
-                       // Boolean - Show a backdrop to the scale label
-                       showLabelBackdrop: true,
+       // label settings
+       ticks: {
+               // Boolean - Show a backdrop to the scale label
+               showLabelBackdrop: true,
 
-                       // String - The colour of the label backdrop
-                       backdropColor: 'rgba(255,255,255,0.75)',
+               // String - The colour of the label backdrop
+               backdropColor: 'rgba(255,255,255,0.75)',
 
-                       // Number - The backdrop padding above & below the label in pixels
-                       backdropPaddingY: 2,
+               // Number - The backdrop padding above & below the label in pixels
+               backdropPaddingY: 2,
 
-                       // Number - The backdrop padding to the side of the label in pixels
-                       backdropPaddingX: 2,
+               // Number - The backdrop padding to the side of the label in pixels
+               backdropPaddingX: 2,
 
-                       callback: Ticks.formatters.linear
-               },
+               callback: Ticks.formatters.linear
+       },
 
-               pointLabels: {
-                       // Boolean - if true, show point labels
-                       display: true,
+       pointLabels: {
+               // Boolean - if true, show point labels
+               display: true,
 
-                       // Number - Point label font size in pixels
-                       fontSize: 10,
+               // Number - Point label font size in pixels
+               fontSize: 10,
 
-                       // Function - Used to convert point labels
-                       callback: function(label) {
-                               return label;
-                       }
+               // Function - Used to convert point labels
+               callback: function(label) {
+                       return label;
                }
-       };
-
-       function getValueCount(scale) {
-               var opts = scale.options;
-               return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
        }
+};
 
-       function getTickBackdropHeight(opts) {
-               var tickOpts = opts.ticks;
+function getValueCount(scale) {
+       var opts = scale.options;
+       return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
+}
 
-               if (tickOpts.display && opts.display) {
-                       return helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2;
-               }
-               return 0;
-       }
+function getTickBackdropHeight(opts) {
+       var tickOpts = opts.ticks;
 
-       function measureLabelSize(ctx, lineHeight, label) {
-               if (helpers.isArray(label)) {
-                       return {
-                               w: helpers.longestText(ctx, ctx.font, label),
-                               h: label.length * lineHeight
-                       };
-               }
+       if (tickOpts.display && opts.display) {
+               return helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2;
+       }
+       return 0;
+}
 
+function measureLabelSize(ctx, lineHeight, label) {
+       if (helpers.isArray(label)) {
                return {
-                       w: ctx.measureText(label).width,
-                       h: lineHeight
+                       w: helpers.longestText(ctx, ctx.font, label),
+                       h: label.length * lineHeight
                };
        }
 
-       function determineLimits(angle, pos, size, min, max) {
-               if (angle === min || angle === max) {
-                       return {
-                               start: pos - (size / 2),
-                               end: pos + (size / 2)
-                       };
-               } else if (angle < min || angle > max) {
-                       return {
-                               start: pos - size,
-                               end: pos
-                       };
-               }
+       return {
+               w: ctx.measureText(label).width,
+               h: lineHeight
+       };
+}
 
+function determineLimits(angle, pos, size, min, max) {
+       if (angle === min || angle === max) {
+               return {
+                       start: pos - (size / 2),
+                       end: pos + (size / 2)
+               };
+       } else if (angle < min || angle > max) {
                return {
-                       start: pos,
-                       end: pos + size
+                       start: pos - size,
+                       end: pos
                };
        }
 
-       /**
-        * Helper function to fit a radial linear scale with point labels
-        */
-       function fitWithPointLabels(scale) {
-               /*
-                * Right, this is really confusing and there is a lot of maths going on here
-                * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
-                *
-                * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
-                *
-                * Solution:
-                *
-                * We assume the radius of the polygon is half the size of the canvas at first
-                * at each index we check if the text overlaps.
-                *
-                * Where it does, we store that angle and that index.
-                *
-                * After finding the largest index and angle we calculate how much we need to remove
-                * from the shape radius to move the point inwards by that x.
-                *
-                * We average the left and right distances to get the maximum shape radius that can fit in the box
-                * along with labels.
-                *
-                * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
-                * on each side, removing that from the size, halving it and adding the left x protrusion width.
-                *
-                * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
-                * and position it in the most space efficient manner
-                *
-                * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
-                */
-
-               var plFont = helpers.options._parseFont(scale.options.pointLabels);
-
-               // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
-               // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
-               var furthestLimits = {
-                       l: 0,
-                       r: scale.width,
-                       t: 0,
-                       b: scale.height
-               };
-               var furthestAngles = {};
-               var i, textSize, pointPosition;
+       return {
+               start: pos,
+               end: pos + size
+       };
+}
+
+/**
+ * Helper function to fit a radial linear scale with point labels
+ */
+function fitWithPointLabels(scale) {
+
+       // Right, this is really confusing and there is a lot of maths going on here
+       // The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
+       //
+       // Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
+       //
+       // Solution:
+       //
+       // We assume the radius of the polygon is half the size of the canvas at first
+       // at each index we check if the text overlaps.
+       //
+       // Where it does, we store that angle and that index.
+       //
+       // After finding the largest index and angle we calculate how much we need to remove
+       // from the shape radius to move the point inwards by that x.
+       //
+       // We average the left and right distances to get the maximum shape radius that can fit in the box
+       // along with labels.
+       //
+       // Once we have that, we can find the centre point for the chart, by taking the x text protrusion
+       // on each side, removing that from the size, halving it and adding the left x protrusion width.
+       //
+       // This will mean we have a shape fitted to the canvas, as large as it can be with the labels
+       // and position it in the most space efficient manner
+       //
+       // https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
+
+       var plFont = helpers.options._parseFont(scale.options.pointLabels);
+
+       // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
+       // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
+       var furthestLimits = {
+               l: 0,
+               r: scale.width,
+               t: 0,
+               b: scale.height
+       };
+       var furthestAngles = {};
+       var i, textSize, pointPosition;
+
+       scale.ctx.font = plFont.string;
+       scale._pointLabelSizes = [];
+
+       var valueCount = getValueCount(scale);
+       for (i = 0; i < valueCount; i++) {
+               pointPosition = scale.getPointPosition(i, scale.drawingArea + 5);
+               textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i] || '');
+               scale._pointLabelSizes[i] = textSize;
+
+               // Add quarter circle to make degree 0 mean top of circle
+               var angleRadians = scale.getIndexAngle(i);
+               var angle = helpers.toDegrees(angleRadians) % 360;
+               var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
+               var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
+
+               if (hLimits.start < furthestLimits.l) {
+                       furthestLimits.l = hLimits.start;
+                       furthestAngles.l = angleRadians;
+               }
 
-               scale.ctx.font = plFont.string;
-               scale._pointLabelSizes = [];
+               if (hLimits.end > furthestLimits.r) {
+                       furthestLimits.r = hLimits.end;
+                       furthestAngles.r = angleRadians;
+               }
 
-               var valueCount = getValueCount(scale);
-               for (i = 0; i < valueCount; i++) {
-                       pointPosition = scale.getPointPosition(i, scale.drawingArea + 5);
-                       textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i] || '');
-                       scale._pointLabelSizes[i] = textSize;
+               if (vLimits.start < furthestLimits.t) {
+                       furthestLimits.t = vLimits.start;
+                       furthestAngles.t = angleRadians;
+               }
 
-                       // Add quarter circle to make degree 0 mean top of circle
-                       var angleRadians = scale.getIndexAngle(i);
-                       var angle = helpers.toDegrees(angleRadians) % 360;
-                       var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
-                       var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
+               if (vLimits.end > furthestLimits.b) {
+                       furthestLimits.b = vLimits.end;
+                       furthestAngles.b = angleRadians;
+               }
+       }
 
-                       if (hLimits.start < furthestLimits.l) {
-                               furthestLimits.l = hLimits.start;
-                               furthestAngles.l = angleRadians;
-                       }
+       scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles);
+}
 
-                       if (hLimits.end > furthestLimits.r) {
-                               furthestLimits.r = hLimits.end;
-                               furthestAngles.r = angleRadians;
-                       }
+function getTextAlignForAngle(angle) {
+       if (angle === 0 || angle === 180) {
+               return 'center';
+       } else if (angle < 180) {
+               return 'left';
+       }
 
-                       if (vLimits.start < furthestLimits.t) {
-                               furthestLimits.t = vLimits.start;
-                               furthestAngles.t = angleRadians;
-                       }
+       return 'right';
+}
 
-                       if (vLimits.end > furthestLimits.b) {
-                               furthestLimits.b = vLimits.end;
-                               furthestAngles.b = angleRadians;
-                       }
+function fillText(ctx, text, position, lineHeight) {
+       var y = position.y + lineHeight / 2;
+       var i, ilen;
+
+       if (helpers.isArray(text)) {
+               for (i = 0, ilen = text.length; i < ilen; ++i) {
+                       ctx.fillText(text[i], position.x, y);
+                       y += lineHeight;
                }
+       } else {
+               ctx.fillText(text, position.x, y);
+       }
+}
 
-               scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles);
+function adjustPointPositionForLabelHeight(angle, textSize, position) {
+       if (angle === 90 || angle === 270) {
+               position.y -= (textSize.h / 2);
+       } else if (angle > 270 || angle < 90) {
+               position.y -= textSize.h;
+       }
+}
+
+function drawPointLabels(scale) {
+       var ctx = scale.ctx;
+       var opts = scale.options;
+       var angleLineOpts = opts.angleLines;
+       var gridLineOpts = opts.gridLines;
+       var pointLabelOpts = opts.pointLabels;
+       var lineWidth = helpers.valueOrDefault(angleLineOpts.lineWidth, gridLineOpts.lineWidth);
+       var lineColor = helpers.valueOrDefault(angleLineOpts.color, gridLineOpts.color);
+       var tickBackdropHeight = getTickBackdropHeight(opts);
+
+       ctx.save();
+       ctx.lineWidth = lineWidth;
+       ctx.strokeStyle = lineColor;
+       if (ctx.setLineDash) {
+               ctx.setLineDash(helpers.valueOrDefault(angleLineOpts.borderDash, gridLineOpts.borderDash) || []);
+               ctx.lineDashOffset = helpers.valueOrDefault(angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset) || 0.0;
        }
 
-       function getTextAlignForAngle(angle) {
-               if (angle === 0 || angle === 180) {
-                       return 'center';
-               } else if (angle < 180) {
-                       return 'left';
+       var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
+
+       // Point Label Font
+       var plFont = helpers.options._parseFont(pointLabelOpts);
+
+       ctx.font = plFont.string;
+       ctx.textBaseline = 'middle';
+
+       for (var i = getValueCount(scale) - 1; i >= 0; i--) {
+               if (angleLineOpts.display && lineWidth && lineColor) {
+                       var outerPosition = scale.getPointPosition(i, outerDistance);
+                       ctx.beginPath();
+                       ctx.moveTo(scale.xCenter, scale.yCenter);
+                       ctx.lineTo(outerPosition.x, outerPosition.y);
+                       ctx.stroke();
                }
 
-               return 'right';
-       }
+               if (pointLabelOpts.display) {
+                       // Extra pixels out for some label spacing
+                       var extra = (i === 0 ? tickBackdropHeight / 2 : 0);
+                       var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
 
-       function fillText(ctx, text, position, lineHeight) {
-               var y = position.y + lineHeight / 2;
-               var i, ilen;
+                       // Keep this in loop since we may support array properties here
+                       var pointLabelFontColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, defaults.global.defaultFontColor);
+                       ctx.fillStyle = pointLabelFontColor;
 
-               if (helpers.isArray(text)) {
-                       for (i = 0, ilen = text.length; i < ilen; ++i) {
-                               ctx.fillText(text[i], position.x, y);
-                               y += lineHeight;
-                       }
-               } else {
-                       ctx.fillText(text, position.x, y);
+                       var angleRadians = scale.getIndexAngle(i);
+                       var angle = helpers.toDegrees(angleRadians);
+                       ctx.textAlign = getTextAlignForAngle(angle);
+                       adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
+                       fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight);
                }
        }
+       ctx.restore();
+}
+
+function drawRadiusLine(scale, gridLineOpts, radius, index) {
+       var ctx = scale.ctx;
+       var circular = gridLineOpts.circular;
+       var valueCount = getValueCount(scale);
+       var lineColor = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
+       var lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
+       var pointPosition;
+
+       if ((!circular && !valueCount) || !lineColor || !lineWidth) {
+               return;
+       }
 
-       function adjustPointPositionForLabelHeight(angle, textSize, position) {
-               if (angle === 90 || angle === 270) {
-                       position.y -= (textSize.h / 2);
-               } else if (angle > 270 || angle < 90) {
-                       position.y -= textSize.h;
-               }
+       ctx.save();
+       ctx.strokeStyle = lineColor;
+       ctx.lineWidth = lineWidth;
+       if (ctx.setLineDash) {
+               ctx.setLineDash(gridLineOpts.borderDash || []);
+               ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0;
        }
 
-       function drawPointLabels(scale) {
-               var ctx = scale.ctx;
-               var opts = scale.options;
-               var angleLineOpts = opts.angleLines;
-               var gridLineOpts = opts.gridLines;
-               var pointLabelOpts = opts.pointLabels;
-               var lineWidth = helpers.valueOrDefault(angleLineOpts.lineWidth, gridLineOpts.lineWidth);
-               var lineColor = helpers.valueOrDefault(angleLineOpts.color, gridLineOpts.color);
-               var tickBackdropHeight = getTickBackdropHeight(opts);
-
-               ctx.save();
-               ctx.lineWidth = lineWidth;
-               ctx.strokeStyle = lineColor;
-               if (ctx.setLineDash) {
-                       ctx.setLineDash(helpers.valueOrDefault(angleLineOpts.borderDash, gridLineOpts.borderDash) || []);
-                       ctx.lineDashOffset = helpers.valueOrDefault(angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset) || 0.0;
+       ctx.beginPath();
+       if (circular) {
+               // Draw circular arcs between the points
+               ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
+       } else {
+               // Draw straight lines connecting each index
+               pointPosition = scale.getPointPosition(0, radius);
+               ctx.moveTo(pointPosition.x, pointPosition.y);
+
+               for (var i = 1; i < valueCount; i++) {
+                       pointPosition = scale.getPointPosition(i, radius);
+                       ctx.lineTo(pointPosition.x, pointPosition.y);
                }
+       }
+       ctx.closePath();
+       ctx.stroke();
+       ctx.restore();
+}
+
+function numberOrZero(param) {
+       return helpers.isNumber(param) ? param : 0;
+}
+
+module.exports = LinearScaleBase.extend({
+       setDimensions: function() {
+               var me = this;
+
+               // Set the unconstrained dimension before label rotation
+               me.width = me.maxWidth;
+               me.height = me.maxHeight;
+               me.paddingTop = getTickBackdropHeight(me.options) / 2;
+               me.xCenter = Math.floor(me.width / 2);
+               me.yCenter = Math.floor((me.height - me.paddingTop) / 2);
+               me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2;
+       },
+
+       determineDataLimits: function() {
+               var me = this;
+               var chart = me.chart;
+               var min = Number.POSITIVE_INFINITY;
+               var max = Number.NEGATIVE_INFINITY;
+
+               helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
+                       if (chart.isDatasetVisible(datasetIndex)) {
+                               var meta = chart.getDatasetMeta(datasetIndex);
+
+                               helpers.each(dataset.data, function(rawValue, index) {
+                                       var value = +me.getRightValue(rawValue);
+                                       if (isNaN(value) || meta.data[index].hidden) {
+                                               return;
+                                       }
 
-               var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
+                                       min = Math.min(value, min);
+                                       max = Math.max(value, max);
+                               });
+                       }
+               });
 
-               // Point Label Font
-               var plFont = helpers.options._parseFont(pointLabelOpts);
+               me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
+               me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
 
-               ctx.font = plFont.string;
-               ctx.textBaseline = 'middle';
+               // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
+               me.handleTickRangeOptions();
+       },
 
-               for (var i = getValueCount(scale) - 1; i >= 0; i--) {
-                       if (angleLineOpts.display && lineWidth && lineColor) {
-                               var outerPosition = scale.getPointPosition(i, outerDistance);
-                               ctx.beginPath();
-                               ctx.moveTo(scale.xCenter, scale.yCenter);
-                               ctx.lineTo(outerPosition.x, outerPosition.y);
-                               ctx.stroke();
-                       }
+       // Returns the maximum number of ticks based on the scale dimension
+       _computeTickLimit: function() {
+               return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
+       },
 
-                       if (pointLabelOpts.display) {
-                               // Extra pixels out for some label spacing
-                               var extra = (i === 0 ? tickBackdropHeight / 2 : 0);
-                               var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
+       convertTicksToLabels: function() {
+               var me = this;
 
-                               // Keep this in loop since we may support array properties here
-                               var pointLabelFontColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, defaults.global.defaultFontColor);
-                               ctx.fillStyle = pointLabelFontColor;
+               LinearScaleBase.prototype.convertTicksToLabels.call(me);
 
-                               var angleRadians = scale.getIndexAngle(i);
-                               var angle = helpers.toDegrees(angleRadians);
-                               ctx.textAlign = getTextAlignForAngle(angle);
-                               adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
-                               fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight);
-                       }
-               }
-               ctx.restore();
-       }
+               // Point labels
+               me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
+       },
 
-       function drawRadiusLine(scale, gridLineOpts, radius, index) {
-               var ctx = scale.ctx;
-               var circular = gridLineOpts.circular;
-               var valueCount = getValueCount(scale);
-               var lineColor = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
-               var lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
-               var pointPosition;
+       getLabelForIndex: function(index, datasetIndex) {
+               return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
+       },
 
-               if ((!circular && !valueCount) || !lineColor || !lineWidth) {
-                       return;
-               }
-
-               ctx.save();
-               ctx.strokeStyle = lineColor;
-               ctx.lineWidth = lineWidth;
-               if (ctx.setLineDash) {
-                       ctx.setLineDash(gridLineOpts.borderDash || []);
-                       ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0;
-               }
+       fit: function() {
+               var me = this;
+               var opts = me.options;
 
-               ctx.beginPath();
-               if (circular) {
-                       // Draw circular arcs between the points
-                       ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
+               if (opts.display && opts.pointLabels.display) {
+                       fitWithPointLabels(me);
                } else {
-                       // Draw straight lines connecting each index
-                       pointPosition = scale.getPointPosition(0, radius);
-                       ctx.moveTo(pointPosition.x, pointPosition.y);
+                       me.setCenterPoint(0, 0, 0, 0);
+               }
+       },
 
-                       for (var i = 1; i < valueCount; i++) {
-                               pointPosition = scale.getPointPosition(i, radius);
-                               ctx.lineTo(pointPosition.x, pointPosition.y);
-                       }
+       /**
+        * Set radius reductions and determine new radius and center point
+        * @private
+        */
+       setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
+               var me = this;
+               var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
+               var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
+               var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
+               var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b);
+
+               radiusReductionLeft = numberOrZero(radiusReductionLeft);
+               radiusReductionRight = numberOrZero(radiusReductionRight);
+               radiusReductionTop = numberOrZero(radiusReductionTop);
+               radiusReductionBottom = numberOrZero(radiusReductionBottom);
+
+               me.drawingArea = Math.min(
+                       Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
+                       Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
+               me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
+       },
+
+       setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
+               var me = this;
+               var maxRight = me.width - rightMovement - me.drawingArea;
+               var maxLeft = leftMovement + me.drawingArea;
+               var maxTop = topMovement + me.drawingArea;
+               var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea;
+
+               me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left);
+               me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop);
+       },
+
+       getIndexAngle: function(index) {
+               var angleMultiplier = (Math.PI * 2) / getValueCount(this);
+               var startAngle = this.chart.options && this.chart.options.startAngle ?
+                       this.chart.options.startAngle :
+                       0;
+
+               var startAngleRadians = startAngle * Math.PI * 2 / 360;
+
+               // Start from the top instead of right, so remove a quarter of the circle
+               return index * angleMultiplier + startAngleRadians;
+       },
+
+       getDistanceFromCenterForValue: function(value) {
+               var me = this;
+
+               if (value === null) {
+                       return 0; // null always in center
                }
-               ctx.closePath();
-               ctx.stroke();
-               ctx.restore();
-       }
 
-       function numberOrZero(param) {
-               return helpers.isNumber(param) ? param : 0;
-       }
+               // Take into account half font size + the yPadding of the top value
+               var scalingFactor = me.drawingArea / (me.max - me.min);
+               if (me.options.ticks.reverse) {
+                       return (me.max - value) * scalingFactor;
+               }
+               return (value - me.min) * scalingFactor;
+       },
 
-       var LinearRadialScale = Chart.LinearScaleBase.extend({
-               setDimensions: function() {
-                       var me = this;
-
-                       // Set the unconstrained dimension before label rotation
-                       me.width = me.maxWidth;
-                       me.height = me.maxHeight;
-                       me.paddingTop = getTickBackdropHeight(me.options) / 2;
-                       me.xCenter = Math.floor(me.width / 2);
-                       me.yCenter = Math.floor((me.height - me.paddingTop) / 2);
-                       me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2;
-               },
-               determineDataLimits: function() {
-                       var me = this;
-                       var chart = me.chart;
-                       var min = Number.POSITIVE_INFINITY;
-                       var max = Number.NEGATIVE_INFINITY;
-
-                       helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
-                               if (chart.isDatasetVisible(datasetIndex)) {
-                                       var meta = chart.getDatasetMeta(datasetIndex);
-
-                                       helpers.each(dataset.data, function(rawValue, index) {
-                                               var value = +me.getRightValue(rawValue);
-                                               if (isNaN(value) || meta.data[index].hidden) {
-                                                       return;
-                                               }
+       getPointPosition: function(index, distanceFromCenter) {
+               var me = this;
+               var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
+               return {
+                       x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter,
+                       y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter
+               };
+       },
+
+       getPointPositionForValue: function(index, value) {
+               return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
+       },
+
+       getBasePosition: function() {
+               var me = this;
+               var min = me.min;
+               var max = me.max;
+
+               return me.getPointPositionForValue(0,
+                       me.beginAtZero ? 0 :
+                       min < 0 && max < 0 ? max :
+                       min > 0 && max > 0 ? min :
+                       0);
+       },
+
+       draw: function() {
+               var me = this;
+               var opts = me.options;
+               var gridLineOpts = opts.gridLines;
+               var tickOpts = opts.ticks;
+               var valueOrDefault = helpers.valueOrDefault;
 
-                                               min = Math.min(value, min);
-                                               max = Math.max(value, max);
-                                       });
-                               }
-                       });
+               if (opts.display) {
+                       var ctx = me.ctx;
+                       var startAngle = this.getIndexAngle(0);
+                       var tickFont = helpers.options._parseFont(tickOpts);
 
-                       me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
-                       me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
-
-                       // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
-                       me.handleTickRangeOptions();
-               },
-               // Returns the maximum number of ticks based on the scale dimension
-               _computeTickLimit: function() {
-                       return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
-               },
-               convertTicksToLabels: function() {
-                       var me = this;
-
-                       Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
-
-                       // Point labels
-                       me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
-               },
-               getLabelForIndex: function(index, datasetIndex) {
-                       return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
-               },
-               fit: function() {
-                       var me = this;
-                       var opts = me.options;
-
-                       if (opts.display && opts.pointLabels.display) {
-                               fitWithPointLabels(me);
-                       } else {
-                               me.setCenterPoint(0, 0, 0, 0);
-                       }
-               },
-               /**
-                * Set radius reductions and determine new radius and center point
-                * @private
-                */
-               setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
-                       var me = this;
-                       var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
-                       var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
-                       var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
-                       var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b);
-
-                       radiusReductionLeft = numberOrZero(radiusReductionLeft);
-                       radiusReductionRight = numberOrZero(radiusReductionRight);
-                       radiusReductionTop = numberOrZero(radiusReductionTop);
-                       radiusReductionBottom = numberOrZero(radiusReductionBottom);
-
-                       me.drawingArea = Math.min(
-                               Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
-                               Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
-                       me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
-               },
-               setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
-                       var me = this;
-                       var maxRight = me.width - rightMovement - me.drawingArea;
-                       var maxLeft = leftMovement + me.drawingArea;
-                       var maxTop = topMovement + me.drawingArea;
-                       var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea;
-
-                       me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left);
-                       me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop);
-               },
-
-               getIndexAngle: function(index) {
-                       var angleMultiplier = (Math.PI * 2) / getValueCount(this);
-                       var startAngle = this.chart.options && this.chart.options.startAngle ?
-                               this.chart.options.startAngle :
-                               0;
-
-                       var startAngleRadians = startAngle * Math.PI * 2 / 360;
-
-                       // Start from the top instead of right, so remove a quarter of the circle
-                       return index * angleMultiplier + startAngleRadians;
-               },
-               getDistanceFromCenterForValue: function(value) {
-                       var me = this;
-
-                       if (value === null) {
-                               return 0; // null always in center
+                       if (opts.angleLines.display || opts.pointLabels.display) {
+                               drawPointLabels(me);
                        }
 
-                       // Take into account half font size + the yPadding of the top value
-                       var scalingFactor = me.drawingArea / (me.max - me.min);
-                       if (me.options.ticks.reverse) {
-                               return (me.max - value) * scalingFactor;
-                       }
-                       return (value - me.min) * scalingFactor;
-               },
-               getPointPosition: function(index, distanceFromCenter) {
-                       var me = this;
-                       var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
-                       return {
-                               x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter,
-                               y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter
-                       };
-               },
-               getPointPositionForValue: function(index, value) {
-                       return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
-               },
-
-               getBasePosition: function() {
-                       var me = this;
-                       var min = me.min;
-                       var max = me.max;
-
-                       return me.getPointPositionForValue(0,
-                               me.beginAtZero ? 0 :
-                               min < 0 && max < 0 ? max :
-                               min > 0 && max > 0 ? min :
-                               0);
-               },
-
-               draw: function() {
-                       var me = this;
-                       var opts = me.options;
-                       var gridLineOpts = opts.gridLines;
-                       var tickOpts = opts.ticks;
-                       var valueOrDefault = helpers.valueOrDefault;
-
-                       if (opts.display) {
-                               var ctx = me.ctx;
-                               var startAngle = this.getIndexAngle(0);
-                               var tickFont = helpers.options._parseFont(tickOpts);
-
-                               if (opts.angleLines.display || opts.pointLabels.display) {
-                                       drawPointLabels(me);
-                               }
+                       helpers.each(me.ticks, function(label, index) {
+                               // Don't draw a centre value (if it is minimum)
+                               if (index > 0 || tickOpts.reverse) {
+                                       var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
 
-                               helpers.each(me.ticks, function(label, index) {
-                                       // Don't draw a centre value (if it is minimum)
-                                       if (index > 0 || tickOpts.reverse) {
-                                               var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
+                                       // Draw circular lines around the scale
+                                       if (gridLineOpts.display && index !== 0) {
+                                               drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
+                                       }
 
-                                               // Draw circular lines around the scale
-                                               if (gridLineOpts.display && index !== 0) {
-                                                       drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
+                                       if (tickOpts.display) {
+                                               var tickFontColor = valueOrDefault(tickOpts.fontColor, defaults.global.defaultFontColor);
+                                               ctx.font = tickFont.string;
+
+                                               ctx.save();
+                                               ctx.translate(me.xCenter, me.yCenter);
+                                               ctx.rotate(startAngle);
+
+                                               if (tickOpts.showLabelBackdrop) {
+                                                       var labelWidth = ctx.measureText(label).width;
+                                                       ctx.fillStyle = tickOpts.backdropColor;
+                                                       ctx.fillRect(
+                                                               -labelWidth / 2 - tickOpts.backdropPaddingX,
+                                                               -yCenterOffset - tickFont.size / 2 - tickOpts.backdropPaddingY,
+                                                               labelWidth + tickOpts.backdropPaddingX * 2,
+                                                               tickFont.size + tickOpts.backdropPaddingY * 2
+                                                       );
                                                }
 
-                                               if (tickOpts.display) {
-                                                       var tickFontColor = valueOrDefault(tickOpts.fontColor, defaults.global.defaultFontColor);
-                                                       ctx.font = tickFont.string;
-
-                                                       ctx.save();
-                                                       ctx.translate(me.xCenter, me.yCenter);
-                                                       ctx.rotate(startAngle);
-
-                                                       if (tickOpts.showLabelBackdrop) {
-                                                               var labelWidth = ctx.measureText(label).width;
-                                                               ctx.fillStyle = tickOpts.backdropColor;
-                                                               ctx.fillRect(
-                                                                       -labelWidth / 2 - tickOpts.backdropPaddingX,
-                                                                       -yCenterOffset - tickFont.size / 2 - tickOpts.backdropPaddingY,
-                                                                       labelWidth + tickOpts.backdropPaddingX * 2,
-                                                                       tickFont.size + tickOpts.backdropPaddingY * 2
-                                                               );
-                                                       }
-
-                                                       ctx.textAlign = 'center';
-                                                       ctx.textBaseline = 'middle';
-                                                       ctx.fillStyle = tickFontColor;
-                                                       ctx.fillText(label, 0, -yCenterOffset);
-                                                       ctx.restore();
-                                               }
+                                               ctx.textAlign = 'center';
+                                               ctx.textBaseline = 'middle';
+                                               ctx.fillStyle = tickFontColor;
+                                               ctx.fillText(label, 0, -yCenterOffset);
+                                               ctx.restore();
                                        }
-                               });
-                       }
+                               }
+                       });
                }
-       });
+       }
+});
 
-       scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);
-};
+// INTERNAL: static default options, registered in src/chart.js
+module.exports._defaults = defaultConfig;
index 3c698f57db08600f820b35413212e7f595fb44cd..c7c49eb6b1b770cf717d72703f65a9fd7dc60e02 100644 (file)
@@ -5,7 +5,6 @@ var moment = require('moment');
 var defaults = require('../core/core.defaults');
 var helpers = require('../helpers/index');
 var Scale = require('../core/core.scale');
-var scaleService = require('../core/core.scaleService');
 
 // Integer constants are from the ES6 spec.
 var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
@@ -426,369 +425,367 @@ function determineLabelFormat(data, timeOpts) {
        return 'MMM D, YYYY';
 }
 
-module.exports = function() {
-
-       var defaultConfig = {
-               position: 'bottom',
+var defaultConfig = {
+       position: 'bottom',
+
+       /**
+        * Data distribution along the scale:
+        * - 'linear': data are spread according to their time (distances can vary),
+        * - 'series': data are spread at the same distance from each other.
+        * @see https://github.com/chartjs/Chart.js/pull/4507
+        * @since 2.7.0
+        */
+       distribution: 'linear',
+
+       /**
+        * Scale boundary strategy (bypassed by min/max time options)
+        * - `data`: make sure data are fully visible, ticks outside are removed
+        * - `ticks`: make sure ticks are fully visible, data outside are truncated
+        * @see https://github.com/chartjs/Chart.js/pull/4556
+        * @since 2.7.0
+        */
+       bounds: 'data',
+
+       time: {
+               parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
+               format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from https://momentjs.com/docs/#/parsing/string-format/
+               unit: false, // false == automatic or override with week, month, year, etc.
+               round: false, // none, or override with week, month, year, etc.
+               displayFormat: false, // DEPRECATED
+               isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/
+               minUnit: 'millisecond',
+
+               // defaults to unit's corresponding unitFormat below or override using pattern string from https://momentjs.com/docs/#/displaying/format/
+               displayFormats: {
+                       millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
+                       second: 'h:mm:ss a', // 11:20:01 AM
+                       minute: 'h:mm a', // 11:20 AM
+                       hour: 'hA', // 5PM
+                       day: 'MMM D', // Sep 4
+                       week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
+                       month: 'MMM YYYY', // Sept 2015
+                       quarter: '[Q]Q - YYYY', // Q3
+                       year: 'YYYY' // 2015
+               },
+       },
+       ticks: {
+               autoSkip: false,
 
                /**
-                * Data distribution along the scale:
-                * - 'linear': data are spread according to their time (distances can vary),
-                * - 'series': data are spread at the same distance from each other.
+                * Ticks generation input values:
+                * - 'auto': generates "optimal" ticks based on scale size and time options.
+                * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
+                * - 'labels': generates ticks from user given `data.labels` values ONLY.
                 * @see https://github.com/chartjs/Chart.js/pull/4507
                 * @since 2.7.0
                 */
-               distribution: 'linear',
+               source: 'auto',
 
-               /**
-                * Scale boundary strategy (bypassed by min/max time options)
-                * - `data`: make sure data are fully visible, ticks outside are removed
-                * - `ticks`: make sure ticks are fully visible, data outside are truncated
-                * @see https://github.com/chartjs/Chart.js/pull/4556
-                * @since 2.7.0
-                */
-               bounds: 'data',
-
-               time: {
-                       parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
-                       format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from https://momentjs.com/docs/#/parsing/string-format/
-                       unit: false, // false == automatic or override with week, month, year, etc.
-                       round: false, // none, or override with week, month, year, etc.
-                       displayFormat: false, // DEPRECATED
-                       isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/
-                       minUnit: 'millisecond',
-
-                       // defaults to unit's corresponding unitFormat below or override using pattern string from https://momentjs.com/docs/#/displaying/format/
-                       displayFormats: {
-                               millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
-                               second: 'h:mm:ss a', // 11:20:01 AM
-                               minute: 'h:mm a', // 11:20 AM
-                               hour: 'hA', // 5PM
-                               day: 'MMM D', // Sep 4
-                               week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
-                               month: 'MMM YYYY', // Sept 2015
-                               quarter: '[Q]Q - YYYY', // Q3
-                               year: 'YYYY' // 2015
-                       },
-               },
-               ticks: {
-                       autoSkip: false,
-
-                       /**
-                        * Ticks generation input values:
-                        * - 'auto': generates "optimal" ticks based on scale size and time options.
-                        * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
-                        * - 'labels': generates ticks from user given `data.labels` values ONLY.
-                        * @see https://github.com/chartjs/Chart.js/pull/4507
-                        * @since 2.7.0
-                        */
-                       source: 'auto',
-
-                       major: {
-                               enabled: false
-                       }
+               major: {
+                       enabled: false
                }
-       };
+       }
+};
 
-       var TimeScale = Scale.extend({
-               initialize: function() {
-                       if (!moment) {
-                               throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');
-                       }
+module.exports = Scale.extend({
+       initialize: function() {
+               if (!moment) {
+                       throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');
+               }
 
-                       this.mergeTicksOptions();
+               this.mergeTicksOptions();
 
-                       Scale.prototype.initialize.call(this);
-               },
+               Scale.prototype.initialize.call(this);
+       },
 
-               update: function() {
-                       var me = this;
-                       var options = me.options;
+       update: function() {
+               var me = this;
+               var options = me.options;
 
-                       // DEPRECATIONS: output a message only one time per update
-                       if (options.time && options.time.format) {
-                               console.warn('options.time.format is deprecated and replaced by options.time.parser.');
-                       }
+               // DEPRECATIONS: output a message only one time per update
+               if (options.time && options.time.format) {
+                       console.warn('options.time.format is deprecated and replaced by options.time.parser.');
+               }
 
-                       return Scale.prototype.update.apply(me, arguments);
-               },
+               return Scale.prototype.update.apply(me, arguments);
+       },
 
-               /**
-                * Allows data to be referenced via 't' attribute
-                */
-               getRightValue: function(rawValue) {
-                       if (rawValue && rawValue.t !== undefined) {
-                               rawValue = rawValue.t;
-                       }
-                       return Scale.prototype.getRightValue.call(this, rawValue);
-               },
+       /**
+        * Allows data to be referenced via 't' attribute
+        */
+       getRightValue: function(rawValue) {
+               if (rawValue && rawValue.t !== undefined) {
+                       rawValue = rawValue.t;
+               }
+               return Scale.prototype.getRightValue.call(this, rawValue);
+       },
 
-               determineDataLimits: function() {
-                       var me = this;
-                       var chart = me.chart;
-                       var timeOpts = me.options.time;
-                       var unit = timeOpts.unit || 'day';
-                       var min = MAX_INTEGER;
-                       var max = MIN_INTEGER;
-                       var timestamps = [];
-                       var datasets = [];
-                       var labels = [];
-                       var i, j, ilen, jlen, data, timestamp;
-                       var dataLabels = chart.data.labels || [];
-
-                       // Convert labels to timestamps
-                       for (i = 0, ilen = dataLabels.length; i < ilen; ++i) {
-                               labels.push(parse(dataLabels[i], me));
-                       }
+       determineDataLimits: function() {
+               var me = this;
+               var chart = me.chart;
+               var timeOpts = me.options.time;
+               var unit = timeOpts.unit || 'day';
+               var min = MAX_INTEGER;
+               var max = MIN_INTEGER;
+               var timestamps = [];
+               var datasets = [];
+               var labels = [];
+               var i, j, ilen, jlen, data, timestamp;
+               var dataLabels = chart.data.labels || [];
+
+               // Convert labels to timestamps
+               for (i = 0, ilen = dataLabels.length; i < ilen; ++i) {
+                       labels.push(parse(dataLabels[i], me));
+               }
+
+               // Convert data to timestamps
+               for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
+                       if (chart.isDatasetVisible(i)) {
+                               data = chart.data.datasets[i].data;
+
+                               // Let's consider that all data have the same format.
+                               if (helpers.isObject(data[0])) {
+                                       datasets[i] = [];
 
-                       // Convert data to timestamps
-                       for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
-                               if (chart.isDatasetVisible(i)) {
-                                       data = chart.data.datasets[i].data;
-
-                                       // Let's consider that all data have the same format.
-                                       if (helpers.isObject(data[0])) {
-                                               datasets[i] = [];
-
-                                               for (j = 0, jlen = data.length; j < jlen; ++j) {
-                                                       timestamp = parse(data[j], me);
-                                                       timestamps.push(timestamp);
-                                                       datasets[i][j] = timestamp;
-                                               }
-                                       } else {
-                                               for (j = 0, jlen = labels.length; j < jlen; ++j) {
-                                                       timestamps.push(labels[j]);
-                                               }
-                                               datasets[i] = labels.slice(0);
+                                       for (j = 0, jlen = data.length; j < jlen; ++j) {
+                                               timestamp = parse(data[j], me);
+                                               timestamps.push(timestamp);
+                                               datasets[i][j] = timestamp;
                                        }
                                } else {
-                                       datasets[i] = [];
+                                       for (j = 0, jlen = labels.length; j < jlen; ++j) {
+                                               timestamps.push(labels[j]);
+                                       }
+                                       datasets[i] = labels.slice(0);
                                }
+                       } else {
+                               datasets[i] = [];
                        }
+               }
 
-                       if (labels.length) {
-                               // Sort labels **after** data have been converted
-                               labels = arrayUnique(labels).sort(sorter);
-                               min = Math.min(min, labels[0]);
-                               max = Math.max(max, labels[labels.length - 1]);
-                       }
+               if (labels.length) {
+                       // Sort labels **after** data have been converted
+                       labels = arrayUnique(labels).sort(sorter);
+                       min = Math.min(min, labels[0]);
+                       max = Math.max(max, labels[labels.length - 1]);
+               }
 
-                       if (timestamps.length) {
-                               timestamps = arrayUnique(timestamps).sort(sorter);
-                               min = Math.min(min, timestamps[0]);
-                               max = Math.max(max, timestamps[timestamps.length - 1]);
-                       }
+               if (timestamps.length) {
+                       timestamps = arrayUnique(timestamps).sort(sorter);
+                       min = Math.min(min, timestamps[0]);
+                       max = Math.max(max, timestamps[timestamps.length - 1]);
+               }
 
-                       min = parse(timeOpts.min, me) || min;
-                       max = parse(timeOpts.max, me) || max;
-
-                       // In case there is no valid min/max, set limits based on unit time option
-                       min = min === MAX_INTEGER ? +moment().startOf(unit) : min;
-                       max = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max;
-
-                       // Make sure that max is strictly higher than min (required by the lookup table)
-                       me.min = Math.min(min, max);
-                       me.max = Math.max(min + 1, max);
-
-                       // PRIVATE
-                       me._horizontal = me.isHorizontal();
-                       me._table = [];
-                       me._timestamps = {
-                               data: timestamps,
-                               datasets: datasets,
-                               labels: labels
-                       };
-               },
+               min = parse(timeOpts.min, me) || min;
+               max = parse(timeOpts.max, me) || max;
+
+               // In case there is no valid min/max, set limits based on unit time option
+               min = min === MAX_INTEGER ? +moment().startOf(unit) : min;
+               max = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max;
+
+               // Make sure that max is strictly higher than min (required by the lookup table)
+               me.min = Math.min(min, max);
+               me.max = Math.max(min + 1, max);
+
+               // PRIVATE
+               me._horizontal = me.isHorizontal();
+               me._table = [];
+               me._timestamps = {
+                       data: timestamps,
+                       datasets: datasets,
+                       labels: labels
+               };
+       },
 
-               buildTicks: function() {
-                       var me = this;
-                       var min = me.min;
-                       var max = me.max;
-                       var options = me.options;
-                       var timeOpts = options.time;
-                       var timestamps = [];
-                       var ticks = [];
-                       var i, ilen, timestamp;
-
-                       switch (options.ticks.source) {
-                       case 'data':
-                               timestamps = me._timestamps.data;
-                               break;
-                       case 'labels':
-                               timestamps = me._timestamps.labels;
-                               break;
-                       case 'auto':
-                       default:
-                               timestamps = generate(min, max, me.getLabelCapacity(min), options);
-                       }
+       buildTicks: function() {
+               var me = this;
+               var min = me.min;
+               var max = me.max;
+               var options = me.options;
+               var timeOpts = options.time;
+               var timestamps = [];
+               var ticks = [];
+               var i, ilen, timestamp;
+
+               switch (options.ticks.source) {
+               case 'data':
+                       timestamps = me._timestamps.data;
+                       break;
+               case 'labels':
+                       timestamps = me._timestamps.labels;
+                       break;
+               case 'auto':
+               default:
+                       timestamps = generate(min, max, me.getLabelCapacity(min), options);
+               }
 
-                       if (options.bounds === 'ticks' && timestamps.length) {
-                               min = timestamps[0];
-                               max = timestamps[timestamps.length - 1];
-                       }
+               if (options.bounds === 'ticks' && timestamps.length) {
+                       min = timestamps[0];
+                       max = timestamps[timestamps.length - 1];
+               }
 
-                       // Enforce limits with user min/max options
-                       min = parse(timeOpts.min, me) || min;
-                       max = parse(timeOpts.max, me) || max;
+               // Enforce limits with user min/max options
+               min = parse(timeOpts.min, me) || min;
+               max = parse(timeOpts.max, me) || max;
 
-                       // Remove ticks outside the min/max range
-                       for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
-                               timestamp = timestamps[i];
-                               if (timestamp >= min && timestamp <= max) {
-                                       ticks.push(timestamp);
-                               }
+               // Remove ticks outside the min/max range
+               for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
+                       timestamp = timestamps[i];
+                       if (timestamp >= min && timestamp <= max) {
+                               ticks.push(timestamp);
                        }
+               }
 
-                       me.min = min;
-                       me.max = max;
+               me.min = min;
+               me.max = max;
 
-                       // PRIVATE
-                       me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max);
-                       me._majorUnit = determineMajorUnit(me._unit);
-                       me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
-                       me._offsets = computeOffsets(me._table, ticks, min, max, options);
-                       me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts);
+               // PRIVATE
+               me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max);
+               me._majorUnit = determineMajorUnit(me._unit);
+               me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
+               me._offsets = computeOffsets(me._table, ticks, min, max, options);
+               me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts);
 
-                       if (options.ticks.reverse) {
-                               ticks.reverse();
-                       }
+               if (options.ticks.reverse) {
+                       ticks.reverse();
+               }
 
-                       return ticksFromTimestamps(ticks, me._majorUnit);
-               },
+               return ticksFromTimestamps(ticks, me._majorUnit);
+       },
 
-               getLabelForIndex: function(index, datasetIndex) {
-                       var me = this;
-                       var data = me.chart.data;
-                       var timeOpts = me.options.time;
-                       var label = data.labels && index < data.labels.length ? data.labels[index] : '';
-                       var value = data.datasets[datasetIndex].data[index];
+       getLabelForIndex: function(index, datasetIndex) {
+               var me = this;
+               var data = me.chart.data;
+               var timeOpts = me.options.time;
+               var label = data.labels && index < data.labels.length ? data.labels[index] : '';
+               var value = data.datasets[datasetIndex].data[index];
 
-                       if (helpers.isObject(value)) {
-                               label = me.getRightValue(value);
-                       }
-                       if (timeOpts.tooltipFormat) {
-                               return momentify(label, timeOpts).format(timeOpts.tooltipFormat);
-                       }
-                       if (typeof label === 'string') {
-                               return label;
-                       }
+               if (helpers.isObject(value)) {
+                       label = me.getRightValue(value);
+               }
+               if (timeOpts.tooltipFormat) {
+                       return momentify(label, timeOpts).format(timeOpts.tooltipFormat);
+               }
+               if (typeof label === 'string') {
+                       return label;
+               }
 
-                       return momentify(label, timeOpts).format(me._labelFormat);
-               },
+               return momentify(label, timeOpts).format(me._labelFormat);
+       },
 
-               /**
-                * Function to format an individual tick mark
-                * @private
-                */
-               tickFormatFunction: function(tick, index, ticks, formatOverride) {
-                       var me = this;
-                       var options = me.options;
-                       var time = tick.valueOf();
-                       var formats = options.time.displayFormats;
-                       var minorFormat = formats[me._unit];
-                       var majorUnit = me._majorUnit;
-                       var majorFormat = formats[majorUnit];
-                       var majorTime = tick.clone().startOf(majorUnit).valueOf();
-                       var majorTickOpts = options.ticks.major;
-                       var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
-                       var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat);
-                       var tickOpts = major ? majorTickOpts : options.ticks.minor;
-                       var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);
-
-                       return formatter ? formatter(label, index, ticks) : label;
-               },
+       /**
+        * Function to format an individual tick mark
+        * @private
+        */
+       tickFormatFunction: function(tick, index, ticks, formatOverride) {
+               var me = this;
+               var options = me.options;
+               var time = tick.valueOf();
+               var formats = options.time.displayFormats;
+               var minorFormat = formats[me._unit];
+               var majorUnit = me._majorUnit;
+               var majorFormat = formats[majorUnit];
+               var majorTime = tick.clone().startOf(majorUnit).valueOf();
+               var majorTickOpts = options.ticks.major;
+               var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
+               var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat);
+               var tickOpts = major ? majorTickOpts : options.ticks.minor;
+               var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);
+
+               return formatter ? formatter(label, index, ticks) : label;
+       },
 
-               convertTicksToLabels: function(ticks) {
-                       var labels = [];
-                       var i, ilen;
+       convertTicksToLabels: function(ticks) {
+               var labels = [];
+               var i, ilen;
 
-                       for (i = 0, ilen = ticks.length; i < ilen; ++i) {
-                               labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));
-                       }
+               for (i = 0, ilen = ticks.length; i < ilen; ++i) {
+                       labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));
+               }
 
-                       return labels;
-               },
+               return labels;
+       },
 
-               /**
-                * @private
-                */
-               getPixelForOffset: function(time) {
-                       var me = this;
-                       var isReverse = me.options.ticks.reverse;
-                       var size = me._horizontal ? me.width : me.height;
-                       var start = me._horizontal ? isReverse ? me.right : me.left : isReverse ? me.bottom : me.top;
-                       var pos = interpolate(me._table, 'time', time, 'pos');
-                       var offset = size * (me._offsets.start + pos) / (me._offsets.start + 1 + me._offsets.end);
-
-                       return isReverse ? start - offset : start + offset;
-               },
+       /**
+        * @private
+        */
+       getPixelForOffset: function(time) {
+               var me = this;
+               var isReverse = me.options.ticks.reverse;
+               var size = me._horizontal ? me.width : me.height;
+               var start = me._horizontal ? isReverse ? me.right : me.left : isReverse ? me.bottom : me.top;
+               var pos = interpolate(me._table, 'time', time, 'pos');
+               var offset = size * (me._offsets.start + pos) / (me._offsets.start + 1 + me._offsets.end);
+
+               return isReverse ? start - offset : start + offset;
+       },
 
-               getPixelForValue: function(value, index, datasetIndex) {
-                       var me = this;
-                       var time = null;
+       getPixelForValue: function(value, index, datasetIndex) {
+               var me = this;
+               var time = null;
 
-                       if (index !== undefined && datasetIndex !== undefined) {
-                               time = me._timestamps.datasets[datasetIndex][index];
-                       }
+               if (index !== undefined && datasetIndex !== undefined) {
+                       time = me._timestamps.datasets[datasetIndex][index];
+               }
 
-                       if (time === null) {
-                               time = parse(value, me);
-                       }
+               if (time === null) {
+                       time = parse(value, me);
+               }
 
-                       if (time !== null) {
-                               return me.getPixelForOffset(time);
-                       }
-               },
+               if (time !== null) {
+                       return me.getPixelForOffset(time);
+               }
+       },
 
-               getPixelForTick: function(index) {
-                       var ticks = this.getTicks();
-                       return index >= 0 && index < ticks.length ?
-                               this.getPixelForOffset(ticks[index].value) :
-                               null;
-               },
+       getPixelForTick: function(index) {
+               var ticks = this.getTicks();
+               return index >= 0 && index < ticks.length ?
+                       this.getPixelForOffset(ticks[index].value) :
+                       null;
+       },
 
-               getValueForPixel: function(pixel) {
-                       var me = this;
-                       var size = me._horizontal ? me.width : me.height;
-                       var start = me._horizontal ? me.left : me.top;
-                       var pos = (size ? (pixel - start) / size : 0) * (me._offsets.start + 1 + me._offsets.start) - me._offsets.end;
-                       var time = interpolate(me._table, 'pos', pos, 'time');
+       getValueForPixel: function(pixel) {
+               var me = this;
+               var size = me._horizontal ? me.width : me.height;
+               var start = me._horizontal ? me.left : me.top;
+               var pos = (size ? (pixel - start) / size : 0) * (me._offsets.start + 1 + me._offsets.start) - me._offsets.end;
+               var time = interpolate(me._table, 'pos', pos, 'time');
 
-                       return moment(time);
-               },
+               return moment(time);
+       },
 
-               /**
-                * Crude approximation of what the label width might be
-                * @private
-                */
-               getLabelWidth: function(label) {
-                       var me = this;
-                       var ticksOpts = me.options.ticks;
-                       var tickLabelWidth = me.ctx.measureText(label).width;
-                       var angle = helpers.toRadians(ticksOpts.maxRotation);
-                       var cosRotation = Math.cos(angle);
-                       var sinRotation = Math.sin(angle);
-                       var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);
-
-                       return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
-               },
+       /**
+        * Crude approximation of what the label width might be
+        * @private
+        */
+       getLabelWidth: function(label) {
+               var me = this;
+               var ticksOpts = me.options.ticks;
+               var tickLabelWidth = me.ctx.measureText(label).width;
+               var angle = helpers.toRadians(ticksOpts.maxRotation);
+               var cosRotation = Math.cos(angle);
+               var sinRotation = Math.sin(angle);
+               var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);
+
+               return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
+       },
 
-               /**
-                * @private
-                */
-               getLabelCapacity: function(exampleTime) {
-                       var me = this;
+       /**
+        * @private
+        */
+       getLabelCapacity: function(exampleTime) {
+               var me = this;
 
-                       var formatOverride = me.options.time.displayFormats.millisecond;        // Pick the longest format for guestimation
+               var formatOverride = me.options.time.displayFormats.millisecond;        // Pick the longest format for guestimation
 
-                       var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride);
-                       var tickLabelWidth = me.getLabelWidth(exampleLabel);
-                       var innerWidth = me.isHorizontal() ? me.width : me.height;
+               var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride);
+               var tickLabelWidth = me.getLabelWidth(exampleLabel);
+               var innerWidth = me.isHorizontal() ? me.width : me.height;
 
-                       var capacity = Math.floor(innerWidth / tickLabelWidth);
-                       return capacity > 0 ? capacity : 1;
-               }
-       });
+               var capacity = Math.floor(innerWidth / tickLabelWidth);
+               return capacity > 0 ? capacity : 1;
+       }
+});
 
-       scaleService.registerScaleType('time', TimeScale, defaultConfig);
-};
+// INTERNAL: static default options, registered in src/chart.js
+module.exports._defaults = defaultConfig;
index eeda99f4b0a6241e3c50fba065a4c1c0386ed1e0..822b234b4173056ced42e08815b2d34cf033d815 100644 (file)
@@ -31,6 +31,14 @@ describe('Deprecations', function() {
                                expect(typeof Chart.helpers.aliasPixel).toBe('function');
                        });
                });
+
+               describe('Chart.LinearScaleBase', function() {
+                       it('should be defined and inherit from Chart.Scale', function() {
+                               expect(Chart.LinearScaleBase).toBeDefined();
+                               expect(typeof Chart.LinearScaleBase).toBe('function');
+                               expect(Chart.LinearScaleBase.prototype instanceof Chart.Scale).toBeTruthy();
+                       });
+               });
        });
 
        describe('Version 2.7.3', function() {