mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Remove const me = this pattern (#9646)
This commit is contained in:
parent
183a1a9f67
commit
8e68481ec4
@ -310,10 +310,9 @@ export default class BarController extends DatasetController {
|
||||
* @protected
|
||||
*/
|
||||
getLabelAndValue(index) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const {iScale, vScale} = meta;
|
||||
const parsed = me.getParsed(index);
|
||||
const parsed = this.getParsed(index);
|
||||
const custom = parsed._custom;
|
||||
const value = isFloatBar(custom)
|
||||
? '[' + custom.start + ', ' + custom.end + ']'
|
||||
@ -326,39 +325,35 @@ export default class BarController extends DatasetController {
|
||||
}
|
||||
|
||||
initialize() {
|
||||
const me = this;
|
||||
me.enableOptionSharing = true;
|
||||
this.enableOptionSharing = true;
|
||||
|
||||
super.initialize();
|
||||
|
||||
const meta = me._cachedMeta;
|
||||
meta.stack = me.getDataset().stack;
|
||||
const meta = this._cachedMeta;
|
||||
meta.stack = this.getDataset().stack;
|
||||
}
|
||||
|
||||
update(mode) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
|
||||
me.updateElements(meta.data, 0, meta.data.length, mode);
|
||||
const meta = this._cachedMeta;
|
||||
this.updateElements(meta.data, 0, meta.data.length, mode);
|
||||
}
|
||||
|
||||
updateElements(bars, start, count, mode) {
|
||||
const me = this;
|
||||
const reset = mode === 'reset';
|
||||
const {index, _cachedMeta: {vScale}} = me;
|
||||
const {index, _cachedMeta: {vScale}} = this;
|
||||
const base = vScale.getBasePixel();
|
||||
const horizontal = vScale.isHorizontal();
|
||||
const ruler = me._getRuler();
|
||||
const firstOpts = me.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = me.getSharedOptions(firstOpts);
|
||||
const includeOptions = me.includeOptions(mode, sharedOptions);
|
||||
const ruler = this._getRuler();
|
||||
const firstOpts = this.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = this.getSharedOptions(firstOpts);
|
||||
const includeOptions = this.includeOptions(mode, sharedOptions);
|
||||
|
||||
me.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
this.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
|
||||
for (let i = start; i < start + count; i++) {
|
||||
const parsed = me.getParsed(i);
|
||||
const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : me._calculateBarValuePixels(i);
|
||||
const ipixels = me._calculateBarIndexPixels(i, ruler);
|
||||
const parsed = this.getParsed(i);
|
||||
const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : this._calculateBarValuePixels(i);
|
||||
const ipixels = this._calculateBarIndexPixels(i, ruler);
|
||||
const stack = (parsed._stacks || {})[vScale.axis];
|
||||
|
||||
const properties = {
|
||||
@ -372,10 +367,10 @@ export default class BarController extends DatasetController {
|
||||
};
|
||||
|
||||
if (includeOptions) {
|
||||
properties.options = sharedOptions || me.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);
|
||||
properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);
|
||||
}
|
||||
setBorderSkipped(properties, properties.options || bars[i].options, stack, index);
|
||||
me.updateElement(bars[i], i, properties, mode);
|
||||
this.updateElement(bars[i], i, properties, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,10 +382,9 @@ export default class BarController extends DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_getStacks(last, dataIndex) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const iScale = meta.iScale;
|
||||
const metasets = iScale.getMatchingVisibleMetas(me._type);
|
||||
const metasets = iScale.getMatchingVisibleMetas(this._type);
|
||||
const stacked = iScale.options.stacked;
|
||||
const ilen = metasets.length;
|
||||
const stacks = [];
|
||||
@ -468,15 +462,14 @@ export default class BarController extends DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_getRuler() {
|
||||
const me = this;
|
||||
const opts = me.options;
|
||||
const meta = me._cachedMeta;
|
||||
const opts = this.options;
|
||||
const meta = this._cachedMeta;
|
||||
const iScale = meta.iScale;
|
||||
const pixels = [];
|
||||
let i, ilen;
|
||||
|
||||
for (i = 0, ilen = meta.data.length; i < ilen; ++i) {
|
||||
pixels.push(iScale.getPixelForValue(me.getParsed(i)[iScale.axis], i));
|
||||
pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i));
|
||||
}
|
||||
|
||||
const barThickness = opts.barThickness;
|
||||
@ -487,7 +480,7 @@ export default class BarController extends DatasetController {
|
||||
pixels,
|
||||
start: iScale._startPixel,
|
||||
end: iScale._endPixel,
|
||||
stackCount: me._getStackCount(),
|
||||
stackCount: this._getStackCount(),
|
||||
scale: iScale,
|
||||
grouped: opts.grouped,
|
||||
// bar thickness ratio used for non-grouped bars
|
||||
@ -500,15 +493,14 @@ export default class BarController extends DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_calculateBarValuePixels(index) {
|
||||
const me = this;
|
||||
const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = me;
|
||||
const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = this;
|
||||
const actualBase = baseValue || 0;
|
||||
const parsed = me.getParsed(index);
|
||||
const parsed = this.getParsed(index);
|
||||
const custom = parsed._custom;
|
||||
const floating = isFloatBar(custom);
|
||||
let value = parsed[vScale.axis];
|
||||
let start = 0;
|
||||
let length = _stacked ? me.applyStack(vScale, parsed, _stacked) : value;
|
||||
let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value;
|
||||
let head, size;
|
||||
|
||||
if (length !== value) {
|
||||
@ -529,7 +521,7 @@ export default class BarController extends DatasetController {
|
||||
const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start;
|
||||
let base = vScale.getPixelForValue(startValue);
|
||||
|
||||
if (me.chart.getDataVisibility(index)) {
|
||||
if (this.chart.getDataVisibility(index)) {
|
||||
head = vScale.getPixelForValue(start + length);
|
||||
} else {
|
||||
// When not visible, no height
|
||||
@ -564,24 +556,23 @@ export default class BarController extends DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_calculateBarIndexPixels(index, ruler) {
|
||||
const me = this;
|
||||
const scale = ruler.scale;
|
||||
const options = me.options;
|
||||
const options = this.options;
|
||||
const skipNull = options.skipNull;
|
||||
const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity);
|
||||
let center, size;
|
||||
if (ruler.grouped) {
|
||||
const stackCount = skipNull ? me._getStackCount(index) : ruler.stackCount;
|
||||
const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount;
|
||||
const range = options.barThickness === 'flex'
|
||||
? computeFlexCategoryTraits(index, ruler, options, stackCount)
|
||||
: computeFitCategoryTraits(index, ruler, options, stackCount);
|
||||
|
||||
const stackIndex = me._getStackIndex(me.index, me._cachedMeta.stack, skipNull ? index : undefined);
|
||||
const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined);
|
||||
center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
|
||||
size = Math.min(maxBarThickness, range.chunk * range.ratio);
|
||||
} else {
|
||||
// For non-grouped bar charts, exact pixel values are used
|
||||
center = scale.getPixelForValue(me.getParsed(index)[scale.axis], index);
|
||||
center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index);
|
||||
size = Math.min(maxBarThickness, ruler.min * ruler.ratio);
|
||||
}
|
||||
|
||||
@ -594,16 +585,15 @@ export default class BarController extends DatasetController {
|
||||
}
|
||||
|
||||
draw() {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const vScale = meta.vScale;
|
||||
const rects = meta.data;
|
||||
const ilen = rects.length;
|
||||
let i = 0;
|
||||
|
||||
for (; i < ilen; ++i) {
|
||||
if (me.getParsed(i)[vScale.axis] !== null) {
|
||||
rects[i].draw(me._ctx);
|
||||
if (this.getParsed(i)[vScale.axis] !== null) {
|
||||
rects[i].draw(this._ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,10 +44,9 @@ export default class BubbleController extends DatasetController {
|
||||
* @protected
|
||||
*/
|
||||
getLabelAndValue(index) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const {xScale, yScale} = meta;
|
||||
const parsed = me.getParsed(index);
|
||||
const parsed = this.getParsed(index);
|
||||
const x = xScale.getLabelForValue(parsed.x);
|
||||
const y = yScale.getLabelForValue(parsed.y);
|
||||
const r = parsed._custom;
|
||||
@ -59,26 +58,24 @@ export default class BubbleController extends DatasetController {
|
||||
}
|
||||
|
||||
update(mode) {
|
||||
const me = this;
|
||||
const points = me._cachedMeta.data;
|
||||
const points = this._cachedMeta.data;
|
||||
|
||||
// Update Points
|
||||
me.updateElements(points, 0, points.length, mode);
|
||||
this.updateElements(points, 0, points.length, mode);
|
||||
}
|
||||
|
||||
updateElements(points, start, count, mode) {
|
||||
const me = this;
|
||||
const reset = mode === 'reset';
|
||||
const {iScale, vScale} = me._cachedMeta;
|
||||
const firstOpts = me.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = me.getSharedOptions(firstOpts);
|
||||
const includeOptions = me.includeOptions(mode, sharedOptions);
|
||||
const {iScale, vScale} = this._cachedMeta;
|
||||
const firstOpts = this.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = this.getSharedOptions(firstOpts);
|
||||
const includeOptions = this.includeOptions(mode, sharedOptions);
|
||||
const iAxis = iScale.axis;
|
||||
const vAxis = vScale.axis;
|
||||
|
||||
for (let i = start; i < start + count; i++) {
|
||||
const point = points[i];
|
||||
const parsed = !reset && me.getParsed(i);
|
||||
const parsed = !reset && this.getParsed(i);
|
||||
const properties = {};
|
||||
const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]);
|
||||
const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]);
|
||||
@ -86,17 +83,17 @@ export default class BubbleController extends DatasetController {
|
||||
properties.skip = isNaN(iPixel) || isNaN(vPixel);
|
||||
|
||||
if (includeOptions) {
|
||||
properties.options = me.resolveDataElementOptions(i, point.active ? 'active' : mode);
|
||||
properties.options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);
|
||||
|
||||
if (reset) {
|
||||
properties.options.radius = 0;
|
||||
}
|
||||
}
|
||||
|
||||
me.updateElement(point, i, properties, mode);
|
||||
this.updateElement(point, i, properties, mode);
|
||||
}
|
||||
|
||||
me.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
this.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -94,11 +94,9 @@ export default class DoughnutController extends DatasetController {
|
||||
let min = TAU;
|
||||
let max = -TAU;
|
||||
|
||||
const me = this;
|
||||
|
||||
for (let i = 0; i < me.chart.data.datasets.length; ++i) {
|
||||
if (me.chart.isDatasetVisible(i)) {
|
||||
const controller = me.chart.getDatasetMeta(i).controller;
|
||||
for (let i = 0; i < this.chart.data.datasets.length; ++i) {
|
||||
if (this.chart.isDatasetVisible(i)) {
|
||||
const controller = this.chart.getDatasetMeta(i).controller;
|
||||
const rotation = controller._getRotation();
|
||||
const circumference = controller._getCircumference();
|
||||
|
||||
@ -117,80 +115,77 @@ export default class DoughnutController extends DatasetController {
|
||||
* @param {string} mode
|
||||
*/
|
||||
update(mode) {
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
const chart = this.chart;
|
||||
const {chartArea} = chart;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const arcs = meta.data;
|
||||
const spacing = me.getMaxBorderWidth() + me.getMaxOffset(arcs) + me.options.spacing;
|
||||
const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing;
|
||||
const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);
|
||||
const cutout = Math.min(toPercentage(me.options.cutout, maxSize), 1);
|
||||
const chartWeight = me._getRingWeight(me.index);
|
||||
const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1);
|
||||
const chartWeight = this._getRingWeight(this.index);
|
||||
|
||||
// Compute the maximal rotation & circumference limits.
|
||||
// If we only consider our dataset, this can cause problems when two datasets
|
||||
// are both less than a circle with different rotations (starting angles)
|
||||
const {circumference, rotation} = me._getRotationExtents();
|
||||
const {circumference, rotation} = this._getRotationExtents();
|
||||
const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(rotation, circumference, cutout);
|
||||
const maxWidth = (chartArea.width - spacing) / ratioX;
|
||||
const maxHeight = (chartArea.height - spacing) / ratioY;
|
||||
const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
|
||||
const outerRadius = toDimension(me.options.radius, maxRadius);
|
||||
const outerRadius = toDimension(this.options.radius, maxRadius);
|
||||
const innerRadius = Math.max(outerRadius * cutout, 0);
|
||||
const radiusLength = (outerRadius - innerRadius) / me._getVisibleDatasetWeightTotal();
|
||||
me.offsetX = offsetX * outerRadius;
|
||||
me.offsetY = offsetY * outerRadius;
|
||||
const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal();
|
||||
this.offsetX = offsetX * outerRadius;
|
||||
this.offsetY = offsetY * outerRadius;
|
||||
|
||||
meta.total = me.calculateTotal();
|
||||
meta.total = this.calculateTotal();
|
||||
|
||||
me.outerRadius = outerRadius - radiusLength * me._getRingWeightOffset(me.index);
|
||||
me.innerRadius = Math.max(me.outerRadius - radiusLength * chartWeight, 0);
|
||||
this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index);
|
||||
this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0);
|
||||
|
||||
me.updateElements(arcs, 0, arcs.length, mode);
|
||||
this.updateElements(arcs, 0, arcs.length, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_circumference(i, reset) {
|
||||
const me = this;
|
||||
const opts = me.options;
|
||||
const meta = me._cachedMeta;
|
||||
const circumference = me._getCircumference();
|
||||
const opts = this.options;
|
||||
const meta = this._cachedMeta;
|
||||
const circumference = this._getCircumference();
|
||||
if ((reset && opts.animation.animateRotate) || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) {
|
||||
return 0;
|
||||
}
|
||||
return me.calculateCircumference(meta._parsed[i] * circumference / TAU);
|
||||
return this.calculateCircumference(meta._parsed[i] * circumference / TAU);
|
||||
}
|
||||
|
||||
updateElements(arcs, start, count, mode) {
|
||||
const me = this;
|
||||
const reset = mode === 'reset';
|
||||
const chart = me.chart;
|
||||
const chart = this.chart;
|
||||
const chartArea = chart.chartArea;
|
||||
const opts = chart.options;
|
||||
const animationOpts = opts.animation;
|
||||
const centerX = (chartArea.left + chartArea.right) / 2;
|
||||
const centerY = (chartArea.top + chartArea.bottom) / 2;
|
||||
const animateScale = reset && animationOpts.animateScale;
|
||||
const innerRadius = animateScale ? 0 : me.innerRadius;
|
||||
const outerRadius = animateScale ? 0 : me.outerRadius;
|
||||
const firstOpts = me.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = me.getSharedOptions(firstOpts);
|
||||
const includeOptions = me.includeOptions(mode, sharedOptions);
|
||||
let startAngle = me._getRotation();
|
||||
const innerRadius = animateScale ? 0 : this.innerRadius;
|
||||
const outerRadius = animateScale ? 0 : this.outerRadius;
|
||||
const firstOpts = this.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = this.getSharedOptions(firstOpts);
|
||||
const includeOptions = this.includeOptions(mode, sharedOptions);
|
||||
let startAngle = this._getRotation();
|
||||
let i;
|
||||
|
||||
for (i = 0; i < start; ++i) {
|
||||
startAngle += me._circumference(i, reset);
|
||||
startAngle += this._circumference(i, reset);
|
||||
}
|
||||
|
||||
for (i = start; i < start + count; ++i) {
|
||||
const circumference = me._circumference(i, reset);
|
||||
const circumference = this._circumference(i, reset);
|
||||
const arc = arcs[i];
|
||||
const properties = {
|
||||
x: centerX + me.offsetX,
|
||||
y: centerY + me.offsetY,
|
||||
x: centerX + this.offsetX,
|
||||
y: centerY + this.offsetY,
|
||||
startAngle,
|
||||
endAngle: startAngle + circumference,
|
||||
circumference,
|
||||
@ -198,13 +193,13 @@ export default class DoughnutController extends DatasetController {
|
||||
innerRadius
|
||||
};
|
||||
if (includeOptions) {
|
||||
properties.options = sharedOptions || me.resolveDataElementOptions(i, arc.active ? 'active' : mode);
|
||||
properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode);
|
||||
}
|
||||
startAngle += circumference;
|
||||
|
||||
me.updateElement(arc, i, properties, mode);
|
||||
this.updateElement(arc, i, properties, mode);
|
||||
}
|
||||
me.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
this.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
}
|
||||
|
||||
calculateTotal() {
|
||||
@ -232,9 +227,8 @@ export default class DoughnutController extends DatasetController {
|
||||
}
|
||||
|
||||
getLabelAndValue(index) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const chart = me.chart;
|
||||
const meta = this._cachedMeta;
|
||||
const chart = this.chart;
|
||||
const labels = chart.data.labels || [];
|
||||
const value = formatNumber(meta._parsed[index], chart.options.locale);
|
||||
|
||||
@ -245,9 +239,8 @@ export default class DoughnutController extends DatasetController {
|
||||
}
|
||||
|
||||
getMaxBorderWidth(arcs) {
|
||||
const me = this;
|
||||
let max = 0;
|
||||
const chart = me.chart;
|
||||
const chart = this.chart;
|
||||
let i, ilen, meta, controller, options;
|
||||
|
||||
if (!arcs) {
|
||||
@ -257,7 +250,7 @@ export default class DoughnutController extends DatasetController {
|
||||
meta = chart.getDatasetMeta(i);
|
||||
arcs = meta.data;
|
||||
controller = meta.controller;
|
||||
if (controller !== me) {
|
||||
if (controller !== this) {
|
||||
controller.configure();
|
||||
}
|
||||
break;
|
||||
|
||||
@ -11,15 +11,14 @@ export default class LineController extends DatasetController {
|
||||
}
|
||||
|
||||
update(mode) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const {dataset: line, data: points = [], _dataset} = meta;
|
||||
// @ts-ignore
|
||||
const animationsDisabled = me.chart._animationsDisabled;
|
||||
const animationsDisabled = this.chart._animationsDisabled;
|
||||
let {start, count} = getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
|
||||
|
||||
me._drawStart = start;
|
||||
me._drawCount = count;
|
||||
this._drawStart = start;
|
||||
this._drawCount = count;
|
||||
|
||||
if (scaleRangesChanged(meta)) {
|
||||
start = 0;
|
||||
@ -27,78 +26,76 @@ export default class LineController extends DatasetController {
|
||||
}
|
||||
|
||||
// Update Line
|
||||
line._datasetIndex = me.index;
|
||||
line._datasetIndex = this.index;
|
||||
line._decimated = !!_dataset._decimated;
|
||||
line.points = points;
|
||||
|
||||
const options = me.resolveDatasetElementOptions(mode);
|
||||
if (!me.options.showLine) {
|
||||
const options = this.resolveDatasetElementOptions(mode);
|
||||
if (!this.options.showLine) {
|
||||
options.borderWidth = 0;
|
||||
}
|
||||
options.segment = me.options.segment;
|
||||
me.updateElement(line, undefined, {
|
||||
options.segment = this.options.segment;
|
||||
this.updateElement(line, undefined, {
|
||||
animated: !animationsDisabled,
|
||||
options
|
||||
}, mode);
|
||||
|
||||
// Update Points
|
||||
me.updateElements(points, start, count, mode);
|
||||
this.updateElements(points, start, count, mode);
|
||||
}
|
||||
|
||||
updateElements(points, start, count, mode) {
|
||||
const me = this;
|
||||
const reset = mode === 'reset';
|
||||
const {iScale, vScale, _stacked} = me._cachedMeta;
|
||||
const firstOpts = me.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = me.getSharedOptions(firstOpts);
|
||||
const includeOptions = me.includeOptions(mode, sharedOptions);
|
||||
const {iScale, vScale, _stacked} = this._cachedMeta;
|
||||
const firstOpts = this.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = this.getSharedOptions(firstOpts);
|
||||
const includeOptions = this.includeOptions(mode, sharedOptions);
|
||||
const iAxis = iScale.axis;
|
||||
const vAxis = vScale.axis;
|
||||
const spanGaps = me.options.spanGaps;
|
||||
const spanGaps = this.options.spanGaps;
|
||||
const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
|
||||
const directUpdate = me.chart._animationsDisabled || reset || mode === 'none';
|
||||
let prevParsed = start > 0 && me.getParsed(start - 1);
|
||||
const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';
|
||||
let prevParsed = start > 0 && this.getParsed(start - 1);
|
||||
|
||||
for (let i = start; i < start + count; ++i) {
|
||||
const point = points[i];
|
||||
const parsed = me.getParsed(i);
|
||||
const parsed = this.getParsed(i);
|
||||
const properties = directUpdate ? point : {};
|
||||
const nullData = isNullOrUndef(parsed[vAxis]);
|
||||
const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
|
||||
const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? me.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
|
||||
const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
|
||||
|
||||
properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
|
||||
properties.stop = i > 0 && (parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;
|
||||
properties.parsed = parsed;
|
||||
|
||||
if (includeOptions) {
|
||||
properties.options = sharedOptions || me.resolveDataElementOptions(i, point.active ? 'active' : mode);
|
||||
properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
|
||||
}
|
||||
|
||||
if (!directUpdate) {
|
||||
me.updateElement(point, i, properties, mode);
|
||||
this.updateElement(point, i, properties, mode);
|
||||
}
|
||||
|
||||
prevParsed = parsed;
|
||||
}
|
||||
|
||||
me.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
this.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
getMaxOverflow() {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const dataset = meta.dataset;
|
||||
const border = dataset.options && dataset.options.borderWidth || 0;
|
||||
const data = meta.data || [];
|
||||
if (!data.length) {
|
||||
return border;
|
||||
}
|
||||
const firstPoint = data[0].size(me.resolveDataElementOptions(0));
|
||||
const lastPoint = data[data.length - 1].size(me.resolveDataElementOptions(data.length - 1));
|
||||
const firstPoint = data[0].size(this.resolveDataElementOptions(0));
|
||||
const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
|
||||
return Math.max(border, firstPoint, lastPoint) / 2;
|
||||
}
|
||||
|
||||
|
||||
@ -12,9 +12,8 @@ export default class PolarAreaController extends DatasetController {
|
||||
}
|
||||
|
||||
getLabelAndValue(index) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const chart = me.chart;
|
||||
const meta = this._cachedMeta;
|
||||
const chart = this.chart;
|
||||
const labels = chart.data.labels || [];
|
||||
const value = formatNumber(meta._parsed[index].r, chart.options.locale);
|
||||
|
||||
@ -35,8 +34,7 @@ export default class PolarAreaController extends DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_updateRadius() {
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
const chart = this.chart;
|
||||
const chartArea = chart.chartArea;
|
||||
const opts = chart.options;
|
||||
const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
|
||||
@ -45,33 +43,32 @@ export default class PolarAreaController extends DatasetController {
|
||||
const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
|
||||
const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();
|
||||
|
||||
me.outerRadius = outerRadius - (radiusLength * me.index);
|
||||
me.innerRadius = me.outerRadius - radiusLength;
|
||||
this.outerRadius = outerRadius - (radiusLength * this.index);
|
||||
this.innerRadius = this.outerRadius - radiusLength;
|
||||
}
|
||||
|
||||
updateElements(arcs, start, count, mode) {
|
||||
const me = this;
|
||||
const reset = mode === 'reset';
|
||||
const chart = me.chart;
|
||||
const dataset = me.getDataset();
|
||||
const chart = this.chart;
|
||||
const dataset = this.getDataset();
|
||||
const opts = chart.options;
|
||||
const animationOpts = opts.animation;
|
||||
const scale = me._cachedMeta.rScale;
|
||||
const scale = this._cachedMeta.rScale;
|
||||
const centerX = scale.xCenter;
|
||||
const centerY = scale.yCenter;
|
||||
const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI;
|
||||
let angle = datasetStartAngle;
|
||||
let i;
|
||||
|
||||
const defaultAngle = 360 / me.countVisibleElements();
|
||||
const defaultAngle = 360 / this.countVisibleElements();
|
||||
|
||||
for (i = 0; i < start; ++i) {
|
||||
angle += me._computeAngle(i, mode, defaultAngle);
|
||||
angle += this._computeAngle(i, mode, defaultAngle);
|
||||
}
|
||||
for (i = start; i < start + count; i++) {
|
||||
const arc = arcs[i];
|
||||
let startAngle = angle;
|
||||
let endAngle = angle + me._computeAngle(i, mode, defaultAngle);
|
||||
let endAngle = angle + this._computeAngle(i, mode, defaultAngle);
|
||||
let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(dataset.data[i]) : 0;
|
||||
angle = endAngle;
|
||||
|
||||
@ -91,10 +88,10 @@ export default class PolarAreaController extends DatasetController {
|
||||
outerRadius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
options: me.resolveDataElementOptions(i, arc.active ? 'active' : mode)
|
||||
options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode)
|
||||
};
|
||||
|
||||
me.updateElement(arc, i, properties, mode);
|
||||
this.updateElement(arc, i, properties, mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,9 +6,8 @@ export default class RadarController extends DatasetController {
|
||||
* @protected
|
||||
*/
|
||||
getLabelAndValue(index) {
|
||||
const me = this;
|
||||
const vScale = me._cachedMeta.vScale;
|
||||
const parsed = me.getParsed(index);
|
||||
const vScale = this._cachedMeta.vScale;
|
||||
const parsed = this.getParsed(index);
|
||||
|
||||
return {
|
||||
label: vScale.getLabels()[index],
|
||||
@ -17,8 +16,7 @@ export default class RadarController extends DatasetController {
|
||||
}
|
||||
|
||||
update(mode) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const line = meta.dataset;
|
||||
const points = meta.data || [];
|
||||
const labels = meta.iScale.getLabels();
|
||||
@ -27,8 +25,8 @@ export default class RadarController extends DatasetController {
|
||||
line.points = points;
|
||||
// In resize mode only point locations change, so no need to set the points or options.
|
||||
if (mode !== 'resize') {
|
||||
const options = me.resolveDatasetElementOptions(mode);
|
||||
if (!me.options.showLine) {
|
||||
const options = this.resolveDatasetElementOptions(mode);
|
||||
if (!this.options.showLine) {
|
||||
options.borderWidth = 0;
|
||||
}
|
||||
|
||||
@ -38,22 +36,21 @@ export default class RadarController extends DatasetController {
|
||||
options
|
||||
};
|
||||
|
||||
me.updateElement(line, undefined, properties, mode);
|
||||
this.updateElement(line, undefined, properties, mode);
|
||||
}
|
||||
|
||||
// Update Points
|
||||
me.updateElements(points, 0, points.length, mode);
|
||||
this.updateElements(points, 0, points.length, mode);
|
||||
}
|
||||
|
||||
updateElements(points, start, count, mode) {
|
||||
const me = this;
|
||||
const dataset = me.getDataset();
|
||||
const scale = me._cachedMeta.rScale;
|
||||
const dataset = this.getDataset();
|
||||
const scale = this._cachedMeta.rScale;
|
||||
const reset = mode === 'reset';
|
||||
|
||||
for (let i = start; i < start + count; i++) {
|
||||
const point = points[i];
|
||||
const options = me.resolveDataElementOptions(i, point.active ? 'active' : mode);
|
||||
const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);
|
||||
const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]);
|
||||
|
||||
const x = reset ? scale.xCenter : pointPosition.x;
|
||||
@ -67,7 +64,7 @@ export default class RadarController extends DatasetController {
|
||||
options
|
||||
};
|
||||
|
||||
me.updateElement(point, i, properties, mode);
|
||||
this.updateElement(point, i, properties, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,60 +44,57 @@ export default class Animation {
|
||||
}
|
||||
|
||||
update(cfg, to, date) {
|
||||
const me = this;
|
||||
if (me._active) {
|
||||
me._notify(false);
|
||||
if (this._active) {
|
||||
this._notify(false);
|
||||
|
||||
const currentValue = me._target[me._prop];
|
||||
const elapsed = date - me._start;
|
||||
const remain = me._duration - elapsed;
|
||||
me._start = date;
|
||||
me._duration = Math.floor(Math.max(remain, cfg.duration));
|
||||
me._total += elapsed;
|
||||
me._loop = !!cfg.loop;
|
||||
me._to = resolve([cfg.to, to, currentValue, cfg.from]);
|
||||
me._from = resolve([cfg.from, currentValue, to]);
|
||||
const currentValue = this._target[this._prop];
|
||||
const elapsed = date - this._start;
|
||||
const remain = this._duration - elapsed;
|
||||
this._start = date;
|
||||
this._duration = Math.floor(Math.max(remain, cfg.duration));
|
||||
this._total += elapsed;
|
||||
this._loop = !!cfg.loop;
|
||||
this._to = resolve([cfg.to, to, currentValue, cfg.from]);
|
||||
this._from = resolve([cfg.from, currentValue, to]);
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
const me = this;
|
||||
if (me._active) {
|
||||
if (this._active) {
|
||||
// update current evaluated value, for smoother animations
|
||||
me.tick(Date.now());
|
||||
me._active = false;
|
||||
me._notify(false);
|
||||
this.tick(Date.now());
|
||||
this._active = false;
|
||||
this._notify(false);
|
||||
}
|
||||
}
|
||||
|
||||
tick(date) {
|
||||
const me = this;
|
||||
const elapsed = date - me._start;
|
||||
const duration = me._duration;
|
||||
const prop = me._prop;
|
||||
const from = me._from;
|
||||
const loop = me._loop;
|
||||
const to = me._to;
|
||||
const elapsed = date - this._start;
|
||||
const duration = this._duration;
|
||||
const prop = this._prop;
|
||||
const from = this._from;
|
||||
const loop = this._loop;
|
||||
const to = this._to;
|
||||
let factor;
|
||||
|
||||
me._active = from !== to && (loop || (elapsed < duration));
|
||||
this._active = from !== to && (loop || (elapsed < duration));
|
||||
|
||||
if (!me._active) {
|
||||
me._target[prop] = to;
|
||||
me._notify(true);
|
||||
if (!this._active) {
|
||||
this._target[prop] = to;
|
||||
this._notify(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (elapsed < 0) {
|
||||
me._target[prop] = from;
|
||||
this._target[prop] = from;
|
||||
return;
|
||||
}
|
||||
|
||||
factor = (elapsed / duration) % 2;
|
||||
factor = loop && factor > 1 ? 2 - factor : factor;
|
||||
factor = me._easing(Math.min(1, Math.max(0, factor)));
|
||||
factor = this._easing(Math.min(1, Math.max(0, factor)));
|
||||
|
||||
me._target[prop] = me._fn(from, to, factor);
|
||||
this._target[prop] = this._fn(from, to, factor);
|
||||
}
|
||||
|
||||
wait() {
|
||||
|
||||
@ -36,19 +36,17 @@ export class Animator {
|
||||
* @private
|
||||
*/
|
||||
_refresh() {
|
||||
const me = this;
|
||||
|
||||
if (me._request) {
|
||||
if (this._request) {
|
||||
return;
|
||||
}
|
||||
me._running = true;
|
||||
this._running = true;
|
||||
|
||||
me._request = requestAnimFrame.call(window, () => {
|
||||
me._update();
|
||||
me._request = null;
|
||||
this._request = requestAnimFrame.call(window, () => {
|
||||
this._update();
|
||||
this._request = null;
|
||||
|
||||
if (me._running) {
|
||||
me._refresh();
|
||||
if (this._running) {
|
||||
this._refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -57,10 +55,9 @@ export class Animator {
|
||||
* @private
|
||||
*/
|
||||
_update(date = Date.now()) {
|
||||
const me = this;
|
||||
let remaining = 0;
|
||||
|
||||
me._charts.forEach((anims, chart) => {
|
||||
this._charts.forEach((anims, chart) => {
|
||||
if (!anims.running || !anims.items.length) {
|
||||
return;
|
||||
}
|
||||
@ -90,22 +87,22 @@ export class Animator {
|
||||
|
||||
if (draw) {
|
||||
chart.draw();
|
||||
me._notify(chart, anims, date, 'progress');
|
||||
this._notify(chart, anims, date, 'progress');
|
||||
}
|
||||
|
||||
if (!items.length) {
|
||||
anims.running = false;
|
||||
me._notify(chart, anims, date, 'complete');
|
||||
this._notify(chart, anims, date, 'complete');
|
||||
anims.initial = false;
|
||||
}
|
||||
|
||||
remaining += items.length;
|
||||
});
|
||||
|
||||
me._lastDate = date;
|
||||
this._lastDate = date;
|
||||
|
||||
if (remaining === 0) {
|
||||
me._running = false;
|
||||
this._running = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -73,8 +73,6 @@ class Chart {
|
||||
|
||||
// eslint-disable-next-line max-statements
|
||||
constructor(item, userConfig) {
|
||||
const me = this;
|
||||
|
||||
const config = this.config = new Config(userConfig);
|
||||
const initialCanvas = getCanvas(item);
|
||||
const existingChart = getChart(initialCanvas);
|
||||
@ -85,11 +83,11 @@ class Chart {
|
||||
);
|
||||
}
|
||||
|
||||
const options = config.createResolver(config.chartOptionScopes(), me.getContext());
|
||||
const options = config.createResolver(config.chartOptionScopes(), this.getContext());
|
||||
|
||||
this.platform = new (config.platform || _detectPlatform(initialCanvas))();
|
||||
|
||||
const context = me.platform.acquireContext(initialCanvas, options.aspectRatio);
|
||||
const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);
|
||||
const canvas = context && context.canvas;
|
||||
const height = canvas && canvas.height;
|
||||
const width = canvas && canvas.width;
|
||||
@ -126,7 +124,7 @@ class Chart {
|
||||
this._doResize = debounce(mode => this.update(mode), options.resizeDelay || 0);
|
||||
|
||||
// Add the chart instance to the global namespace
|
||||
instances[me.id] = me;
|
||||
instances[this.id] = this;
|
||||
|
||||
if (!context || !canvas) {
|
||||
// The given item is not a compatible context2d element, let's return before finalizing
|
||||
@ -137,12 +135,12 @@ class Chart {
|
||||
return;
|
||||
}
|
||||
|
||||
animator.listen(me, 'complete', onAnimationsComplete);
|
||||
animator.listen(me, 'progress', onAnimationProgress);
|
||||
animator.listen(this, 'complete', onAnimationsComplete);
|
||||
animator.listen(this, 'progress', onAnimationProgress);
|
||||
|
||||
me._initialize();
|
||||
if (me.attached) {
|
||||
me.update();
|
||||
this._initialize();
|
||||
if (this.attached) {
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,23 +180,21 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_initialize() {
|
||||
const me = this;
|
||||
|
||||
// Before init plugin notification
|
||||
me.notifyPlugins('beforeInit');
|
||||
this.notifyPlugins('beforeInit');
|
||||
|
||||
if (me.options.responsive) {
|
||||
me.resize();
|
||||
if (this.options.responsive) {
|
||||
this.resize();
|
||||
} else {
|
||||
retinaScale(me, me.options.devicePixelRatio);
|
||||
retinaScale(this, this.options.devicePixelRatio);
|
||||
}
|
||||
|
||||
me.bindEvents();
|
||||
this.bindEvents();
|
||||
|
||||
// After init plugin notification
|
||||
me.notifyPlugins('afterInit');
|
||||
this.notifyPlugins('afterInit');
|
||||
|
||||
return me;
|
||||
return this;
|
||||
}
|
||||
|
||||
clear() {
|
||||
@ -225,29 +221,28 @@ class Chart {
|
||||
}
|
||||
|
||||
_resize(width, height) {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const canvas = me.canvas;
|
||||
const aspectRatio = options.maintainAspectRatio && me.aspectRatio;
|
||||
const newSize = me.platform.getMaximumSize(canvas, width, height, aspectRatio);
|
||||
const newRatio = options.devicePixelRatio || me.platform.getDevicePixelRatio();
|
||||
const mode = me.width ? 'resize' : 'attach';
|
||||
const options = this.options;
|
||||
const canvas = this.canvas;
|
||||
const aspectRatio = options.maintainAspectRatio && this.aspectRatio;
|
||||
const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);
|
||||
const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();
|
||||
const mode = this.width ? 'resize' : 'attach';
|
||||
|
||||
me.width = newSize.width;
|
||||
me.height = newSize.height;
|
||||
me._aspectRatio = me.aspectRatio;
|
||||
if (!retinaScale(me, newRatio, true)) {
|
||||
this.width = newSize.width;
|
||||
this.height = newSize.height;
|
||||
this._aspectRatio = this.aspectRatio;
|
||||
if (!retinaScale(this, newRatio, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
me.notifyPlugins('resize', {size: newSize});
|
||||
this.notifyPlugins('resize', {size: newSize});
|
||||
|
||||
callCallback(options.onResize, [me, newSize], me);
|
||||
callCallback(options.onResize, [this, newSize], this);
|
||||
|
||||
if (me.attached) {
|
||||
if (me._doResize(mode)) {
|
||||
if (this.attached) {
|
||||
if (this._doResize(mode)) {
|
||||
// The resize update is delayed, only draw without updating.
|
||||
me.render();
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -265,10 +260,9 @@ class Chart {
|
||||
* Builds a map of scale ID to scale object for future lookup.
|
||||
*/
|
||||
buildOrUpdateScales() {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const options = this.options;
|
||||
const scaleOpts = options.scales;
|
||||
const scales = me.scales;
|
||||
const scales = this.scales;
|
||||
const updated = Object.keys(scales).reduce((obj, id) => {
|
||||
obj[id] = false;
|
||||
return obj;
|
||||
@ -310,8 +304,8 @@ class Chart {
|
||||
scale = new scaleClass({
|
||||
id,
|
||||
type: scaleType,
|
||||
ctx: me.ctx,
|
||||
chart: me
|
||||
ctx: this.ctx,
|
||||
chart: this
|
||||
});
|
||||
scales[scale.id] = scale;
|
||||
}
|
||||
@ -326,8 +320,8 @@ class Chart {
|
||||
});
|
||||
|
||||
each(scales, (scale) => {
|
||||
layouts.configure(me, scale, scale.options);
|
||||
layouts.addBox(me, scale);
|
||||
layouts.configure(this, scale, scale.options);
|
||||
layouts.addBox(this, scale);
|
||||
});
|
||||
}
|
||||
|
||||
@ -335,60 +329,57 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_updateMetasets() {
|
||||
const me = this;
|
||||
const metasets = me._metasets;
|
||||
const numData = me.data.datasets.length;
|
||||
const metasets = this._metasets;
|
||||
const numData = this.data.datasets.length;
|
||||
const numMeta = metasets.length;
|
||||
|
||||
metasets.sort((a, b) => a.index - b.index);
|
||||
if (numMeta > numData) {
|
||||
for (let i = numData; i < numMeta; ++i) {
|
||||
me._destroyDatasetMeta(i);
|
||||
this._destroyDatasetMeta(i);
|
||||
}
|
||||
metasets.splice(numData, numMeta - numData);
|
||||
}
|
||||
me._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));
|
||||
this._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_removeUnreferencedMetasets() {
|
||||
const me = this;
|
||||
const {_metasets: metasets, data: {datasets}} = me;
|
||||
const {_metasets: metasets, data: {datasets}} = this;
|
||||
if (metasets.length > datasets.length) {
|
||||
delete me._stacks;
|
||||
delete this._stacks;
|
||||
}
|
||||
metasets.forEach((meta, index) => {
|
||||
if (datasets.filter(x => x === meta._dataset).length === 0) {
|
||||
me._destroyDatasetMeta(index);
|
||||
this._destroyDatasetMeta(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
buildOrUpdateControllers() {
|
||||
const me = this;
|
||||
const newControllers = [];
|
||||
const datasets = me.data.datasets;
|
||||
const datasets = this.data.datasets;
|
||||
let i, ilen;
|
||||
|
||||
me._removeUnreferencedMetasets();
|
||||
this._removeUnreferencedMetasets();
|
||||
|
||||
for (i = 0, ilen = datasets.length; i < ilen; i++) {
|
||||
const dataset = datasets[i];
|
||||
let meta = me.getDatasetMeta(i);
|
||||
const type = dataset.type || me.config.type;
|
||||
let meta = this.getDatasetMeta(i);
|
||||
const type = dataset.type || this.config.type;
|
||||
|
||||
if (meta.type && meta.type !== type) {
|
||||
me._destroyDatasetMeta(i);
|
||||
meta = me.getDatasetMeta(i);
|
||||
this._destroyDatasetMeta(i);
|
||||
meta = this.getDatasetMeta(i);
|
||||
}
|
||||
meta.type = type;
|
||||
meta.indexAxis = dataset.indexAxis || getIndexAxis(type, me.options);
|
||||
meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options);
|
||||
meta.order = dataset.order || 0;
|
||||
meta.index = i;
|
||||
meta.label = '' + dataset.label;
|
||||
meta.visible = me.isDatasetVisible(i);
|
||||
meta.visible = this.isDatasetVisible(i);
|
||||
|
||||
if (meta.controller) {
|
||||
meta.controller.updateIndex(i);
|
||||
@ -400,12 +391,12 @@ class Chart {
|
||||
dataElementType: registry.getElement(dataElementType),
|
||||
datasetElementType: datasetElementType && registry.getElement(datasetElementType)
|
||||
});
|
||||
meta.controller = new ControllerClass(me, i);
|
||||
meta.controller = new ControllerClass(this, i);
|
||||
newControllers.push(meta.controller);
|
||||
}
|
||||
}
|
||||
|
||||
me._updateMetasets();
|
||||
this._updateMetasets();
|
||||
return newControllers;
|
||||
}
|
||||
|
||||
@ -414,10 +405,9 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_resetElements() {
|
||||
const me = this;
|
||||
each(me.data.datasets, (dataset, datasetIndex) => {
|
||||
me.getDatasetMeta(datasetIndex).controller.reset();
|
||||
}, me);
|
||||
each(this.data.datasets, (dataset, datasetIndex) => {
|
||||
this.getDatasetMeta(datasetIndex).controller.reset();
|
||||
}, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -429,55 +419,54 @@ class Chart {
|
||||
}
|
||||
|
||||
update(mode) {
|
||||
const me = this;
|
||||
const config = me.config;
|
||||
const config = this.config;
|
||||
|
||||
config.update();
|
||||
me._options = config.createResolver(config.chartOptionScopes(), me.getContext());
|
||||
this._options = config.createResolver(config.chartOptionScopes(), this.getContext());
|
||||
|
||||
each(me.scales, (scale) => {
|
||||
layouts.removeBox(me, scale);
|
||||
each(this.scales, (scale) => {
|
||||
layouts.removeBox(this, scale);
|
||||
});
|
||||
|
||||
const animsDisabled = me._animationsDisabled = !me.options.animation;
|
||||
const animsDisabled = this._animationsDisabled = !this.options.animation;
|
||||
|
||||
me.ensureScalesHaveIDs();
|
||||
me.buildOrUpdateScales();
|
||||
this.ensureScalesHaveIDs();
|
||||
this.buildOrUpdateScales();
|
||||
|
||||
const existingEvents = new Set(Object.keys(me._listeners));
|
||||
const newEvents = new Set(me.options.events);
|
||||
const existingEvents = new Set(Object.keys(this._listeners));
|
||||
const newEvents = new Set(this.options.events);
|
||||
|
||||
if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== me.options.responsive) {
|
||||
if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== this.options.responsive) {
|
||||
// The configured events have changed. Rebind.
|
||||
me.unbindEvents();
|
||||
me.bindEvents();
|
||||
this.unbindEvents();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
// plugins options references might have change, let's invalidate the cache
|
||||
// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
||||
me._plugins.invalidate();
|
||||
this._plugins.invalidate();
|
||||
|
||||
if (me.notifyPlugins('beforeUpdate', {mode, cancelable: true}) === false) {
|
||||
if (this.notifyPlugins('beforeUpdate', {mode, cancelable: true}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure dataset controllers are updated and new controllers are reset
|
||||
const newControllers = me.buildOrUpdateControllers();
|
||||
const newControllers = this.buildOrUpdateControllers();
|
||||
|
||||
me.notifyPlugins('beforeElementsUpdate');
|
||||
this.notifyPlugins('beforeElementsUpdate');
|
||||
|
||||
// Make sure all dataset controllers have correct meta data counts
|
||||
let minPadding = 0;
|
||||
for (let i = 0, ilen = me.data.datasets.length; i < ilen; i++) {
|
||||
const {controller} = me.getDatasetMeta(i);
|
||||
for (let i = 0, ilen = this.data.datasets.length; i < ilen; i++) {
|
||||
const {controller} = this.getDatasetMeta(i);
|
||||
const reset = !animsDisabled && newControllers.indexOf(controller) === -1;
|
||||
// New controllers will be reset after the layout pass, so we only want to modify
|
||||
// elements added to new datasets
|
||||
controller.buildOrUpdateElements(reset);
|
||||
minPadding = Math.max(+controller.getMaxOverflow(), minPadding);
|
||||
}
|
||||
me._minPadding = minPadding;
|
||||
me._updateLayout(minPadding);
|
||||
this._minPadding = minPadding;
|
||||
this._updateLayout(minPadding);
|
||||
|
||||
// Only reset the controllers if we have animations
|
||||
if (!animsDisabled) {
|
||||
@ -488,19 +477,19 @@ class Chart {
|
||||
});
|
||||
}
|
||||
|
||||
me._updateDatasets(mode);
|
||||
this._updateDatasets(mode);
|
||||
|
||||
// Do this before render so that any plugins that need final scale updates can use it
|
||||
me.notifyPlugins('afterUpdate', {mode});
|
||||
this.notifyPlugins('afterUpdate', {mode});
|
||||
|
||||
me._layers.sort(compare2Level('z', '_idx'));
|
||||
this._layers.sort(compare2Level('z', '_idx'));
|
||||
|
||||
// Replay last event from before update
|
||||
if (me._lastEvent) {
|
||||
me._eventHandler(me._lastEvent, true);
|
||||
if (this._lastEvent) {
|
||||
this._eventHandler(this._lastEvent, true);
|
||||
}
|
||||
|
||||
me.render();
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -509,19 +498,17 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_updateLayout(minPadding) {
|
||||
const me = this;
|
||||
|
||||
if (me.notifyPlugins('beforeLayout', {cancelable: true}) === false) {
|
||||
if (this.notifyPlugins('beforeLayout', {cancelable: true}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
layouts.update(me, me.width, me.height, minPadding);
|
||||
layouts.update(this, this.width, this.height, minPadding);
|
||||
|
||||
const area = me.chartArea;
|
||||
const area = this.chartArea;
|
||||
const noArea = area.width <= 0 || area.height <= 0;
|
||||
|
||||
me._layers = [];
|
||||
each(me.boxes, (box) => {
|
||||
this._layers = [];
|
||||
each(this.boxes, (box) => {
|
||||
if (noArea && box.position === 'chartArea') {
|
||||
// Skip drawing and configuring chartArea boxes when chartArea is zero or negative
|
||||
return;
|
||||
@ -532,14 +519,14 @@ class Chart {
|
||||
if (box.configure) {
|
||||
box.configure();
|
||||
}
|
||||
me._layers.push(...box._layers());
|
||||
}, me);
|
||||
this._layers.push(...box._layers());
|
||||
}, this);
|
||||
|
||||
me._layers.forEach((item, index) => {
|
||||
this._layers.forEach((item, index) => {
|
||||
item._idx = index;
|
||||
});
|
||||
|
||||
me.notifyPlugins('afterLayout');
|
||||
this.notifyPlugins('afterLayout');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -548,18 +535,17 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_updateDatasets(mode) {
|
||||
const me = this;
|
||||
const isFunction = typeof mode === 'function';
|
||||
|
||||
if (me.notifyPlugins('beforeDatasetsUpdate', {mode, cancelable: true}) === false) {
|
||||
if (this.notifyPlugins('beforeDatasetsUpdate', {mode, cancelable: true}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
|
||||
me._updateDataset(i, isFunction ? mode({datasetIndex: i}) : mode);
|
||||
for (let i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
|
||||
this._updateDataset(i, isFunction ? mode({datasetIndex: i}) : mode);
|
||||
}
|
||||
|
||||
me.notifyPlugins('afterDatasetsUpdate', {mode});
|
||||
this.notifyPlugins('afterDatasetsUpdate', {mode});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -568,78 +554,74 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_updateDataset(index, mode) {
|
||||
const me = this;
|
||||
const meta = me.getDatasetMeta(index);
|
||||
const meta = this.getDatasetMeta(index);
|
||||
const args = {meta, index, mode, cancelable: true};
|
||||
|
||||
if (me.notifyPlugins('beforeDatasetUpdate', args) === false) {
|
||||
if (this.notifyPlugins('beforeDatasetUpdate', args) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
meta.controller._update(mode);
|
||||
|
||||
args.cancelable = false;
|
||||
me.notifyPlugins('afterDatasetUpdate', args);
|
||||
this.notifyPlugins('afterDatasetUpdate', args);
|
||||
}
|
||||
|
||||
render() {
|
||||
const me = this;
|
||||
if (me.notifyPlugins('beforeRender', {cancelable: true}) === false) {
|
||||
if (this.notifyPlugins('beforeRender', {cancelable: true}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (animator.has(me)) {
|
||||
if (me.attached && !animator.running(me)) {
|
||||
animator.start(me);
|
||||
if (animator.has(this)) {
|
||||
if (this.attached && !animator.running(this)) {
|
||||
animator.start(this);
|
||||
}
|
||||
} else {
|
||||
me.draw();
|
||||
onAnimationsComplete({chart: me});
|
||||
this.draw();
|
||||
onAnimationsComplete({chart: this});
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
const me = this;
|
||||
let i;
|
||||
if (me._resizeBeforeDraw) {
|
||||
const {width, height} = me._resizeBeforeDraw;
|
||||
me._resize(width, height);
|
||||
me._resizeBeforeDraw = null;
|
||||
if (this._resizeBeforeDraw) {
|
||||
const {width, height} = this._resizeBeforeDraw;
|
||||
this._resize(width, height);
|
||||
this._resizeBeforeDraw = null;
|
||||
}
|
||||
me.clear();
|
||||
this.clear();
|
||||
|
||||
if (me.width <= 0 || me.height <= 0) {
|
||||
if (this.width <= 0 || this.height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (me.notifyPlugins('beforeDraw', {cancelable: true}) === false) {
|
||||
if (this.notifyPlugins('beforeDraw', {cancelable: true}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Because of plugin hooks (before/afterDatasetsDraw), datasets can't
|
||||
// currently be part of layers. Instead, we draw
|
||||
// layers <= 0 before(default, backward compat), and the rest after
|
||||
const layers = me._layers;
|
||||
const layers = this._layers;
|
||||
for (i = 0; i < layers.length && layers[i].z <= 0; ++i) {
|
||||
layers[i].draw(me.chartArea);
|
||||
layers[i].draw(this.chartArea);
|
||||
}
|
||||
|
||||
me._drawDatasets();
|
||||
this._drawDatasets();
|
||||
|
||||
// Rest of layers
|
||||
for (; i < layers.length; ++i) {
|
||||
layers[i].draw(me.chartArea);
|
||||
layers[i].draw(this.chartArea);
|
||||
}
|
||||
|
||||
me.notifyPlugins('afterDraw');
|
||||
this.notifyPlugins('afterDraw');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getSortedDatasetMetas(filterVisible) {
|
||||
const me = this;
|
||||
const metasets = me._sortedMetasets;
|
||||
const metasets = this._sortedMetasets;
|
||||
const result = [];
|
||||
let i, ilen;
|
||||
|
||||
@ -667,18 +649,16 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_drawDatasets() {
|
||||
const me = this;
|
||||
|
||||
if (me.notifyPlugins('beforeDatasetsDraw', {cancelable: true}) === false) {
|
||||
if (this.notifyPlugins('beforeDatasetsDraw', {cancelable: true}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metasets = me.getSortedVisibleDatasetMetas();
|
||||
const metasets = this.getSortedVisibleDatasetMetas();
|
||||
for (let i = metasets.length - 1; i >= 0; --i) {
|
||||
me._drawDataset(metasets[i]);
|
||||
this._drawDataset(metasets[i]);
|
||||
}
|
||||
|
||||
me.notifyPlugins('afterDatasetsDraw');
|
||||
this.notifyPlugins('afterDatasetsDraw');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -687,27 +667,26 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_drawDataset(meta) {
|
||||
const me = this;
|
||||
const ctx = me.ctx;
|
||||
const ctx = this.ctx;
|
||||
const clip = meta._clip;
|
||||
const useClip = !clip.disabled;
|
||||
const area = me.chartArea;
|
||||
const area = this.chartArea;
|
||||
const args = {
|
||||
meta,
|
||||
index: meta.index,
|
||||
cancelable: true
|
||||
};
|
||||
|
||||
if (me.notifyPlugins('beforeDatasetDraw', args) === false) {
|
||||
if (this.notifyPlugins('beforeDatasetDraw', args) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (useClip) {
|
||||
clipArea(ctx, {
|
||||
left: clip.left === false ? 0 : area.left - clip.left,
|
||||
right: clip.right === false ? me.width : area.right + clip.right,
|
||||
right: clip.right === false ? this.width : area.right + clip.right,
|
||||
top: clip.top === false ? 0 : area.top - clip.top,
|
||||
bottom: clip.bottom === false ? me.height : area.bottom + clip.bottom
|
||||
bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom
|
||||
});
|
||||
}
|
||||
|
||||
@ -718,7 +697,7 @@ class Chart {
|
||||
}
|
||||
|
||||
args.cancelable = false;
|
||||
me.notifyPlugins('afterDatasetDraw', args);
|
||||
this.notifyPlugins('afterDatasetDraw', args);
|
||||
}
|
||||
|
||||
getElementsAtEventForMode(e, mode, options, useFinalPosition) {
|
||||
@ -731,9 +710,8 @@ class Chart {
|
||||
}
|
||||
|
||||
getDatasetMeta(datasetIndex) {
|
||||
const me = this;
|
||||
const dataset = me.data.datasets[datasetIndex];
|
||||
const metasets = me._metasets;
|
||||
const dataset = this.data.datasets[datasetIndex];
|
||||
const metasets = this._metasets;
|
||||
let meta = metasets.filter(x => x && x._dataset === dataset).pop();
|
||||
|
||||
if (!meta) {
|
||||
@ -795,19 +773,18 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_updateVisibility(datasetIndex, dataIndex, visible) {
|
||||
const me = this;
|
||||
const mode = visible ? 'show' : 'hide';
|
||||
const meta = me.getDatasetMeta(datasetIndex);
|
||||
const meta = this.getDatasetMeta(datasetIndex);
|
||||
const anims = meta.controller._resolveAnimations(undefined, mode);
|
||||
|
||||
if (defined(dataIndex)) {
|
||||
meta.data[dataIndex].hidden = !visible;
|
||||
me.update();
|
||||
this.update();
|
||||
} else {
|
||||
me.setDatasetVisibility(datasetIndex, visible);
|
||||
this.setDatasetVisibility(datasetIndex, visible);
|
||||
// Animate visible state, so hide animation can be seen. This could be handled better if update / updateDataset returned a Promise.
|
||||
anims.update(meta, {visible});
|
||||
me.update((ctx) => ctx.datasetIndex === datasetIndex ? mode : undefined);
|
||||
this.update((ctx) => ctx.datasetIndex === datasetIndex ? mode : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@ -823,44 +800,41 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_destroyDatasetMeta(datasetIndex) {
|
||||
const me = this;
|
||||
const meta = me._metasets && me._metasets[datasetIndex];
|
||||
const meta = this._metasets && this._metasets[datasetIndex];
|
||||
|
||||
if (meta && meta.controller) {
|
||||
meta.controller._destroy();
|
||||
delete me._metasets[datasetIndex];
|
||||
delete this._metasets[datasetIndex];
|
||||
}
|
||||
}
|
||||
|
||||
_stop() {
|
||||
const me = this;
|
||||
let i, ilen;
|
||||
me.stop();
|
||||
animator.remove(me);
|
||||
this.stop();
|
||||
animator.remove(this);
|
||||
|
||||
for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
|
||||
me._destroyDatasetMeta(i);
|
||||
for (i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
|
||||
this._destroyDatasetMeta(i);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
const me = this;
|
||||
const {canvas, ctx} = me;
|
||||
const {canvas, ctx} = this;
|
||||
|
||||
me._stop();
|
||||
me.config.clearCache();
|
||||
this._stop();
|
||||
this.config.clearCache();
|
||||
|
||||
if (canvas) {
|
||||
me.unbindEvents();
|
||||
this.unbindEvents();
|
||||
clearCanvas(canvas, ctx);
|
||||
me.platform.releaseContext(ctx);
|
||||
me.canvas = null;
|
||||
me.ctx = null;
|
||||
this.platform.releaseContext(ctx);
|
||||
this.canvas = null;
|
||||
this.ctx = null;
|
||||
}
|
||||
|
||||
me.notifyPlugins('destroy');
|
||||
this.notifyPlugins('destroy');
|
||||
|
||||
delete instances[me.id];
|
||||
delete instances[this.id];
|
||||
}
|
||||
|
||||
toBase64Image(...args) {
|
||||
@ -883,49 +857,47 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
bindUserEvents() {
|
||||
const me = this;
|
||||
const listeners = me._listeners;
|
||||
const platform = me.platform;
|
||||
const listeners = this._listeners;
|
||||
const platform = this.platform;
|
||||
|
||||
const _add = (type, listener) => {
|
||||
platform.addEventListener(me, type, listener);
|
||||
platform.addEventListener(this, type, listener);
|
||||
listeners[type] = listener;
|
||||
};
|
||||
|
||||
const listener = function(e, x, y) {
|
||||
const listener = (e, x, y) => {
|
||||
e.offsetX = x;
|
||||
e.offsetY = y;
|
||||
me._eventHandler(e);
|
||||
this._eventHandler(e);
|
||||
};
|
||||
|
||||
each(me.options.events, (type) => _add(type, listener));
|
||||
each(this.options.events, (type) => _add(type, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
bindResponsiveEvents() {
|
||||
const me = this;
|
||||
if (!me._responsiveListeners) {
|
||||
me._responsiveListeners = {};
|
||||
if (!this._responsiveListeners) {
|
||||
this._responsiveListeners = {};
|
||||
}
|
||||
const listeners = me._responsiveListeners;
|
||||
const platform = me.platform;
|
||||
const listeners = this._responsiveListeners;
|
||||
const platform = this.platform;
|
||||
|
||||
const _add = (type, listener) => {
|
||||
platform.addEventListener(me, type, listener);
|
||||
platform.addEventListener(this, type, listener);
|
||||
listeners[type] = listener;
|
||||
};
|
||||
const _remove = (type, listener) => {
|
||||
if (listeners[type]) {
|
||||
platform.removeEventListener(me, type, listener);
|
||||
platform.removeEventListener(this, type, listener);
|
||||
delete listeners[type];
|
||||
}
|
||||
};
|
||||
|
||||
const listener = (width, height) => {
|
||||
if (me.canvas) {
|
||||
me.resize(width, height);
|
||||
if (this.canvas) {
|
||||
this.resize(width, height);
|
||||
}
|
||||
};
|
||||
|
||||
@ -933,26 +905,26 @@ class Chart {
|
||||
const attached = () => {
|
||||
_remove('attach', attached);
|
||||
|
||||
me.attached = true;
|
||||
me.resize();
|
||||
this.attached = true;
|
||||
this.resize();
|
||||
|
||||
_add('resize', listener);
|
||||
_add('detach', detached);
|
||||
};
|
||||
|
||||
detached = () => {
|
||||
me.attached = false;
|
||||
this.attached = false;
|
||||
|
||||
_remove('resize', listener);
|
||||
|
||||
// Stop animating and remove metasets, so when re-attached, the animations start from begining.
|
||||
me._stop();
|
||||
me._resize(0, 0);
|
||||
this._stop();
|
||||
this._resize(0, 0);
|
||||
|
||||
_add('attach', attached);
|
||||
};
|
||||
|
||||
if (platform.isAttached(me.canvas)) {
|
||||
if (platform.isAttached(this.canvas)) {
|
||||
attached();
|
||||
} else {
|
||||
detached();
|
||||
@ -963,17 +935,15 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
unbindEvents() {
|
||||
const me = this;
|
||||
|
||||
each(me._listeners, (listener, type) => {
|
||||
me.platform.removeEventListener(me, type, listener);
|
||||
each(this._listeners, (listener, type) => {
|
||||
this.platform.removeEventListener(this, type, listener);
|
||||
});
|
||||
me._listeners = {};
|
||||
this._listeners = {};
|
||||
|
||||
each(me._responsiveListeners, (listener, type) => {
|
||||
me.platform.removeEventListener(me, type, listener);
|
||||
each(this._responsiveListeners, (listener, type) => {
|
||||
this.platform.removeEventListener(this, type, listener);
|
||||
});
|
||||
me._responsiveListeners = undefined;
|
||||
this._responsiveListeners = undefined;
|
||||
}
|
||||
|
||||
updateHoverStyle(items, mode, enabled) {
|
||||
@ -1007,10 +977,9 @@ class Chart {
|
||||
* @param {array} activeElements New active data points
|
||||
*/
|
||||
setActiveElements(activeElements) {
|
||||
const me = this;
|
||||
const lastActive = me._active || [];
|
||||
const lastActive = this._active || [];
|
||||
const active = activeElements.map(({datasetIndex, index}) => {
|
||||
const meta = me.getDatasetMeta(datasetIndex);
|
||||
const meta = this.getDatasetMeta(datasetIndex);
|
||||
if (!meta) {
|
||||
throw new Error('No dataset found at index ' + datasetIndex);
|
||||
}
|
||||
@ -1024,8 +993,8 @@ class Chart {
|
||||
const changed = !_elementsEqual(active, lastActive);
|
||||
|
||||
if (changed) {
|
||||
me._active = active;
|
||||
me._updateHoverStyles(active, lastActive);
|
||||
this._active = active;
|
||||
this._updateHoverStyles(active, lastActive);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1046,18 +1015,17 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_updateHoverStyles(active, lastActive, replay) {
|
||||
const me = this;
|
||||
const hoverOptions = me.options.hover;
|
||||
const hoverOptions = this.options.hover;
|
||||
const diff = (a, b) => a.filter(x => !b.some(y => x.datasetIndex === y.datasetIndex && x.index === y.index));
|
||||
const deactivated = diff(lastActive, active);
|
||||
const activated = replay ? active : diff(active, lastActive);
|
||||
|
||||
if (deactivated.length) {
|
||||
me.updateHoverStyle(deactivated, hoverOptions.mode, false);
|
||||
this.updateHoverStyle(deactivated, hoverOptions.mode, false);
|
||||
}
|
||||
|
||||
if (activated.length && hoverOptions.mode) {
|
||||
me.updateHoverStyle(activated, hoverOptions.mode, true);
|
||||
this.updateHoverStyle(activated, hoverOptions.mode, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1065,24 +1033,23 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_eventHandler(e, replay) {
|
||||
const me = this;
|
||||
const args = {event: e, replay, cancelable: true};
|
||||
const eventFilter = (plugin) => (plugin.options.events || this.options.events).includes(e.native.type);
|
||||
|
||||
if (me.notifyPlugins('beforeEvent', args, eventFilter) === false) {
|
||||
if (this.notifyPlugins('beforeEvent', args, eventFilter) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changed = me._handleEvent(e, replay);
|
||||
const changed = this._handleEvent(e, replay);
|
||||
|
||||
args.cancelable = false;
|
||||
me.notifyPlugins('afterEvent', args, eventFilter);
|
||||
this.notifyPlugins('afterEvent', args, eventFilter);
|
||||
|
||||
if (changed || args.changed) {
|
||||
me.render();
|
||||
this.render();
|
||||
}
|
||||
|
||||
return me;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1093,8 +1060,7 @@ class Chart {
|
||||
* @private
|
||||
*/
|
||||
_handleEvent(e, replay) {
|
||||
const me = this;
|
||||
const {_active: lastActive = [], options} = me;
|
||||
const {_active: lastActive = [], options} = this;
|
||||
const hoverOptions = options.hover;
|
||||
|
||||
// If the event is replayed from `update`, we should evaluate with the final positions.
|
||||
@ -1118,29 +1084,29 @@ class Chart {
|
||||
|
||||
// Find Active Elements for hover and tooltips
|
||||
if (e.type !== 'mouseout') {
|
||||
active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);
|
||||
lastEvent = e.type === 'click' ? me._lastEvent : e;
|
||||
active = this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);
|
||||
lastEvent = e.type === 'click' ? this._lastEvent : e;
|
||||
}
|
||||
// Set _lastEvent to null while we are processing the event handlers.
|
||||
// This prevents recursion if the handler calls chart.update()
|
||||
me._lastEvent = null;
|
||||
this._lastEvent = null;
|
||||
|
||||
if (_isPointInArea(e, me.chartArea, me._minPadding)) {
|
||||
if (_isPointInArea(e, this.chartArea, this._minPadding)) {
|
||||
// Invoke onHover hook
|
||||
callCallback(options.onHover, [e, active, me], me);
|
||||
callCallback(options.onHover, [e, active, this], this);
|
||||
|
||||
if (e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu') {
|
||||
callCallback(options.onClick, [e, active, me], me);
|
||||
callCallback(options.onClick, [e, active, this], this);
|
||||
}
|
||||
}
|
||||
|
||||
changed = !_elementsEqual(active, lastActive);
|
||||
if (changed || replay) {
|
||||
me._active = active;
|
||||
me._updateHoverStyles(active, lastActive, replay);
|
||||
this._active = active;
|
||||
this._updateHoverStyles(active, lastActive, replay);
|
||||
}
|
||||
|
||||
me._lastEvent = lastEvent;
|
||||
this._lastEvent = lastEvent;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@ -242,12 +242,11 @@ export default class DatasetController {
|
||||
}
|
||||
|
||||
initialize() {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
me.configure();
|
||||
me.linkScales();
|
||||
const meta = this._cachedMeta;
|
||||
this.configure();
|
||||
this.linkScales();
|
||||
meta._stacked = isStacked(meta.vScale, meta);
|
||||
me.addElements();
|
||||
this.addElements();
|
||||
}
|
||||
|
||||
updateIndex(datasetIndex) {
|
||||
@ -258,10 +257,9 @@ export default class DatasetController {
|
||||
}
|
||||
|
||||
linkScales() {
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
const meta = me._cachedMeta;
|
||||
const dataset = me.getDataset();
|
||||
const chart = this.chart;
|
||||
const meta = this._cachedMeta;
|
||||
const dataset = this.getDataset();
|
||||
|
||||
const chooseId = (axis, x, y, r) => axis === 'x' ? x : axis === 'r' ? r : y;
|
||||
|
||||
@ -271,11 +269,11 @@ export default class DatasetController {
|
||||
const indexAxis = meta.indexAxis;
|
||||
const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);
|
||||
const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);
|
||||
meta.xScale = me.getScaleForId(xid);
|
||||
meta.yScale = me.getScaleForId(yid);
|
||||
meta.rScale = me.getScaleForId(rid);
|
||||
meta.iScale = me.getScaleForId(iid);
|
||||
meta.vScale = me.getScaleForId(vid);
|
||||
meta.xScale = this.getScaleForId(xid);
|
||||
meta.yScale = this.getScaleForId(yid);
|
||||
meta.rScale = this.getScaleForId(rid);
|
||||
meta.iScale = this.getScaleForId(iid);
|
||||
meta.vScale = this.getScaleForId(vid);
|
||||
}
|
||||
|
||||
getDataset() {
|
||||
@ -325,52 +323,49 @@ export default class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_dataCheck() {
|
||||
const me = this;
|
||||
const dataset = me.getDataset();
|
||||
const dataset = this.getDataset();
|
||||
const data = dataset.data || (dataset.data = []);
|
||||
const _data = me._data;
|
||||
const _data = this._data;
|
||||
|
||||
// In order to correctly handle data addition/deletion animation (an thus simulate
|
||||
// real-time charts), we need to monitor these data modifications and synchronize
|
||||
// the internal meta data accordingly.
|
||||
|
||||
if (isObject(data)) {
|
||||
me._data = convertObjectDataToArray(data);
|
||||
this._data = convertObjectDataToArray(data);
|
||||
} else if (_data !== data) {
|
||||
if (_data) {
|
||||
// This case happens when the user replaced the data array instance.
|
||||
unlistenArrayEvents(_data, me);
|
||||
unlistenArrayEvents(_data, this);
|
||||
// Discard old parsed data and stacks
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
clearStacks(meta);
|
||||
meta._parsed = [];
|
||||
}
|
||||
if (data && Object.isExtensible(data)) {
|
||||
listenArrayEvents(data, me);
|
||||
listenArrayEvents(data, this);
|
||||
}
|
||||
me._syncList = [];
|
||||
me._data = data;
|
||||
this._syncList = [];
|
||||
this._data = data;
|
||||
}
|
||||
}
|
||||
|
||||
addElements() {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
|
||||
me._dataCheck();
|
||||
this._dataCheck();
|
||||
|
||||
if (me.datasetElementType) {
|
||||
meta.dataset = new me.datasetElementType();
|
||||
if (this.datasetElementType) {
|
||||
meta.dataset = new this.datasetElementType();
|
||||
}
|
||||
}
|
||||
|
||||
buildOrUpdateElements(resetNewElements) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const dataset = me.getDataset();
|
||||
const meta = this._cachedMeta;
|
||||
const dataset = this.getDataset();
|
||||
let stackChanged = false;
|
||||
|
||||
me._dataCheck();
|
||||
this._dataCheck();
|
||||
|
||||
// make sure cached _stacked status is current
|
||||
const oldStacked = meta._stacked;
|
||||
@ -386,11 +381,11 @@ export default class DatasetController {
|
||||
|
||||
// Re-sync meta data in case the user replaced the data array or if we missed
|
||||
// any updates and so make sure that we handle number of datapoints changing.
|
||||
me._resyncElements(resetNewElements);
|
||||
this._resyncElements(resetNewElements);
|
||||
|
||||
// if stack changed, update stack values for the whole dataset
|
||||
if (stackChanged || oldStacked !== meta._stacked) {
|
||||
updateStacks(me, meta._parsed);
|
||||
updateStacks(this, meta._parsed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,12 +394,11 @@ export default class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
configure() {
|
||||
const me = this;
|
||||
const config = me.chart.config;
|
||||
const scopeKeys = config.datasetScopeKeys(me._type);
|
||||
const scopes = config.getOptionScopes(me.getDataset(), scopeKeys, true);
|
||||
me.options = config.createResolver(scopes, me.getContext());
|
||||
me._parsing = me.options.parsing;
|
||||
const config = this.chart.config;
|
||||
const scopeKeys = config.datasetScopeKeys(this._type);
|
||||
const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true);
|
||||
this.options = config.createResolver(scopes, this.getContext());
|
||||
this._parsing = this.options.parsing;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -412,8 +406,7 @@ export default class DatasetController {
|
||||
* @param {number} count
|
||||
*/
|
||||
parse(start, count) {
|
||||
const me = this;
|
||||
const {_cachedMeta: meta, _data: data} = me;
|
||||
const {_cachedMeta: meta, _data: data} = this;
|
||||
const {iScale, _stacked} = meta;
|
||||
const iAxis = iScale.axis;
|
||||
|
||||
@ -421,17 +414,17 @@ export default class DatasetController {
|
||||
let prev = start > 0 && meta._parsed[start - 1];
|
||||
let i, cur, parsed;
|
||||
|
||||
if (me._parsing === false) {
|
||||
if (this._parsing === false) {
|
||||
meta._parsed = data;
|
||||
meta._sorted = true;
|
||||
parsed = data;
|
||||
} else {
|
||||
if (isArray(data[start])) {
|
||||
parsed = me.parseArrayData(meta, data, start, count);
|
||||
parsed = this.parseArrayData(meta, data, start, count);
|
||||
} else if (isObject(data[start])) {
|
||||
parsed = me.parseObjectData(meta, data, start, count);
|
||||
parsed = this.parseObjectData(meta, data, start, count);
|
||||
} else {
|
||||
parsed = me.parsePrimitiveData(meta, data, start, count);
|
||||
parsed = this.parsePrimitiveData(meta, data, start, count);
|
||||
}
|
||||
|
||||
const isNotInOrderComparedToPrev = () => cur[iAxis] === null || (prev && cur[iAxis] < prev[iAxis]);
|
||||
@ -448,7 +441,7 @@ export default class DatasetController {
|
||||
}
|
||||
|
||||
if (_stacked) {
|
||||
updateStacks(me, parsed);
|
||||
updateStacks(this, parsed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -588,13 +581,12 @@ export default class DatasetController {
|
||||
* @protected
|
||||
*/
|
||||
getMinMax(scale, canStack) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const _parsed = meta._parsed;
|
||||
const sorted = meta._sorted && scale === meta.iScale;
|
||||
const ilen = _parsed.length;
|
||||
const otherScale = me._getOtherScale(scale);
|
||||
const stack = canStack && meta._stacked && {keys: getSortedDatasetIndices(me.chart, true), values: null};
|
||||
const otherScale = this._getOtherScale(scale);
|
||||
const stack = canStack && meta._stacked && {keys: getSortedDatasetIndices(this.chart, true), values: null};
|
||||
const range = {min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY};
|
||||
const {min: otherMin, max: otherMax} = getUserBounds(otherScale);
|
||||
let i, value, parsed, otherValue;
|
||||
@ -610,7 +602,7 @@ export default class DatasetController {
|
||||
if (_skip()) {
|
||||
continue;
|
||||
}
|
||||
me.updateRangeFromParsed(range, scale, parsed, stack);
|
||||
this.updateRangeFromParsed(range, scale, parsed, stack);
|
||||
if (sorted) {
|
||||
// if the data is sorted, we don't need to check further from this end of array
|
||||
break;
|
||||
@ -622,7 +614,7 @@ export default class DatasetController {
|
||||
if (_skip()) {
|
||||
continue;
|
||||
}
|
||||
me.updateRangeFromParsed(range, scale, parsed, stack);
|
||||
this.updateRangeFromParsed(range, scale, parsed, stack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -655,11 +647,10 @@ export default class DatasetController {
|
||||
* @protected
|
||||
*/
|
||||
getLabelAndValue(index) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const iScale = meta.iScale;
|
||||
const vScale = meta.vScale;
|
||||
const parsed = me.getParsed(index);
|
||||
const parsed = this.getParsed(index);
|
||||
return {
|
||||
label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',
|
||||
value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''
|
||||
@ -670,12 +661,11 @@ export default class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_update(mode) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
me.configure();
|
||||
me._cachedDataOpts = {};
|
||||
me.update(mode || 'default');
|
||||
meta._clip = toClip(valueOrDefault(me.options.clip, defaultClip(meta.xScale, meta.yScale, me.getMaxOverflow())));
|
||||
const meta = this._cachedMeta;
|
||||
this.configure();
|
||||
this._cachedDataOpts = {};
|
||||
this.update(mode || 'default');
|
||||
meta._clip = toClip(valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow())));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -684,15 +674,14 @@ export default class DatasetController {
|
||||
update(mode) {} // eslint-disable-line no-unused-vars
|
||||
|
||||
draw() {
|
||||
const me = this;
|
||||
const ctx = me._ctx;
|
||||
const chart = me.chart;
|
||||
const meta = me._cachedMeta;
|
||||
const ctx = this._ctx;
|
||||
const chart = this.chart;
|
||||
const meta = this._cachedMeta;
|
||||
const elements = meta.data || [];
|
||||
const area = chart.chartArea;
|
||||
const active = [];
|
||||
const start = me._drawStart || 0;
|
||||
const count = me._drawCount || (elements.length - start);
|
||||
const start = this._drawStart || 0;
|
||||
const count = this._drawCount || (elements.length - start);
|
||||
let i;
|
||||
|
||||
if (meta.dataset) {
|
||||
@ -734,21 +723,20 @@ export default class DatasetController {
|
||||
* @protected
|
||||
*/
|
||||
getContext(index, active, mode) {
|
||||
const me = this;
|
||||
const dataset = me.getDataset();
|
||||
const dataset = this.getDataset();
|
||||
let context;
|
||||
if (index >= 0 && index < me._cachedMeta.data.length) {
|
||||
const element = me._cachedMeta.data[index];
|
||||
if (index >= 0 && index < this._cachedMeta.data.length) {
|
||||
const element = this._cachedMeta.data[index];
|
||||
context = element.$context ||
|
||||
(element.$context = createDataContext(me.getContext(), index, element));
|
||||
context.parsed = me.getParsed(index);
|
||||
(element.$context = createDataContext(this.getContext(), index, element));
|
||||
context.parsed = this.getParsed(index);
|
||||
context.raw = dataset.data[index];
|
||||
context.index = context.dataIndex = index;
|
||||
} else {
|
||||
context = me.$context ||
|
||||
(me.$context = createDatasetContext(me.chart.getContext(), me.index));
|
||||
context = this.$context ||
|
||||
(this.$context = createDatasetContext(this.chart.getContext(), this.index));
|
||||
context.dataset = dataset;
|
||||
context.index = context.datasetIndex = me.index;
|
||||
context.index = context.datasetIndex = this.index;
|
||||
}
|
||||
|
||||
context.active = !!active;
|
||||
@ -777,23 +765,22 @@ export default class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_resolveElementOptions(elementType, mode = 'default', index) {
|
||||
const me = this;
|
||||
const active = mode === 'active';
|
||||
const cache = me._cachedDataOpts;
|
||||
const cache = this._cachedDataOpts;
|
||||
const cacheKey = elementType + '-' + mode;
|
||||
const cached = cache[cacheKey];
|
||||
const sharing = me.enableOptionSharing && defined(index);
|
||||
const sharing = this.enableOptionSharing && defined(index);
|
||||
if (cached) {
|
||||
return cloneIfNotShared(cached, sharing);
|
||||
}
|
||||
const config = me.chart.config;
|
||||
const scopeKeys = config.datasetElementScopeKeys(me._type, elementType);
|
||||
const config = this.chart.config;
|
||||
const scopeKeys = config.datasetElementScopeKeys(this._type, elementType);
|
||||
const prefixes = active ? [`${elementType}Hover`, 'hover', elementType, ''] : [elementType, ''];
|
||||
const scopes = config.getOptionScopes(me.getDataset(), scopeKeys);
|
||||
const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
|
||||
const names = Object.keys(defaults.elements[elementType]);
|
||||
// context is provided as a function, and is called only if needed,
|
||||
// so we don't create a context for each element if not needed.
|
||||
const context = () => me.getContext(index, active);
|
||||
const context = () => this.getContext(index, active);
|
||||
const values = config.resolveNamedOptions(scopes, names, context, prefixes);
|
||||
|
||||
if (values.$shared) {
|
||||
@ -815,9 +802,8 @@ export default class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_resolveAnimations(index, transition, active) {
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
const cache = me._cachedDataOpts;
|
||||
const chart = this.chart;
|
||||
const cache = this._cachedDataOpts;
|
||||
const cacheKey = `animation-${transition}`;
|
||||
const cached = cache[cacheKey];
|
||||
if (cached) {
|
||||
@ -825,10 +811,10 @@ export default class DatasetController {
|
||||
}
|
||||
let options;
|
||||
if (chart.options.animation !== false) {
|
||||
const config = me.chart.config;
|
||||
const scopeKeys = config.datasetAnimationScopeKeys(me._type, transition);
|
||||
const scopes = config.getOptionScopes(me.getDataset(), scopeKeys);
|
||||
options = config.createResolver(scopes, me.getContext(index, active, transition));
|
||||
const config = this.chart.config;
|
||||
const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition);
|
||||
const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
|
||||
options = config.createResolver(scopes, this.getContext(index, active, transition));
|
||||
}
|
||||
const animations = new Animations(chart, options && options.animations);
|
||||
if (options && options._cacheable) {
|
||||
@ -925,15 +911,14 @@ export default class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_resyncElements(resetNewElements) {
|
||||
const me = this;
|
||||
const data = me._data;
|
||||
const elements = me._cachedMeta.data;
|
||||
const data = this._data;
|
||||
const elements = this._cachedMeta.data;
|
||||
|
||||
// Apply changes detected through array listeners
|
||||
for (const [method, arg1, arg2] of me._syncList) {
|
||||
me[method](arg1, arg2);
|
||||
for (const [method, arg1, arg2] of this._syncList) {
|
||||
this[method](arg1, arg2);
|
||||
}
|
||||
me._syncList = [];
|
||||
this._syncList = [];
|
||||
|
||||
const numMeta = elements.length;
|
||||
const numData = data.length;
|
||||
@ -944,13 +929,13 @@ export default class DatasetController {
|
||||
// This is done because we are not detecting direct assignments:
|
||||
// chart.data.datasets[0].data[5] = 10;
|
||||
// chart.data.datasets[0].data[5].y = 10;
|
||||
me.parse(0, count);
|
||||
this.parse(0, count);
|
||||
}
|
||||
|
||||
if (numData > numMeta) {
|
||||
me._insertElements(numMeta, numData - numMeta, resetNewElements);
|
||||
this._insertElements(numMeta, numData - numMeta, resetNewElements);
|
||||
} else if (numData < numMeta) {
|
||||
me._removeElements(numData, numMeta - numData);
|
||||
this._removeElements(numData, numMeta - numData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -958,8 +943,7 @@ export default class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_insertElements(start, count, resetNewElements = true) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
const meta = this._cachedMeta;
|
||||
const data = meta.data;
|
||||
const end = start + count;
|
||||
let i;
|
||||
@ -973,16 +957,16 @@ export default class DatasetController {
|
||||
move(data);
|
||||
|
||||
for (i = start; i < end; ++i) {
|
||||
data[i] = new me.dataElementType();
|
||||
data[i] = new this.dataElementType();
|
||||
}
|
||||
|
||||
if (me._parsing) {
|
||||
if (this._parsing) {
|
||||
move(meta._parsed);
|
||||
}
|
||||
me.parse(start, count);
|
||||
this.parse(start, count);
|
||||
|
||||
if (resetNewElements) {
|
||||
me.updateElements(data, start, count, 'reset');
|
||||
this.updateElements(data, start, count, 'reset');
|
||||
}
|
||||
}
|
||||
|
||||
@ -992,9 +976,8 @@ export default class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_removeElements(start, count) {
|
||||
const me = this;
|
||||
const meta = me._cachedMeta;
|
||||
if (me._parsing) {
|
||||
const meta = this._cachedMeta;
|
||||
if (this._parsing) {
|
||||
const removed = meta._parsed.splice(start, count);
|
||||
if (meta._stacked) {
|
||||
clearStacks(meta, removed);
|
||||
|
||||
@ -29,15 +29,14 @@ export default class Element {
|
||||
* @return {object}
|
||||
*/
|
||||
getProps(props, final) {
|
||||
const me = this;
|
||||
const anims = this.$animations;
|
||||
if (!final || !anims) {
|
||||
// let's not create an object, if not needed
|
||||
return me;
|
||||
return this;
|
||||
}
|
||||
const ret = {};
|
||||
props.forEach(prop => {
|
||||
ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : me[prop];
|
||||
ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop];
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -33,19 +33,17 @@ export default class PluginService {
|
||||
* @returns {boolean} false if any of the plugins return false, else returns true.
|
||||
*/
|
||||
notify(chart, hook, args, filter) {
|
||||
const me = this;
|
||||
|
||||
if (hook === 'beforeInit') {
|
||||
me._init = me._createDescriptors(chart, true);
|
||||
me._notify(me._init, chart, 'install');
|
||||
this._init = this._createDescriptors(chart, true);
|
||||
this._notify(this._init, chart, 'install');
|
||||
}
|
||||
|
||||
const descriptors = filter ? me._descriptors(chart).filter(filter) : me._descriptors(chart);
|
||||
const result = me._notify(descriptors, chart, hook, args);
|
||||
const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart);
|
||||
const result = this._notify(descriptors, chart, hook, args);
|
||||
|
||||
if (hook === 'destroy') {
|
||||
me._notify(descriptors, chart, 'stop');
|
||||
me._notify(me._init, chart, 'uninstall');
|
||||
this._notify(descriptors, chart, 'stop');
|
||||
this._notify(this._init, chart, 'uninstall');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -122,11 +122,10 @@ export class Registry {
|
||||
* @private
|
||||
*/
|
||||
_each(method, args, typedRegistry) {
|
||||
const me = this;
|
||||
[...args].forEach(arg => {
|
||||
const reg = typedRegistry || me._getRegistryForType(arg);
|
||||
if (typedRegistry || reg.isForType(arg) || (reg === me.plugins && arg.id)) {
|
||||
me._exec(method, reg, arg);
|
||||
const reg = typedRegistry || this._getRegistryForType(arg);
|
||||
if (typedRegistry || reg.isForType(arg) || (reg === this.plugins && arg.id)) {
|
||||
this._exec(method, reg, arg);
|
||||
} else {
|
||||
// Handle loopable args
|
||||
// Use case:
|
||||
@ -139,8 +138,8 @@ export class Registry {
|
||||
// import * as treemap from 'chartjs-chart-treemap';
|
||||
// Chart.register(treemap);
|
||||
|
||||
const itemReg = typedRegistry || me._getRegistryForType(item);
|
||||
me._exec(method, itemReg, item);
|
||||
const itemReg = typedRegistry || this._getRegistryForType(item);
|
||||
this._exec(method, itemReg, item);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -22,18 +22,17 @@ export default class TypedRegistry {
|
||||
* @returns {string} The scope where items defaults were registered to.
|
||||
*/
|
||||
register(item) {
|
||||
const me = this;
|
||||
const proto = Object.getPrototypeOf(item);
|
||||
let parentScope;
|
||||
|
||||
if (isIChartComponent(proto)) {
|
||||
// Make sure the parent is registered and note the scope where its defaults are.
|
||||
parentScope = me.register(proto);
|
||||
parentScope = this.register(proto);
|
||||
}
|
||||
|
||||
const items = me.items;
|
||||
const items = this.items;
|
||||
const id = item.id;
|
||||
const scope = me.scope + '.' + id;
|
||||
const scope = this.scope + '.' + id;
|
||||
|
||||
if (!id) {
|
||||
throw new Error('class does not have id: ' + item);
|
||||
@ -46,7 +45,7 @@ export default class TypedRegistry {
|
||||
|
||||
items[id] = item;
|
||||
registerDefaults(item, scope, parentScope);
|
||||
if (me.override) {
|
||||
if (this.override) {
|
||||
defaults.override(item.id, item.overrides);
|
||||
}
|
||||
|
||||
|
||||
@ -318,14 +318,13 @@ export default class ArcElement extends Element {
|
||||
}
|
||||
|
||||
draw(ctx) {
|
||||
const me = this;
|
||||
const {options, circumference} = me;
|
||||
const {options, circumference} = this;
|
||||
const offset = (options.offset || 0) / 2;
|
||||
const spacing = (options.spacing || 0) / 2;
|
||||
me.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
|
||||
me.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
|
||||
this.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
|
||||
this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
|
||||
|
||||
if (circumference === 0 || me.innerRadius < 0 || me.outerRadius < 0) {
|
||||
if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -334,9 +333,9 @@ export default class ArcElement extends Element {
|
||||
let radiusOffset = 0;
|
||||
if (offset) {
|
||||
radiusOffset = offset / 2;
|
||||
const halfAngle = (me.startAngle + me.endAngle) / 2;
|
||||
const halfAngle = (this.startAngle + this.endAngle) / 2;
|
||||
ctx.translate(Math.cos(halfAngle) * radiusOffset, Math.sin(halfAngle) * radiusOffset);
|
||||
if (me.circumference >= PI) {
|
||||
if (this.circumference >= PI) {
|
||||
radiusOffset = offset;
|
||||
}
|
||||
}
|
||||
@ -344,8 +343,8 @@ export default class ArcElement extends Element {
|
||||
ctx.fillStyle = options.backgroundColor;
|
||||
ctx.strokeStyle = options.borderColor;
|
||||
|
||||
const endAngle = drawArc(ctx, me, radiusOffset, spacing);
|
||||
drawBorder(ctx, me, radiusOffset, spacing, endAngle);
|
||||
const endAngle = drawArc(ctx, this, radiusOffset, spacing);
|
||||
drawBorder(ctx, this, radiusOffset, spacing, endAngle);
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
@ -259,21 +259,19 @@ export default class LineElement extends Element {
|
||||
}
|
||||
|
||||
updateControlPoints(chartArea, indexAxis) {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !me._pointsUpdated) {
|
||||
const loop = options.spanGaps ? me._loop : me._fullLoop;
|
||||
_updateBezierControlPoints(me._points, options, chartArea, loop, indexAxis);
|
||||
me._pointsUpdated = true;
|
||||
const options = this.options;
|
||||
if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !this._pointsUpdated) {
|
||||
const loop = options.spanGaps ? this._loop : this._fullLoop;
|
||||
_updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis);
|
||||
this._pointsUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
set points(points) {
|
||||
const me = this;
|
||||
me._points = points;
|
||||
delete me._segments;
|
||||
delete me._path;
|
||||
me._pointsUpdated = false;
|
||||
this._points = points;
|
||||
delete this._segments;
|
||||
delete this._path;
|
||||
this._pointsUpdated = false;
|
||||
}
|
||||
|
||||
get points() {
|
||||
@ -313,11 +311,10 @@ export default class LineElement extends Element {
|
||||
* @returns {PointElement|undefined}
|
||||
*/
|
||||
interpolate(point, property) {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const options = this.options;
|
||||
const value = point[property];
|
||||
const points = me.points;
|
||||
const segments = _boundSegments(me, {property, start: value, end: value});
|
||||
const points = this.points;
|
||||
const segments = _boundSegments(this, {property, start: value, end: value});
|
||||
|
||||
if (!segments.length) {
|
||||
return;
|
||||
@ -369,16 +366,15 @@ export default class LineElement extends Element {
|
||||
* @returns {undefined|boolean} - true if line is a full loop (path should be closed)
|
||||
*/
|
||||
path(ctx, start, count) {
|
||||
const me = this;
|
||||
const segments = me.segments;
|
||||
const segmentMethod = _getSegmentMethod(me);
|
||||
let loop = me._loop;
|
||||
const segments = this.segments;
|
||||
const segmentMethod = _getSegmentMethod(this);
|
||||
let loop = this._loop;
|
||||
|
||||
start = start || 0;
|
||||
count = count || (me.points.length - start);
|
||||
count = count || (this.points.length - start);
|
||||
|
||||
for (const segment of segments) {
|
||||
loop &= segmentMethod(ctx, me, segment, {start, end: start + count - 1});
|
||||
loop &= segmentMethod(ctx, this, segment, {start, end: start + count - 1});
|
||||
}
|
||||
return !!loop;
|
||||
}
|
||||
@ -391,22 +387,21 @@ export default class LineElement extends Element {
|
||||
* @param {number} [count]
|
||||
*/
|
||||
draw(ctx, chartArea, start, count) {
|
||||
const me = this;
|
||||
const options = me.options || {};
|
||||
const points = me.points || [];
|
||||
const options = this.options || {};
|
||||
const points = this.points || [];
|
||||
|
||||
if (points.length && options.borderWidth) {
|
||||
ctx.save();
|
||||
|
||||
draw(ctx, me, start, count);
|
||||
draw(ctx, this, start, count);
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
if (me.animated) {
|
||||
if (this.animated) {
|
||||
// When line is animated, the control points and path are not cached.
|
||||
me._pointsUpdated = false;
|
||||
me._path = undefined;
|
||||
this._pointsUpdated = false;
|
||||
this._path = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,17 +51,16 @@ export default class PointElement extends Element {
|
||||
}
|
||||
|
||||
draw(ctx, area) {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const options = this.options;
|
||||
|
||||
if (me.skip || options.radius < 0.1 || !_isPointInArea(me, area, me.size(options) / 2)) {
|
||||
if (this.skip || options.radius < 0.1 || !_isPointInArea(this, area, this.size(options) / 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.strokeStyle = options.borderColor;
|
||||
ctx.lineWidth = options.borderWidth;
|
||||
ctx.fillStyle = options.backgroundColor;
|
||||
drawPoint(ctx, options, me.x, me.y);
|
||||
drawPoint(ctx, options, this.x, this.y);
|
||||
}
|
||||
|
||||
getRange() {
|
||||
|
||||
@ -72,95 +72,88 @@ export class Legend extends Element {
|
||||
}
|
||||
|
||||
update(maxWidth, maxHeight, margins) {
|
||||
const me = this;
|
||||
this.maxWidth = maxWidth;
|
||||
this.maxHeight = maxHeight;
|
||||
this._margins = margins;
|
||||
|
||||
me.maxWidth = maxWidth;
|
||||
me.maxHeight = maxHeight;
|
||||
me._margins = margins;
|
||||
|
||||
me.setDimensions();
|
||||
me.buildLabels();
|
||||
me.fit();
|
||||
this.setDimensions();
|
||||
this.buildLabels();
|
||||
this.fit();
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
const me = this;
|
||||
|
||||
if (me.isHorizontal()) {
|
||||
me.width = me.maxWidth;
|
||||
me.left = me._margins.left;
|
||||
me.right = me.width;
|
||||
if (this.isHorizontal()) {
|
||||
this.width = this.maxWidth;
|
||||
this.left = this._margins.left;
|
||||
this.right = this.width;
|
||||
} else {
|
||||
me.height = me.maxHeight;
|
||||
me.top = me._margins.top;
|
||||
me.bottom = me.height;
|
||||
this.height = this.maxHeight;
|
||||
this.top = this._margins.top;
|
||||
this.bottom = this.height;
|
||||
}
|
||||
}
|
||||
|
||||
buildLabels() {
|
||||
const me = this;
|
||||
const labelOpts = me.options.labels || {};
|
||||
let legendItems = call(labelOpts.generateLabels, [me.chart], me) || [];
|
||||
const labelOpts = this.options.labels || {};
|
||||
let legendItems = call(labelOpts.generateLabels, [this.chart], this) || [];
|
||||
|
||||
if (labelOpts.filter) {
|
||||
legendItems = legendItems.filter((item) => labelOpts.filter(item, me.chart.data));
|
||||
legendItems = legendItems.filter((item) => labelOpts.filter(item, this.chart.data));
|
||||
}
|
||||
|
||||
if (labelOpts.sort) {
|
||||
legendItems = legendItems.sort((a, b) => labelOpts.sort(a, b, me.chart.data));
|
||||
legendItems = legendItems.sort((a, b) => labelOpts.sort(a, b, this.chart.data));
|
||||
}
|
||||
|
||||
if (me.options.reverse) {
|
||||
if (this.options.reverse) {
|
||||
legendItems.reverse();
|
||||
}
|
||||
|
||||
me.legendItems = legendItems;
|
||||
this.legendItems = legendItems;
|
||||
}
|
||||
|
||||
fit() {
|
||||
const me = this;
|
||||
const {options, ctx} = me;
|
||||
const {options, ctx} = this;
|
||||
|
||||
// The legend may not be displayed for a variety of reasons including
|
||||
// the fact that the defaults got set to `false`.
|
||||
// When the legend is not displayed, there are no guarantees that the options
|
||||
// are correctly formatted so we need to bail out as early as possible.
|
||||
if (!options.display) {
|
||||
me.width = me.height = 0;
|
||||
this.width = this.height = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const labelOpts = options.labels;
|
||||
const labelFont = toFont(labelOpts.font);
|
||||
const fontSize = labelFont.size;
|
||||
const titleHeight = me._computeTitleHeight();
|
||||
const titleHeight = this._computeTitleHeight();
|
||||
const {boxWidth, itemHeight} = getBoxSize(labelOpts, fontSize);
|
||||
|
||||
let width, height;
|
||||
|
||||
ctx.font = labelFont.string;
|
||||
|
||||
if (me.isHorizontal()) {
|
||||
width = me.maxWidth; // fill all the width
|
||||
height = me._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;
|
||||
if (this.isHorizontal()) {
|
||||
width = this.maxWidth; // fill all the width
|
||||
height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;
|
||||
} else {
|
||||
height = me.maxHeight; // fill all the height
|
||||
width = me._fitCols(titleHeight, fontSize, boxWidth, itemHeight) + 10;
|
||||
height = this.maxHeight; // fill all the height
|
||||
width = this._fitCols(titleHeight, fontSize, boxWidth, itemHeight) + 10;
|
||||
}
|
||||
|
||||
me.width = Math.min(width, options.maxWidth || me.maxWidth);
|
||||
me.height = Math.min(height, options.maxHeight || me.maxHeight);
|
||||
this.width = Math.min(width, options.maxWidth || this.maxWidth);
|
||||
this.height = Math.min(height, options.maxHeight || this.maxHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_fitRows(titleHeight, fontSize, boxWidth, itemHeight) {
|
||||
const me = this;
|
||||
const {ctx, maxWidth, options: {labels: {padding}}} = me;
|
||||
const hitboxes = me.legendHitBoxes = [];
|
||||
const {ctx, maxWidth, options: {labels: {padding}}} = this;
|
||||
const hitboxes = this.legendHitBoxes = [];
|
||||
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
|
||||
const lineWidths = me.lineWidths = [0];
|
||||
const lineWidths = this.lineWidths = [0];
|
||||
const lineHeight = itemHeight + padding;
|
||||
let totalHeight = titleHeight;
|
||||
|
||||
@ -169,7 +162,7 @@ export class Legend extends Element {
|
||||
|
||||
let row = -1;
|
||||
let top = -lineHeight;
|
||||
me.legendItems.forEach((legendItem, i) => {
|
||||
this.legendItems.forEach((legendItem, i) => {
|
||||
const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||||
|
||||
if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {
|
||||
@ -188,10 +181,9 @@ export class Legend extends Element {
|
||||
}
|
||||
|
||||
_fitCols(titleHeight, fontSize, boxWidth, itemHeight) {
|
||||
const me = this;
|
||||
const {ctx, maxHeight, options: {labels: {padding}}} = me;
|
||||
const hitboxes = me.legendHitBoxes = [];
|
||||
const columnSizes = me.columnSizes = [];
|
||||
const {ctx, maxHeight, options: {labels: {padding}}} = this;
|
||||
const hitboxes = this.legendHitBoxes = [];
|
||||
const columnSizes = this.columnSizes = [];
|
||||
const heightLimit = maxHeight - titleHeight;
|
||||
|
||||
let totalWidth = padding;
|
||||
@ -201,7 +193,7 @@ export class Legend extends Element {
|
||||
let left = 0;
|
||||
let col = 0;
|
||||
|
||||
me.legendItems.forEach((legendItem, i) => {
|
||||
this.legendItems.forEach((legendItem, i) => {
|
||||
const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||||
|
||||
// If too tall, go to new column
|
||||
@ -228,35 +220,34 @@ export class Legend extends Element {
|
||||
}
|
||||
|
||||
adjustHitBoxes() {
|
||||
const me = this;
|
||||
if (!me.options.display) {
|
||||
if (!this.options.display) {
|
||||
return;
|
||||
}
|
||||
const titleHeight = me._computeTitleHeight();
|
||||
const {legendHitBoxes: hitboxes, options: {align, labels: {padding}, rtl}} = me;
|
||||
const rtlHelper = getRtlAdapter(rtl, me.left, me.width);
|
||||
const titleHeight = this._computeTitleHeight();
|
||||
const {legendHitBoxes: hitboxes, options: {align, labels: {padding}, rtl}} = this;
|
||||
const rtlHelper = getRtlAdapter(rtl, this.left, this.width);
|
||||
if (this.isHorizontal()) {
|
||||
let row = 0;
|
||||
let left = _alignStartEnd(align, me.left + padding, me.right - me.lineWidths[row]);
|
||||
let left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);
|
||||
for (const hitbox of hitboxes) {
|
||||
if (row !== hitbox.row) {
|
||||
row = hitbox.row;
|
||||
left = _alignStartEnd(align, me.left + padding, me.right - me.lineWidths[row]);
|
||||
left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);
|
||||
}
|
||||
hitbox.top += me.top + titleHeight + padding;
|
||||
hitbox.top += this.top + titleHeight + padding;
|
||||
hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width);
|
||||
left += hitbox.width + padding;
|
||||
}
|
||||
} else {
|
||||
let col = 0;
|
||||
let top = _alignStartEnd(align, me.top + titleHeight + padding, me.bottom - me.columnSizes[col].height);
|
||||
let top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);
|
||||
for (const hitbox of hitboxes) {
|
||||
if (hitbox.col !== col) {
|
||||
col = hitbox.col;
|
||||
top = _alignStartEnd(align, me.top + titleHeight + padding, me.bottom - me.columnSizes[col].height);
|
||||
top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);
|
||||
}
|
||||
hitbox.top = top;
|
||||
hitbox.left += me.left + padding;
|
||||
hitbox.left += this.left + padding;
|
||||
hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox.left), hitbox.width);
|
||||
top += hitbox.height + padding;
|
||||
}
|
||||
@ -268,12 +259,11 @@ export class Legend extends Element {
|
||||
}
|
||||
|
||||
draw() {
|
||||
const me = this;
|
||||
if (me.options.display) {
|
||||
const ctx = me.ctx;
|
||||
clipArea(ctx, me);
|
||||
if (this.options.display) {
|
||||
const ctx = this.ctx;
|
||||
clipArea(ctx, this);
|
||||
|
||||
me._draw();
|
||||
this._draw();
|
||||
|
||||
unclipArea(ctx);
|
||||
}
|
||||
@ -283,18 +273,17 @@ export class Legend extends Element {
|
||||
* @private
|
||||
*/
|
||||
_draw() {
|
||||
const me = this;
|
||||
const {options: opts, columnSizes, lineWidths, ctx} = me;
|
||||
const {options: opts, columnSizes, lineWidths, ctx} = this;
|
||||
const {align, labels: labelOpts} = opts;
|
||||
const defaultColor = defaults.color;
|
||||
const rtlHelper = getRtlAdapter(opts.rtl, me.left, me.width);
|
||||
const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);
|
||||
const labelFont = toFont(labelOpts.font);
|
||||
const {color: fontColor, padding} = labelOpts;
|
||||
const fontSize = labelFont.size;
|
||||
const halfFontSize = fontSize / 2;
|
||||
let cursor;
|
||||
|
||||
me.drawTitle();
|
||||
this.drawTitle();
|
||||
|
||||
// Canvas setup
|
||||
ctx.textAlign = rtlHelper.textAlign('left');
|
||||
@ -375,26 +364,26 @@ export class Legend extends Element {
|
||||
};
|
||||
|
||||
// Horizontal
|
||||
const isHorizontal = me.isHorizontal();
|
||||
const isHorizontal = this.isHorizontal();
|
||||
const titleHeight = this._computeTitleHeight();
|
||||
if (isHorizontal) {
|
||||
cursor = {
|
||||
x: _alignStartEnd(align, me.left + padding, me.right - lineWidths[0]),
|
||||
y: me.top + padding + titleHeight,
|
||||
x: _alignStartEnd(align, this.left + padding, this.right - lineWidths[0]),
|
||||
y: this.top + padding + titleHeight,
|
||||
line: 0
|
||||
};
|
||||
} else {
|
||||
cursor = {
|
||||
x: me.left + padding,
|
||||
y: _alignStartEnd(align, me.top + titleHeight + padding, me.bottom - columnSizes[0].height),
|
||||
x: this.left + padding,
|
||||
y: _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[0].height),
|
||||
line: 0
|
||||
};
|
||||
}
|
||||
|
||||
overrideTextDirection(me.ctx, opts.textDirection);
|
||||
overrideTextDirection(this.ctx, opts.textDirection);
|
||||
|
||||
const lineHeight = itemHeight + padding;
|
||||
me.legendItems.forEach((legendItem, i) => {
|
||||
this.legendItems.forEach((legendItem, i) => {
|
||||
// TODO: Remove fallbacks at v4
|
||||
ctx.strokeStyle = legendItem.fontColor || fontColor; // for strikethrough effect
|
||||
ctx.fillStyle = legendItem.fontColor || fontColor; // render in correct colour
|
||||
@ -405,25 +394,25 @@ export class Legend extends Element {
|
||||
let x = cursor.x;
|
||||
let y = cursor.y;
|
||||
|
||||
rtlHelper.setWidth(me.width);
|
||||
rtlHelper.setWidth(this.width);
|
||||
|
||||
if (isHorizontal) {
|
||||
if (i > 0 && x + width + padding > me.right) {
|
||||
if (i > 0 && x + width + padding > this.right) {
|
||||
y = cursor.y += lineHeight;
|
||||
cursor.line++;
|
||||
x = cursor.x = _alignStartEnd(align, me.left + padding, me.right - lineWidths[cursor.line]);
|
||||
x = cursor.x = _alignStartEnd(align, this.left + padding, this.right - lineWidths[cursor.line]);
|
||||
}
|
||||
} else if (i > 0 && y + lineHeight > me.bottom) {
|
||||
} else if (i > 0 && y + lineHeight > this.bottom) {
|
||||
x = cursor.x = x + columnSizes[cursor.line].width + padding;
|
||||
cursor.line++;
|
||||
y = cursor.y = _alignStartEnd(align, me.top + titleHeight + padding, me.bottom - columnSizes[cursor.line].height);
|
||||
y = cursor.y = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[cursor.line].height);
|
||||
}
|
||||
|
||||
const realX = rtlHelper.x(x);
|
||||
|
||||
drawLegendBox(realX, y, legendItem);
|
||||
|
||||
x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : me.right, opts.rtl);
|
||||
x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl);
|
||||
|
||||
// Fill the actual label
|
||||
fillText(rtlHelper.x(x), y, legendItem);
|
||||
@ -435,15 +424,14 @@ export class Legend extends Element {
|
||||
}
|
||||
});
|
||||
|
||||
restoreTextDirection(me.ctx, opts.textDirection);
|
||||
restoreTextDirection(this.ctx, opts.textDirection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
drawTitle() {
|
||||
const me = this;
|
||||
const opts = me.options;
|
||||
const opts = this.options;
|
||||
const titleOpts = opts.title;
|
||||
const titleFont = toFont(titleOpts.font);
|
||||
const titlePadding = toPadding(titleOpts.padding);
|
||||
@ -452,8 +440,8 @@ export class Legend extends Element {
|
||||
return;
|
||||
}
|
||||
|
||||
const rtlHelper = getRtlAdapter(opts.rtl, me.left, me.width);
|
||||
const ctx = me.ctx;
|
||||
const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);
|
||||
const ctx = this.ctx;
|
||||
const position = titleOpts.position;
|
||||
const halfFontSize = titleFont.size / 2;
|
||||
const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize;
|
||||
@ -461,18 +449,18 @@ export class Legend extends Element {
|
||||
|
||||
// These defaults are used when the legend is vertical.
|
||||
// When horizontal, they are computed below.
|
||||
let left = me.left;
|
||||
let maxWidth = me.width;
|
||||
let left = this.left;
|
||||
let maxWidth = this.width;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
// Move left / right so that the title is above the legend lines
|
||||
maxWidth = Math.max(...me.lineWidths);
|
||||
y = me.top + topPaddingPlusHalfFontSize;
|
||||
left = _alignStartEnd(opts.align, left, me.right - maxWidth);
|
||||
maxWidth = Math.max(...this.lineWidths);
|
||||
y = this.top + topPaddingPlusHalfFontSize;
|
||||
left = _alignStartEnd(opts.align, left, this.right - maxWidth);
|
||||
} else {
|
||||
// Move down so that the title is above the legend stack in every alignment
|
||||
const maxHeight = me.columnSizes.reduce((acc, size) => Math.max(acc, size.height), 0);
|
||||
y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, me.top, me.bottom - maxHeight - opts.labels.padding - me._computeTitleHeight());
|
||||
const maxHeight = this.columnSizes.reduce((acc, size) => Math.max(acc, size.height), 0);
|
||||
y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, this.top, this.bottom - maxHeight - opts.labels.padding - this._computeTitleHeight());
|
||||
}
|
||||
|
||||
// Now that we know the left edge of the inner legend box, compute the correct
|
||||
@ -503,18 +491,17 @@ export class Legend extends Element {
|
||||
* @private
|
||||
*/
|
||||
_getLegendItemAt(x, y) {
|
||||
const me = this;
|
||||
let i, hitBox, lh;
|
||||
|
||||
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
|
||||
if (x >= this.left && x <= this.right && y >= this.top && y <= this.bottom) {
|
||||
// See if we are touching one of the dataset boxes
|
||||
lh = me.legendHitBoxes;
|
||||
lh = this.legendHitBoxes;
|
||||
for (i = 0; i < lh.length; ++i) {
|
||||
hitBox = lh[i];
|
||||
|
||||
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
|
||||
// Touching an element
|
||||
return me.legendItems[i];
|
||||
return this.legendItems[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -527,29 +514,28 @@ export class Legend extends Element {
|
||||
* @param {ChartEvent} e - The event to handle
|
||||
*/
|
||||
handleEvent(e) {
|
||||
const me = this;
|
||||
const opts = me.options;
|
||||
const opts = this.options;
|
||||
if (!isListened(e.type, opts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chart event already has relative position in it
|
||||
const hoveredItem = me._getLegendItemAt(e.x, e.y);
|
||||
const hoveredItem = this._getLegendItemAt(e.x, e.y);
|
||||
|
||||
if (e.type === 'mousemove') {
|
||||
const previous = me._hoveredItem;
|
||||
const previous = this._hoveredItem;
|
||||
const sameItem = itemsEqual(previous, hoveredItem);
|
||||
if (previous && !sameItem) {
|
||||
call(opts.onLeave, [e, previous, me], me);
|
||||
call(opts.onLeave, [e, previous, this], this);
|
||||
}
|
||||
|
||||
me._hoveredItem = hoveredItem;
|
||||
this._hoveredItem = hoveredItem;
|
||||
|
||||
if (hoveredItem && !sameItem) {
|
||||
call(opts.onHover, [e, hoveredItem, me], me);
|
||||
call(opts.onHover, [e, hoveredItem, this], this);
|
||||
}
|
||||
} else if (hoveredItem) {
|
||||
call(opts.onClick, [e, hoveredItem, me], me);
|
||||
call(opts.onClick, [e, hoveredItem, this], this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,28 +27,27 @@ export class Title extends Element {
|
||||
}
|
||||
|
||||
update(maxWidth, maxHeight) {
|
||||
const me = this;
|
||||
const opts = me.options;
|
||||
const opts = this.options;
|
||||
|
||||
me.left = 0;
|
||||
me.top = 0;
|
||||
this.left = 0;
|
||||
this.top = 0;
|
||||
|
||||
if (!opts.display) {
|
||||
me.width = me.height = me.right = me.bottom = 0;
|
||||
this.width = this.height = this.right = this.bottom = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
me.width = me.right = maxWidth;
|
||||
me.height = me.bottom = maxHeight;
|
||||
this.width = this.right = maxWidth;
|
||||
this.height = this.bottom = maxHeight;
|
||||
|
||||
const lineCount = isArray(opts.text) ? opts.text.length : 1;
|
||||
me._padding = toPadding(opts.padding);
|
||||
const textSize = lineCount * toFont(opts.font).lineHeight + me._padding.height;
|
||||
this._padding = toPadding(opts.padding);
|
||||
const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height;
|
||||
|
||||
if (me.isHorizontal()) {
|
||||
me.height = textSize;
|
||||
if (this.isHorizontal()) {
|
||||
this.height = textSize;
|
||||
} else {
|
||||
me.width = textSize;
|
||||
this.width = textSize;
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,9 +82,8 @@ export class Title extends Element {
|
||||
}
|
||||
|
||||
draw() {
|
||||
const me = this;
|
||||
const ctx = me.ctx;
|
||||
const opts = me.options;
|
||||
const ctx = this.ctx;
|
||||
const opts = this.options;
|
||||
|
||||
if (!opts.display) {
|
||||
return;
|
||||
@ -93,8 +91,8 @@ export class Title extends Element {
|
||||
|
||||
const fontOpts = toFont(opts.font);
|
||||
const lineHeight = fontOpts.lineHeight;
|
||||
const offset = lineHeight / 2 + me._padding.top;
|
||||
const {titleX, titleY, maxWidth, rotation} = me._drawArgs(offset);
|
||||
const offset = lineHeight / 2 + this._padding.top;
|
||||
const {titleX, titleY, maxWidth, rotation} = this._drawArgs(offset);
|
||||
|
||||
renderText(ctx, opts.text, 0, 0, fontOpts, {
|
||||
color: opts.color,
|
||||
|
||||
@ -392,19 +392,18 @@ export class Tooltip extends Element {
|
||||
* @private
|
||||
*/
|
||||
_resolveAnimations() {
|
||||
const me = this;
|
||||
const cached = me._cachedAnimations;
|
||||
const cached = this._cachedAnimations;
|
||||
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const chart = me._chart;
|
||||
const options = me.options.setContext(me.getContext());
|
||||
const chart = this._chart;
|
||||
const options = this.options.setContext(this.getContext());
|
||||
const opts = options.enabled && chart.options.animation && options.animations;
|
||||
const animations = new Animations(me._chart, opts);
|
||||
const animations = new Animations(this._chart, opts);
|
||||
if (opts._cacheable) {
|
||||
me._cachedAnimations = Object.freeze(animations);
|
||||
this._cachedAnimations = Object.freeze(animations);
|
||||
}
|
||||
|
||||
return animations;
|
||||
@ -414,18 +413,16 @@ export class Tooltip extends Element {
|
||||
* @protected
|
||||
*/
|
||||
getContext() {
|
||||
const me = this;
|
||||
return me.$context ||
|
||||
(me.$context = createTooltipContext(me._chart.getContext(), me, me._tooltipItems));
|
||||
return this.$context ||
|
||||
(this.$context = createTooltipContext(this._chart.getContext(), this, this._tooltipItems));
|
||||
}
|
||||
|
||||
getTitle(context, options) {
|
||||
const me = this;
|
||||
const {callbacks} = options;
|
||||
|
||||
const beforeTitle = callbacks.beforeTitle.apply(me, [context]);
|
||||
const title = callbacks.title.apply(me, [context]);
|
||||
const afterTitle = callbacks.afterTitle.apply(me, [context]);
|
||||
const beforeTitle = callbacks.beforeTitle.apply(this, [context]);
|
||||
const title = callbacks.title.apply(this, [context]);
|
||||
const afterTitle = callbacks.afterTitle.apply(this, [context]);
|
||||
|
||||
let lines = [];
|
||||
lines = pushOrConcat(lines, splitNewlines(beforeTitle));
|
||||
@ -440,7 +437,6 @@ export class Tooltip extends Element {
|
||||
}
|
||||
|
||||
getBody(tooltipItems, options) {
|
||||
const me = this;
|
||||
const {callbacks} = options;
|
||||
const bodyItems = [];
|
||||
|
||||
@ -451,9 +447,9 @@ export class Tooltip extends Element {
|
||||
after: []
|
||||
};
|
||||
const scoped = overrideCallbacks(callbacks, context);
|
||||
pushOrConcat(bodyItem.before, splitNewlines(scoped.beforeLabel.call(me, context)));
|
||||
pushOrConcat(bodyItem.lines, scoped.label.call(me, context));
|
||||
pushOrConcat(bodyItem.after, splitNewlines(scoped.afterLabel.call(me, context)));
|
||||
pushOrConcat(bodyItem.before, splitNewlines(scoped.beforeLabel.call(this, context)));
|
||||
pushOrConcat(bodyItem.lines, scoped.label.call(this, context));
|
||||
pushOrConcat(bodyItem.after, splitNewlines(scoped.afterLabel.call(this, context)));
|
||||
|
||||
bodyItems.push(bodyItem);
|
||||
});
|
||||
@ -467,12 +463,11 @@ export class Tooltip extends Element {
|
||||
|
||||
// Get the footer and beforeFooter and afterFooter lines
|
||||
getFooter(tooltipItems, options) {
|
||||
const me = this;
|
||||
const {callbacks} = options;
|
||||
|
||||
const beforeFooter = callbacks.beforeFooter.apply(me, [tooltipItems]);
|
||||
const footer = callbacks.footer.apply(me, [tooltipItems]);
|
||||
const afterFooter = callbacks.afterFooter.apply(me, [tooltipItems]);
|
||||
const beforeFooter = callbacks.beforeFooter.apply(this, [tooltipItems]);
|
||||
const footer = callbacks.footer.apply(this, [tooltipItems]);
|
||||
const afterFooter = callbacks.afterFooter.apply(this, [tooltipItems]);
|
||||
|
||||
let lines = [];
|
||||
lines = pushOrConcat(lines, splitNewlines(beforeFooter));
|
||||
@ -486,9 +481,8 @@ export class Tooltip extends Element {
|
||||
* @private
|
||||
*/
|
||||
_createItems(options) {
|
||||
const me = this;
|
||||
const active = me._active;
|
||||
const data = me._chart.data;
|
||||
const active = this._active;
|
||||
const data = this._chart.data;
|
||||
const labelColors = [];
|
||||
const labelPointStyles = [];
|
||||
const labelTextColors = [];
|
||||
@ -496,7 +490,7 @@ export class Tooltip extends Element {
|
||||
let i, len;
|
||||
|
||||
for (i = 0, len = active.length; i < len; ++i) {
|
||||
tooltipItems.push(createTooltipItem(me._chart, active[i]));
|
||||
tooltipItems.push(createTooltipItem(this._chart, active[i]));
|
||||
}
|
||||
|
||||
// If the user provided a filter function, use it to modify the tooltip items
|
||||
@ -512,48 +506,47 @@ export class Tooltip extends Element {
|
||||
// Determine colors for boxes
|
||||
each(tooltipItems, (context) => {
|
||||
const scoped = overrideCallbacks(options.callbacks, context);
|
||||
labelColors.push(scoped.labelColor.call(me, context));
|
||||
labelPointStyles.push(scoped.labelPointStyle.call(me, context));
|
||||
labelTextColors.push(scoped.labelTextColor.call(me, context));
|
||||
labelColors.push(scoped.labelColor.call(this, context));
|
||||
labelPointStyles.push(scoped.labelPointStyle.call(this, context));
|
||||
labelTextColors.push(scoped.labelTextColor.call(this, context));
|
||||
});
|
||||
|
||||
me.labelColors = labelColors;
|
||||
me.labelPointStyles = labelPointStyles;
|
||||
me.labelTextColors = labelTextColors;
|
||||
me.dataPoints = tooltipItems;
|
||||
this.labelColors = labelColors;
|
||||
this.labelPointStyles = labelPointStyles;
|
||||
this.labelTextColors = labelTextColors;
|
||||
this.dataPoints = tooltipItems;
|
||||
return tooltipItems;
|
||||
}
|
||||
|
||||
update(changed, replay) {
|
||||
const me = this;
|
||||
const options = me.options.setContext(me.getContext());
|
||||
const active = me._active;
|
||||
const options = this.options.setContext(this.getContext());
|
||||
const active = this._active;
|
||||
let properties;
|
||||
let tooltipItems = [];
|
||||
|
||||
if (!active.length) {
|
||||
if (me.opacity !== 0) {
|
||||
if (this.opacity !== 0) {
|
||||
properties = {
|
||||
opacity: 0
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const position = positioners[options.position].call(me, active, me._eventPosition);
|
||||
tooltipItems = me._createItems(options);
|
||||
const position = positioners[options.position].call(this, active, this._eventPosition);
|
||||
tooltipItems = this._createItems(options);
|
||||
|
||||
me.title = me.getTitle(tooltipItems, options);
|
||||
me.beforeBody = me.getBeforeBody(tooltipItems, options);
|
||||
me.body = me.getBody(tooltipItems, options);
|
||||
me.afterBody = me.getAfterBody(tooltipItems, options);
|
||||
me.footer = me.getFooter(tooltipItems, options);
|
||||
this.title = this.getTitle(tooltipItems, options);
|
||||
this.beforeBody = this.getBeforeBody(tooltipItems, options);
|
||||
this.body = this.getBody(tooltipItems, options);
|
||||
this.afterBody = this.getAfterBody(tooltipItems, options);
|
||||
this.footer = this.getFooter(tooltipItems, options);
|
||||
|
||||
const size = me._size = getTooltipSize(me, options);
|
||||
const size = this._size = getTooltipSize(this, options);
|
||||
const positionAndSize = Object.assign({}, position, size);
|
||||
const alignment = determineAlignment(me._chart, options, positionAndSize);
|
||||
const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, me._chart);
|
||||
const alignment = determineAlignment(this._chart, options, positionAndSize);
|
||||
const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this._chart);
|
||||
|
||||
me.xAlign = alignment.xAlign;
|
||||
me.yAlign = alignment.yAlign;
|
||||
this.xAlign = alignment.xAlign;
|
||||
this.yAlign = alignment.yAlign;
|
||||
|
||||
properties = {
|
||||
opacity: 1,
|
||||
@ -566,15 +559,15 @@ export class Tooltip extends Element {
|
||||
};
|
||||
}
|
||||
|
||||
me._tooltipItems = tooltipItems;
|
||||
me.$context = undefined;
|
||||
this._tooltipItems = tooltipItems;
|
||||
this.$context = undefined;
|
||||
|
||||
if (properties) {
|
||||
me._resolveAnimations().update(me, properties);
|
||||
this._resolveAnimations().update(this, properties);
|
||||
}
|
||||
|
||||
if (changed && options.external) {
|
||||
options.external.call(me, {chart: me._chart, tooltip: me, replay});
|
||||
options.external.call(this, {chart: this._chart, tooltip: this, replay});
|
||||
}
|
||||
}
|
||||
|
||||
@ -644,15 +637,14 @@ export class Tooltip extends Element {
|
||||
}
|
||||
|
||||
drawTitle(pt, ctx, options) {
|
||||
const me = this;
|
||||
const title = me.title;
|
||||
const title = this.title;
|
||||
const length = title.length;
|
||||
let titleFont, titleSpacing, i;
|
||||
|
||||
if (length) {
|
||||
const rtlHelper = getRtlAdapter(options.rtl, me.x, me.width);
|
||||
const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
|
||||
|
||||
pt.x = getAlignedX(me, options.titleAlign, options);
|
||||
pt.x = getAlignedX(this, options.titleAlign, options);
|
||||
|
||||
ctx.textAlign = rtlHelper.textAlign(options.titleAlign);
|
||||
ctx.textBaseline = 'middle';
|
||||
@ -678,12 +670,11 @@ export class Tooltip extends Element {
|
||||
* @private
|
||||
*/
|
||||
_drawColorBox(ctx, pt, i, rtlHelper, options) {
|
||||
const me = this;
|
||||
const labelColors = me.labelColors[i];
|
||||
const labelPointStyle = me.labelPointStyles[i];
|
||||
const labelColors = this.labelColors[i];
|
||||
const labelPointStyle = this.labelPointStyles[i];
|
||||
const {boxHeight, boxWidth, boxPadding} = options;
|
||||
const bodyFont = toFont(options.bodyFont);
|
||||
const colorX = getAlignedX(me, 'left', options);
|
||||
const colorX = getAlignedX(this, 'left', options);
|
||||
const rtlColorX = rtlHelper.x(colorX);
|
||||
const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0;
|
||||
const colorY = pt.y + yOffSet;
|
||||
@ -757,18 +748,17 @@ export class Tooltip extends Element {
|
||||
}
|
||||
|
||||
// restore fillStyle
|
||||
ctx.fillStyle = me.labelTextColors[i];
|
||||
ctx.fillStyle = this.labelTextColors[i];
|
||||
}
|
||||
|
||||
drawBody(pt, ctx, options) {
|
||||
const me = this;
|
||||
const {body} = me;
|
||||
const {body} = this;
|
||||
const {bodySpacing, bodyAlign, displayColors, boxHeight, boxWidth, boxPadding} = options;
|
||||
const bodyFont = toFont(options.bodyFont);
|
||||
let bodyLineHeight = bodyFont.lineHeight;
|
||||
let xLinePadding = 0;
|
||||
|
||||
const rtlHelper = getRtlAdapter(options.rtl, me.x, me.width);
|
||||
const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
|
||||
|
||||
const fillLineOfText = function(line) {
|
||||
ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2);
|
||||
@ -782,11 +772,11 @@ export class Tooltip extends Element {
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.font = bodyFont.string;
|
||||
|
||||
pt.x = getAlignedX(me, bodyAlignForCalculation, options);
|
||||
pt.x = getAlignedX(this, bodyAlignForCalculation, options);
|
||||
|
||||
// Before body lines
|
||||
ctx.fillStyle = options.bodyColor;
|
||||
each(me.beforeBody, fillLineOfText);
|
||||
each(this.beforeBody, fillLineOfText);
|
||||
|
||||
xLinePadding = displayColors && bodyAlignForCalculation !== 'right'
|
||||
? bodyAlign === 'center' ? (boxWidth / 2 + boxPadding) : (boxWidth + 2 + boxPadding)
|
||||
@ -795,7 +785,7 @@ export class Tooltip extends Element {
|
||||
// Draw body lines now
|
||||
for (i = 0, ilen = body.length; i < ilen; ++i) {
|
||||
bodyItem = body[i];
|
||||
textColor = me.labelTextColors[i];
|
||||
textColor = this.labelTextColors[i];
|
||||
|
||||
ctx.fillStyle = textColor;
|
||||
each(bodyItem.before, fillLineOfText);
|
||||
@ -803,7 +793,7 @@ export class Tooltip extends Element {
|
||||
lines = bodyItem.lines;
|
||||
// Draw Legend-like boxes if needed
|
||||
if (displayColors && lines.length) {
|
||||
me._drawColorBox(ctx, pt, i, rtlHelper, options);
|
||||
this._drawColorBox(ctx, pt, i, rtlHelper, options);
|
||||
bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight);
|
||||
}
|
||||
|
||||
@ -821,20 +811,19 @@ export class Tooltip extends Element {
|
||||
bodyLineHeight = bodyFont.lineHeight;
|
||||
|
||||
// After body lines
|
||||
each(me.afterBody, fillLineOfText);
|
||||
each(this.afterBody, fillLineOfText);
|
||||
pt.y -= bodySpacing; // Remove last body spacing
|
||||
}
|
||||
|
||||
drawFooter(pt, ctx, options) {
|
||||
const me = this;
|
||||
const footer = me.footer;
|
||||
const footer = this.footer;
|
||||
const length = footer.length;
|
||||
let footerFont, i;
|
||||
|
||||
if (length) {
|
||||
const rtlHelper = getRtlAdapter(options.rtl, me.x, me.width);
|
||||
const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
|
||||
|
||||
pt.x = getAlignedX(me, options.footerAlign, options);
|
||||
pt.x = getAlignedX(this, options.footerAlign, options);
|
||||
pt.y += options.footerMarginTop;
|
||||
|
||||
ctx.textAlign = rtlHelper.textAlign(options.footerAlign);
|
||||
@ -898,50 +887,48 @@ export class Tooltip extends Element {
|
||||
* @private
|
||||
*/
|
||||
_updateAnimationTarget(options) {
|
||||
const me = this;
|
||||
const chart = me._chart;
|
||||
const anims = me.$animations;
|
||||
const chart = this._chart;
|
||||
const anims = this.$animations;
|
||||
const animX = anims && anims.x;
|
||||
const animY = anims && anims.y;
|
||||
if (animX || animY) {
|
||||
const position = positioners[options.position].call(me, me._active, me._eventPosition);
|
||||
const position = positioners[options.position].call(this, this._active, this._eventPosition);
|
||||
if (!position) {
|
||||
return;
|
||||
}
|
||||
const size = me._size = getTooltipSize(me, options);
|
||||
const positionAndSize = Object.assign({}, position, me._size);
|
||||
const size = this._size = getTooltipSize(this, options);
|
||||
const positionAndSize = Object.assign({}, position, this._size);
|
||||
const alignment = determineAlignment(chart, options, positionAndSize);
|
||||
const point = getBackgroundPoint(options, positionAndSize, alignment, chart);
|
||||
if (animX._to !== point.x || animY._to !== point.y) {
|
||||
me.xAlign = alignment.xAlign;
|
||||
me.yAlign = alignment.yAlign;
|
||||
me.width = size.width;
|
||||
me.height = size.height;
|
||||
me.caretX = position.x;
|
||||
me.caretY = position.y;
|
||||
me._resolveAnimations().update(me, point);
|
||||
this.xAlign = alignment.xAlign;
|
||||
this.yAlign = alignment.yAlign;
|
||||
this.width = size.width;
|
||||
this.height = size.height;
|
||||
this.caretX = position.x;
|
||||
this.caretY = position.y;
|
||||
this._resolveAnimations().update(this, point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw(ctx) {
|
||||
const me = this;
|
||||
const options = me.options.setContext(me.getContext());
|
||||
let opacity = me.opacity;
|
||||
const options = this.options.setContext(this.getContext());
|
||||
let opacity = this.opacity;
|
||||
|
||||
if (!opacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
me._updateAnimationTarget(options);
|
||||
this._updateAnimationTarget(options);
|
||||
|
||||
const tooltipSize = {
|
||||
width: me.width,
|
||||
height: me.height
|
||||
width: this.width,
|
||||
height: this.height
|
||||
};
|
||||
const pt = {
|
||||
x: me.x,
|
||||
y: me.y
|
||||
x: this.x,
|
||||
y: this.y
|
||||
};
|
||||
|
||||
// IE11/Edge does not like very small opacities, so snap to 0
|
||||
@ -950,27 +937,27 @@ export class Tooltip extends Element {
|
||||
const padding = toPadding(options.padding);
|
||||
|
||||
// Truthy/falsey value for empty tooltip
|
||||
const hasTooltipContent = me.title.length || me.beforeBody.length || me.body.length || me.afterBody.length || me.footer.length;
|
||||
const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length;
|
||||
|
||||
if (options.enabled && hasTooltipContent) {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = opacity;
|
||||
|
||||
// Draw Background
|
||||
me.drawBackground(pt, ctx, tooltipSize, options);
|
||||
this.drawBackground(pt, ctx, tooltipSize, options);
|
||||
|
||||
overrideTextDirection(ctx, options.textDirection);
|
||||
|
||||
pt.y += padding.top;
|
||||
|
||||
// Titles
|
||||
me.drawTitle(pt, ctx, options);
|
||||
this.drawTitle(pt, ctx, options);
|
||||
|
||||
// Body
|
||||
me.drawBody(pt, ctx, options);
|
||||
this.drawBody(pt, ctx, options);
|
||||
|
||||
// Footer
|
||||
me.drawFooter(pt, ctx, options);
|
||||
this.drawFooter(pt, ctx, options);
|
||||
|
||||
restoreTextDirection(ctx, options.textDirection);
|
||||
|
||||
@ -992,10 +979,9 @@ export class Tooltip extends Element {
|
||||
* @param {object} eventPosition Synthetic event position used in positioning
|
||||
*/
|
||||
setActiveElements(activeElements, eventPosition) {
|
||||
const me = this;
|
||||
const lastActive = me._active;
|
||||
const lastActive = this._active;
|
||||
const active = activeElements.map(({datasetIndex, index}) => {
|
||||
const meta = me._chart.getDatasetMeta(datasetIndex);
|
||||
const meta = this._chart.getDatasetMeta(datasetIndex);
|
||||
|
||||
if (!meta) {
|
||||
throw new Error('Cannot find a dataset at index ' + datasetIndex);
|
||||
@ -1008,12 +994,12 @@ export class Tooltip extends Element {
|
||||
};
|
||||
});
|
||||
const changed = !_elementsEqual(lastActive, active);
|
||||
const positionChanged = me._positionChanged(active, eventPosition);
|
||||
const positionChanged = this._positionChanged(active, eventPosition);
|
||||
|
||||
if (changed || positionChanged) {
|
||||
me._active = active;
|
||||
me._eventPosition = eventPosition;
|
||||
me.update(true);
|
||||
this._active = active;
|
||||
this._eventPosition = eventPosition;
|
||||
this.update(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1024,15 +1010,14 @@ export class Tooltip extends Element {
|
||||
* @returns {boolean} true if the tooltip changed
|
||||
*/
|
||||
handleEvent(e, replay) {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const lastActive = me._active || [];
|
||||
const options = this.options;
|
||||
const lastActive = this._active || [];
|
||||
let changed = false;
|
||||
let active = [];
|
||||
|
||||
// Find Active Elements for tooltips
|
||||
if (e.type !== 'mouseout') {
|
||||
active = me._chart.getElementsAtEventForMode(e, options.mode, options, replay);
|
||||
active = this._chart.getElementsAtEventForMode(e, options.mode, options, replay);
|
||||
if (options.reverse) {
|
||||
active.reverse();
|
||||
}
|
||||
@ -1041,22 +1026,22 @@ export class Tooltip extends Element {
|
||||
// When there are multiple items shown, but the tooltip position is nearest mode
|
||||
// an update may need to be made because our position may have changed even though
|
||||
// the items are the same as before.
|
||||
const positionChanged = me._positionChanged(active, e);
|
||||
const positionChanged = this._positionChanged(active, e);
|
||||
|
||||
// Remember Last Actives
|
||||
changed = replay || !_elementsEqual(active, lastActive) || positionChanged;
|
||||
|
||||
// Only handle target event on tooltip change
|
||||
if (changed) {
|
||||
me._active = active;
|
||||
this._active = active;
|
||||
|
||||
if (options.enabled || options.external) {
|
||||
me._eventPosition = {
|
||||
this._eventPosition = {
|
||||
x: e.x,
|
||||
y: e.y
|
||||
};
|
||||
|
||||
me.update(true, replay);
|
||||
this.update(true, replay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -37,36 +37,34 @@ export default class CategoryScale extends Scale {
|
||||
}
|
||||
|
||||
determineDataLimits() {
|
||||
const me = this;
|
||||
const {minDefined, maxDefined} = me.getUserBounds();
|
||||
let {min, max} = me.getMinMax(true);
|
||||
const {minDefined, maxDefined} = this.getUserBounds();
|
||||
let {min, max} = this.getMinMax(true);
|
||||
|
||||
if (me.options.bounds === 'ticks') {
|
||||
if (this.options.bounds === 'ticks') {
|
||||
if (!minDefined) {
|
||||
min = 0;
|
||||
}
|
||||
if (!maxDefined) {
|
||||
max = me.getLabels().length - 1;
|
||||
max = this.getLabels().length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
me.min = min;
|
||||
me.max = max;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
buildTicks() {
|
||||
const me = this;
|
||||
const min = me.min;
|
||||
const max = me.max;
|
||||
const offset = me.options.offset;
|
||||
const min = this.min;
|
||||
const max = this.max;
|
||||
const offset = this.options.offset;
|
||||
const ticks = [];
|
||||
let labels = me.getLabels();
|
||||
let labels = this.getLabels();
|
||||
|
||||
// If we are viewing some subset of labels, slice the original array
|
||||
labels = (min === 0 && max === labels.length - 1) ? labels : labels.slice(min, max + 1);
|
||||
|
||||
me._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);
|
||||
me._startValue = me.min - (offset ? 0.5 : 0);
|
||||
this._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);
|
||||
this._startValue = this.min - (offset ? 0.5 : 0);
|
||||
|
||||
for (let value = min; value <= max; value++) {
|
||||
ticks.push({value});
|
||||
@ -75,8 +73,7 @@ export default class CategoryScale extends Scale {
|
||||
}
|
||||
|
||||
getLabelForValue(value) {
|
||||
const me = this;
|
||||
const labels = me.getLabels();
|
||||
const labels = this.getLabels();
|
||||
|
||||
if (value >= 0 && value < labels.length) {
|
||||
return labels[value];
|
||||
@ -88,41 +85,35 @@ export default class CategoryScale extends Scale {
|
||||
* @protected
|
||||
*/
|
||||
configure() {
|
||||
const me = this;
|
||||
|
||||
super.configure();
|
||||
|
||||
if (!me.isHorizontal()) {
|
||||
if (!this.isHorizontal()) {
|
||||
// For backward compatibility, vertical category scale reverse is inverted.
|
||||
me._reversePixels = !me._reversePixels;
|
||||
this._reversePixels = !this._reversePixels;
|
||||
}
|
||||
}
|
||||
|
||||
// Used to get data value locations. Value can either be an index or a numerical value
|
||||
getPixelForValue(value) {
|
||||
const me = this;
|
||||
|
||||
if (typeof value !== 'number') {
|
||||
value = me.parse(value);
|
||||
value = this.parse(value);
|
||||
}
|
||||
|
||||
return value === null ? NaN : me.getPixelForDecimal((value - me._startValue) / me._valueRange);
|
||||
return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
|
||||
}
|
||||
|
||||
// Must override base implementation because it calls getPixelForValue
|
||||
// and category scale can have duplicate values
|
||||
getPixelForTick(index) {
|
||||
const me = this;
|
||||
const ticks = me.ticks;
|
||||
const ticks = this.ticks;
|
||||
if (index < 0 || index > ticks.length - 1) {
|
||||
return null;
|
||||
}
|
||||
return me.getPixelForValue(ticks[index].value);
|
||||
return this.getPixelForValue(ticks[index].value);
|
||||
}
|
||||
|
||||
getValueForPixel(pixel) {
|
||||
const me = this;
|
||||
return Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange);
|
||||
return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange);
|
||||
}
|
||||
|
||||
getBasePixel() {
|
||||
|
||||
@ -6,14 +6,13 @@ import {toRadians} from '../helpers';
|
||||
export default class LinearScale extends LinearScaleBase {
|
||||
|
||||
determineDataLimits() {
|
||||
const me = this;
|
||||
const {min, max} = me.getMinMax(true);
|
||||
const {min, max} = this.getMinMax(true);
|
||||
|
||||
me.min = isFinite(min) ? min : 0;
|
||||
me.max = isFinite(max) ? max : 1;
|
||||
this.min = isFinite(min) ? min : 0;
|
||||
this.max = isFinite(max) ? max : 1;
|
||||
|
||||
// Common base implementation to handle min, max, beginAtZero
|
||||
me.handleTickRangeOptions();
|
||||
this.handleTickRangeOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -21,12 +20,11 @@ export default class LinearScale extends LinearScaleBase {
|
||||
* @protected
|
||||
*/
|
||||
computeTickLimit() {
|
||||
const me = this;
|
||||
const horizontal = me.isHorizontal();
|
||||
const length = horizontal ? me.width : me.height;
|
||||
const minRotation = toRadians(me.options.ticks.minRotation);
|
||||
const horizontal = this.isHorizontal();
|
||||
const length = horizontal ? this.width : this.height;
|
||||
const minRotation = toRadians(this.options.ticks.minRotation);
|
||||
const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 0.001;
|
||||
const tickFont = me._resolveTickFontOptions(0);
|
||||
const tickFont = this._resolveTickFontOptions(0);
|
||||
return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));
|
||||
}
|
||||
|
||||
|
||||
@ -175,10 +175,9 @@ export default class LinearScaleBase extends Scale {
|
||||
}
|
||||
|
||||
handleTickRangeOptions() {
|
||||
const me = this;
|
||||
const {beginAtZero} = me.options;
|
||||
const {minDefined, maxDefined} = me.getUserBounds();
|
||||
let {min, max} = me;
|
||||
const {beginAtZero} = this.options;
|
||||
const {minDefined, maxDefined} = this.getUserBounds();
|
||||
let {min, max} = this;
|
||||
|
||||
const setMin = v => (min = minDefined ? min : v);
|
||||
const setMax = v => (max = maxDefined ? max : v);
|
||||
@ -211,21 +210,20 @@ export default class LinearScaleBase extends Scale {
|
||||
setMin(min - offset);
|
||||
}
|
||||
}
|
||||
me.min = min;
|
||||
me.max = max;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
getTickLimit() {
|
||||
const me = this;
|
||||
const tickOpts = me.options.ticks;
|
||||
const tickOpts = this.options.ticks;
|
||||
// eslint-disable-next-line prefer-const
|
||||
let {maxTicksLimit, stepSize} = tickOpts;
|
||||
let maxTicks;
|
||||
|
||||
if (stepSize) {
|
||||
maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1;
|
||||
maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1;
|
||||
} else {
|
||||
maxTicks = me.computeTickLimit();
|
||||
maxTicks = this.computeTickLimit();
|
||||
maxTicksLimit = maxTicksLimit || 11;
|
||||
}
|
||||
|
||||
@ -244,15 +242,14 @@ export default class LinearScaleBase extends Scale {
|
||||
}
|
||||
|
||||
buildTicks() {
|
||||
const me = this;
|
||||
const opts = me.options;
|
||||
const opts = this.options;
|
||||
const 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
|
||||
let maxTicks = me.getTickLimit();
|
||||
let maxTicks = this.getTickLimit();
|
||||
maxTicks = Math.max(2, maxTicks);
|
||||
|
||||
const numericGeneratorOptions = {
|
||||
@ -263,28 +260,28 @@ export default class LinearScaleBase extends Scale {
|
||||
precision: tickOpts.precision,
|
||||
step: tickOpts.stepSize,
|
||||
count: tickOpts.count,
|
||||
maxDigits: me._maxDigits(),
|
||||
horizontal: me.isHorizontal(),
|
||||
maxDigits: this._maxDigits(),
|
||||
horizontal: this.isHorizontal(),
|
||||
minRotation: tickOpts.minRotation || 0,
|
||||
includeBounds: tickOpts.includeBounds !== false
|
||||
};
|
||||
const dataRange = me._range || me;
|
||||
const dataRange = this._range || this;
|
||||
const ticks = generateTicks(numericGeneratorOptions, dataRange);
|
||||
|
||||
// At this point, we need to update our max and min given the tick values,
|
||||
// since we probably have expanded the range of the scale
|
||||
if (opts.bounds === 'ticks') {
|
||||
_setMinAndMaxByKey(ticks, me, 'value');
|
||||
_setMinAndMaxByKey(ticks, this, 'value');
|
||||
}
|
||||
|
||||
if (opts.reverse) {
|
||||
ticks.reverse();
|
||||
|
||||
me.start = me.max;
|
||||
me.end = me.min;
|
||||
this.start = this.max;
|
||||
this.end = this.min;
|
||||
} else {
|
||||
me.start = me.min;
|
||||
me.end = me.max;
|
||||
this.start = this.min;
|
||||
this.end = this.max;
|
||||
}
|
||||
|
||||
return ticks;
|
||||
@ -294,21 +291,20 @@ export default class LinearScaleBase extends Scale {
|
||||
* @protected
|
||||
*/
|
||||
configure() {
|
||||
const me = this;
|
||||
const ticks = me.ticks;
|
||||
let start = me.min;
|
||||
let end = me.max;
|
||||
const ticks = this.ticks;
|
||||
let start = this.min;
|
||||
let end = this.max;
|
||||
|
||||
super.configure();
|
||||
|
||||
if (me.options.offset && ticks.length) {
|
||||
if (this.options.offset && ticks.length) {
|
||||
const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
|
||||
start -= offset;
|
||||
end += offset;
|
||||
}
|
||||
me._startValue = start;
|
||||
me._endValue = end;
|
||||
me._valueRange = end - start;
|
||||
this._startValue = start;
|
||||
this._endValue = end;
|
||||
this._valueRange = end - start;
|
||||
}
|
||||
|
||||
getLabelForValue(value) {
|
||||
|
||||
@ -68,24 +68,22 @@ export default class LogarithmicScale extends Scale {
|
||||
}
|
||||
|
||||
determineDataLimits() {
|
||||
const me = this;
|
||||
const {min, max} = me.getMinMax(true);
|
||||
const {min, max} = this.getMinMax(true);
|
||||
|
||||
me.min = isFinite(min) ? Math.max(0, min) : null;
|
||||
me.max = isFinite(max) ? Math.max(0, max) : null;
|
||||
this.min = isFinite(min) ? Math.max(0, min) : null;
|
||||
this.max = isFinite(max) ? Math.max(0, max) : null;
|
||||
|
||||
if (me.options.beginAtZero) {
|
||||
me._zero = true;
|
||||
if (this.options.beginAtZero) {
|
||||
this._zero = true;
|
||||
}
|
||||
|
||||
me.handleTickRangeOptions();
|
||||
this.handleTickRangeOptions();
|
||||
}
|
||||
|
||||
handleTickRangeOptions() {
|
||||
const me = this;
|
||||
const {minDefined, maxDefined} = me.getUserBounds();
|
||||
let min = me.min;
|
||||
let max = me.max;
|
||||
const {minDefined, maxDefined} = this.getUserBounds();
|
||||
let min = this.min;
|
||||
let max = this.max;
|
||||
|
||||
const setMin = v => (min = minDefined ? min : v);
|
||||
const setMax = v => (max = maxDefined ? max : v);
|
||||
@ -108,37 +106,36 @@ export default class LogarithmicScale extends Scale {
|
||||
}
|
||||
// if data has `0` in it or `beginAtZero` is true, min (non zero) value is at bottom
|
||||
// of scale, and it does not equal suggestedMin, lower the min bound by one exp.
|
||||
if (me._zero && me.min !== me._suggestedMin && min === exp(me.min, 0)) {
|
||||
if (this._zero && this.min !== this._suggestedMin && min === exp(this.min, 0)) {
|
||||
setMin(exp(min, -1));
|
||||
}
|
||||
me.min = min;
|
||||
me.max = max;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
buildTicks() {
|
||||
const me = this;
|
||||
const opts = me.options;
|
||||
const opts = this.options;
|
||||
|
||||
const generationOptions = {
|
||||
min: me._userMin,
|
||||
max: me._userMax
|
||||
min: this._userMin,
|
||||
max: this._userMax
|
||||
};
|
||||
const ticks = generateTicks(generationOptions, me);
|
||||
const ticks = generateTicks(generationOptions, this);
|
||||
|
||||
// At this point, we need to update our max and min given the tick values,
|
||||
// since we probably have expanded the range of the scale
|
||||
if (opts.bounds === 'ticks') {
|
||||
_setMinAndMaxByKey(ticks, me, 'value');
|
||||
_setMinAndMaxByKey(ticks, this, 'value');
|
||||
}
|
||||
|
||||
if (opts.reverse) {
|
||||
ticks.reverse();
|
||||
|
||||
me.start = me.max;
|
||||
me.end = me.min;
|
||||
this.start = this.max;
|
||||
this.end = this.min;
|
||||
} else {
|
||||
me.start = me.min;
|
||||
me.end = me.max;
|
||||
this.start = this.min;
|
||||
this.end = this.max;
|
||||
}
|
||||
|
||||
return ticks;
|
||||
@ -156,32 +153,29 @@ export default class LogarithmicScale extends Scale {
|
||||
* @protected
|
||||
*/
|
||||
configure() {
|
||||
const me = this;
|
||||
const start = me.min;
|
||||
const start = this.min;
|
||||
|
||||
super.configure();
|
||||
|
||||
me._startValue = log10(start);
|
||||
me._valueRange = log10(me.max) - log10(start);
|
||||
this._startValue = log10(start);
|
||||
this._valueRange = log10(this.max) - log10(start);
|
||||
}
|
||||
|
||||
getPixelForValue(value) {
|
||||
const me = this;
|
||||
if (value === undefined || value === 0) {
|
||||
value = me.min;
|
||||
value = this.min;
|
||||
}
|
||||
if (value === null || isNaN(value)) {
|
||||
return NaN;
|
||||
}
|
||||
return me.getPixelForDecimal(value === me.min
|
||||
return this.getPixelForDecimal(value === this.min
|
||||
? 0
|
||||
: (log10(value) - me._startValue) / me._valueRange);
|
||||
: (log10(value) - this._startValue) / this._valueRange);
|
||||
}
|
||||
|
||||
getValueForPixel(pixel) {
|
||||
const me = this;
|
||||
const decimal = me.getDecimalForPixel(pixel);
|
||||
return Math.pow(10, me._startValue + decimal * me._valueRange);
|
||||
const decimal = this.getDecimalForPixel(pixel);
|
||||
return Math.pow(10, this._startValue + decimal * this._valueRange);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -289,26 +289,23 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
const 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;
|
||||
this.width = this.maxWidth;
|
||||
this.height = this.maxHeight;
|
||||
this.paddingTop = getTickBackdropHeight(this.options) / 2;
|
||||
this.xCenter = Math.floor(this.width / 2);
|
||||
this.yCenter = Math.floor((this.height - this.paddingTop) / 2);
|
||||
this.drawingArea = Math.min(this.height - this.paddingTop, this.width) / 2;
|
||||
}
|
||||
|
||||
determineDataLimits() {
|
||||
const me = this;
|
||||
const {min, max} = me.getMinMax(false);
|
||||
const {min, max} = this.getMinMax(false);
|
||||
|
||||
me.min = isFinite(min) && !isNaN(min) ? min : 0;
|
||||
me.max = isFinite(max) && !isNaN(max) ? max : 0;
|
||||
this.min = isFinite(min) && !isNaN(min) ? min : 0;
|
||||
this.max = isFinite(max) && !isNaN(max) ? max : 0;
|
||||
|
||||
// Common base implementation to handle min, max, beginAtZero
|
||||
me.handleTickRangeOptions();
|
||||
this.handleTickRangeOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -320,25 +317,22 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
}
|
||||
|
||||
generateTickLabels(ticks) {
|
||||
const me = this;
|
||||
|
||||
LinearScaleBase.prototype.generateTickLabels.call(me, ticks);
|
||||
LinearScaleBase.prototype.generateTickLabels.call(this, ticks);
|
||||
|
||||
// Point labels
|
||||
me._pointLabels = me.getLabels().map((value, index) => {
|
||||
const label = callCallback(me.options.pointLabels.callback, [value, index], me);
|
||||
this._pointLabels = this.getLabels().map((value, index) => {
|
||||
const label = callCallback(this.options.pointLabels.callback, [value, index], this);
|
||||
return label || label === 0 ? label : '';
|
||||
});
|
||||
}
|
||||
|
||||
fit() {
|
||||
const me = this;
|
||||
const opts = me.options;
|
||||
const opts = this.options;
|
||||
|
||||
if (opts.display && opts.pointLabels.display) {
|
||||
fitWithPointLabels(me);
|
||||
fitWithPointLabels(this);
|
||||
} else {
|
||||
me.setCenterPoint(0, 0, 0, 0);
|
||||
this.setCenterPoint(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,32 +341,30 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
* @private
|
||||
*/
|
||||
_setReductions(largestPossibleRadius, furthestLimits, furthestAngles) {
|
||||
const me = this;
|
||||
let radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
|
||||
let radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
|
||||
let radiusReductionRight = Math.max(furthestLimits.r - this.width, 0) / Math.sin(furthestAngles.r);
|
||||
let radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
|
||||
let radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b);
|
||||
let radiusReductionBottom = -Math.max(furthestLimits.b - (this.height - this.paddingTop), 0) / Math.cos(furthestAngles.b);
|
||||
|
||||
radiusReductionLeft = numberOrZero(radiusReductionLeft);
|
||||
radiusReductionRight = numberOrZero(radiusReductionRight);
|
||||
radiusReductionTop = numberOrZero(radiusReductionTop);
|
||||
radiusReductionBottom = numberOrZero(radiusReductionBottom);
|
||||
|
||||
me.drawingArea = Math.max(largestPossibleRadius / 2, Math.min(
|
||||
this.drawingArea = Math.max(largestPossibleRadius / 2, Math.min(
|
||||
Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
|
||||
Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)));
|
||||
me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
|
||||
this.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
|
||||
}
|
||||
|
||||
setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {
|
||||
const me = this;
|
||||
const maxRight = me.width - rightMovement - me.drawingArea;
|
||||
const maxLeft = leftMovement + me.drawingArea;
|
||||
const maxTop = topMovement + me.drawingArea;
|
||||
const maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea;
|
||||
const maxRight = this.width - rightMovement - this.drawingArea;
|
||||
const maxLeft = leftMovement + this.drawingArea;
|
||||
const maxTop = topMovement + this.drawingArea;
|
||||
const maxBottom = (this.height - this.paddingTop) - bottomMovement - this.drawingArea;
|
||||
|
||||
me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left);
|
||||
me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop);
|
||||
this.xCenter = Math.floor(((maxLeft + maxRight) / 2) + this.left);
|
||||
this.yCenter = Math.floor(((maxTop + maxBottom) / 2) + this.top + this.paddingTop);
|
||||
}
|
||||
|
||||
getIndexAngle(index) {
|
||||
@ -382,18 +374,16 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
}
|
||||
|
||||
getDistanceFromCenterForValue(value) {
|
||||
const me = this;
|
||||
|
||||
if (isNullOrUndef(value)) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
// Take into account half font size + the yPadding of the top value
|
||||
const scalingFactor = me.drawingArea / (me.max - me.min);
|
||||
if (me.options.reverse) {
|
||||
return (me.max - value) * scalingFactor;
|
||||
const scalingFactor = this.drawingArea / (this.max - this.min);
|
||||
if (this.options.reverse) {
|
||||
return (this.max - value) * scalingFactor;
|
||||
}
|
||||
return (value - me.min) * scalingFactor;
|
||||
return (value - this.min) * scalingFactor;
|
||||
}
|
||||
|
||||
getValueForDistanceFromCenter(distance) {
|
||||
@ -401,27 +391,24 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
const me = this;
|
||||
const scaledDistance = distance / (me.drawingArea / (me.max - me.min));
|
||||
return me.options.reverse ? me.max - scaledDistance : me.min + scaledDistance;
|
||||
const scaledDistance = distance / (this.drawingArea / (this.max - this.min));
|
||||
return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance;
|
||||
}
|
||||
|
||||
getPointLabelContext(index) {
|
||||
const me = this;
|
||||
const pointLabels = me._pointLabels || [];
|
||||
const pointLabels = this._pointLabels || [];
|
||||
|
||||
if (index >= 0 && index < pointLabels.length) {
|
||||
const pointLabel = pointLabels[index];
|
||||
return createPointLabelContext(me.getContext(), index, pointLabel);
|
||||
return createPointLabelContext(this.getContext(), index, pointLabel);
|
||||
}
|
||||
}
|
||||
|
||||
getPointPosition(index, distanceFromCenter) {
|
||||
const me = this;
|
||||
const angle = me.getIndexAngle(index) - HALF_PI;
|
||||
const angle = this.getIndexAngle(index) - HALF_PI;
|
||||
return {
|
||||
x: Math.cos(angle) * distanceFromCenter + me.xCenter,
|
||||
y: Math.sin(angle) * distanceFromCenter + me.yCenter,
|
||||
x: Math.cos(angle) * distanceFromCenter + this.xCenter,
|
||||
y: Math.sin(angle) * distanceFromCenter + this.yCenter,
|
||||
angle
|
||||
};
|
||||
}
|
||||
@ -448,13 +435,12 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
* @protected
|
||||
*/
|
||||
drawBackground() {
|
||||
const me = this;
|
||||
const {backgroundColor, grid: {circular}} = me.options;
|
||||
const {backgroundColor, grid: {circular}} = this.options;
|
||||
if (backgroundColor) {
|
||||
const ctx = me.ctx;
|
||||
const ctx = this.ctx;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
pathRadiusLine(me, me.getDistanceFromCenterForValue(me._endValue), circular, me.getLabels().length);
|
||||
pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this.getLabels().length);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = backgroundColor;
|
||||
ctx.fill();
|
||||
@ -466,24 +452,23 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
* @protected
|
||||
*/
|
||||
drawGrid() {
|
||||
const me = this;
|
||||
const ctx = me.ctx;
|
||||
const opts = me.options;
|
||||
const ctx = this.ctx;
|
||||
const opts = this.options;
|
||||
const {angleLines, grid} = opts;
|
||||
const labelCount = me.getLabels().length;
|
||||
const labelCount = this.getLabels().length;
|
||||
|
||||
let i, offset, position;
|
||||
|
||||
if (opts.pointLabels.display) {
|
||||
drawPointLabels(me, labelCount);
|
||||
drawPointLabels(this, labelCount);
|
||||
}
|
||||
|
||||
if (grid.display) {
|
||||
me.ticks.forEach((tick, index) => {
|
||||
this.ticks.forEach((tick, index) => {
|
||||
if (index !== 0) {
|
||||
offset = me.getDistanceFromCenterForValue(tick.value);
|
||||
const optsAtIndex = grid.setContext(me.getContext(index - 1));
|
||||
drawRadiusLine(me, optsAtIndex, offset, labelCount);
|
||||
offset = this.getDistanceFromCenterForValue(tick.value);
|
||||
const optsAtIndex = grid.setContext(this.getContext(index - 1));
|
||||
drawRadiusLine(this, optsAtIndex, offset, labelCount);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -491,8 +476,8 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
if (angleLines.display) {
|
||||
ctx.save();
|
||||
|
||||
for (i = me.getLabels().length - 1; i >= 0; i--) {
|
||||
const optsAtIndex = angleLines.setContext(me.getPointLabelContext(i));
|
||||
for (i = this.getLabels().length - 1; i >= 0; i--) {
|
||||
const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i));
|
||||
const {color, lineWidth} = optsAtIndex;
|
||||
|
||||
if (!lineWidth || !color) {
|
||||
@ -505,10 +490,10 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
ctx.setLineDash(optsAtIndex.borderDash);
|
||||
ctx.lineDashOffset = optsAtIndex.borderDashOffset;
|
||||
|
||||
offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max);
|
||||
position = me.getPointPosition(i, offset);
|
||||
offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max);
|
||||
position = this.getPointPosition(i, offset);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(me.xCenter, me.yCenter);
|
||||
ctx.moveTo(this.xCenter, this.yCenter);
|
||||
ctx.lineTo(position.x, position.y);
|
||||
ctx.stroke();
|
||||
}
|
||||
@ -526,32 +511,31 @@ export default class RadialLinearScale extends LinearScaleBase {
|
||||
* @protected
|
||||
*/
|
||||
drawLabels() {
|
||||
const me = this;
|
||||
const ctx = me.ctx;
|
||||
const opts = me.options;
|
||||
const ctx = this.ctx;
|
||||
const opts = this.options;
|
||||
const tickOpts = opts.ticks;
|
||||
|
||||
if (!tickOpts.display) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startAngle = me.getIndexAngle(0);
|
||||
const startAngle = this.getIndexAngle(0);
|
||||
let offset, width;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(me.xCenter, me.yCenter);
|
||||
ctx.translate(this.xCenter, this.yCenter);
|
||||
ctx.rotate(startAngle);
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
|
||||
me.ticks.forEach((tick, index) => {
|
||||
this.ticks.forEach((tick, index) => {
|
||||
if (index === 0 && !opts.reverse) {
|
||||
return;
|
||||
}
|
||||
|
||||
const optsAtIndex = tickOpts.setContext(me.getContext(index));
|
||||
const optsAtIndex = tickOpts.setContext(this.getContext(index));
|
||||
const tickFont = toFont(optsAtIndex.font);
|
||||
offset = me.getDistanceFromCenterForValue(me.ticks[index].value);
|
||||
offset = this.getDistanceFromCenterForValue(this.ticks[index].value);
|
||||
|
||||
if (optsAtIndex.showLabelBackdrop) {
|
||||
ctx.font = tickFont.string;
|
||||
|
||||
@ -262,12 +262,11 @@ export default class TimeScale extends Scale {
|
||||
}
|
||||
|
||||
determineDataLimits() {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const adapter = me._adapter;
|
||||
const options = this.options;
|
||||
const adapter = this._adapter;
|
||||
const unit = options.time.unit || 'day';
|
||||
// eslint-disable-next-line prefer-const
|
||||
let {min, max, minDefined, maxDefined} = me.getUserBounds();
|
||||
let {min, max, minDefined, maxDefined} = this.getUserBounds();
|
||||
|
||||
/**
|
||||
* @param {object} bounds
|
||||
@ -284,12 +283,12 @@ export default class TimeScale extends Scale {
|
||||
// If we have user provided `min` and `max` labels / data bounds can be ignored
|
||||
if (!minDefined || !maxDefined) {
|
||||
// Labels are always considered, when user did not force bounds
|
||||
_applyBounds(me._getLabelBounds());
|
||||
_applyBounds(this._getLabelBounds());
|
||||
|
||||
// If `bounds` is `'ticks'` and `ticks.source` is `'labels'`,
|
||||
// data bounds are ignored (and don't need to be determined)
|
||||
if (options.bounds !== 'ticks' || options.ticks.source !== 'labels') {
|
||||
_applyBounds(me.getMinMax(false));
|
||||
_applyBounds(this.getMinMax(false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,8 +296,8 @@ export default class TimeScale extends Scale {
|
||||
max = isFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;
|
||||
|
||||
// Make sure that max is strictly higher than min (required by the timeseries lookup table)
|
||||
me.min = Math.min(min, max - 1);
|
||||
me.max = Math.max(min + 1, max);
|
||||
this.min = Math.min(min, max - 1);
|
||||
this.max = Math.max(min + 1, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -320,37 +319,36 @@ export default class TimeScale extends Scale {
|
||||
* @return {object[]}
|
||||
*/
|
||||
buildTicks() {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const options = this.options;
|
||||
const timeOpts = options.time;
|
||||
const tickOpts = options.ticks;
|
||||
const timestamps = tickOpts.source === 'labels' ? me.getLabelTimestamps() : me._generate();
|
||||
const timestamps = tickOpts.source === 'labels' ? this.getLabelTimestamps() : this._generate();
|
||||
|
||||
if (options.bounds === 'ticks' && timestamps.length) {
|
||||
me.min = me._userMin || timestamps[0];
|
||||
me.max = me._userMax || timestamps[timestamps.length - 1];
|
||||
this.min = this._userMin || timestamps[0];
|
||||
this.max = this._userMax || timestamps[timestamps.length - 1];
|
||||
}
|
||||
|
||||
const min = me.min;
|
||||
const max = me.max;
|
||||
const min = this.min;
|
||||
const max = this.max;
|
||||
|
||||
const ticks = _filterBetween(timestamps, min, max);
|
||||
|
||||
// PRIVATE
|
||||
// determineUnitForFormatting relies on the number of ticks so we don't use it when
|
||||
// autoSkip is enabled because we don't yet know what the final number of ticks will be
|
||||
me._unit = timeOpts.unit || (tickOpts.autoSkip
|
||||
? determineUnitForAutoTicks(timeOpts.minUnit, me.min, me.max, me._getLabelCapacity(min))
|
||||
: determineUnitForFormatting(me, ticks.length, timeOpts.minUnit, me.min, me.max));
|
||||
me._majorUnit = !tickOpts.major.enabled || me._unit === 'year' ? undefined
|
||||
: determineMajorUnit(me._unit);
|
||||
me.initOffsets(timestamps);
|
||||
this._unit = timeOpts.unit || (tickOpts.autoSkip
|
||||
? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min))
|
||||
: determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max));
|
||||
this._majorUnit = !tickOpts.major.enabled || this._unit === 'year' ? undefined
|
||||
: determineMajorUnit(this._unit);
|
||||
this.initOffsets(timestamps);
|
||||
|
||||
if (options.reverse) {
|
||||
ticks.reverse();
|
||||
}
|
||||
|
||||
return ticksFromTimestamps(me, ticks, me._majorUnit);
|
||||
return ticksFromTimestamps(this, ticks, this._majorUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -363,30 +361,29 @@ export default class TimeScale extends Scale {
|
||||
* @protected
|
||||
*/
|
||||
initOffsets(timestamps) {
|
||||
const me = this;
|
||||
let start = 0;
|
||||
let end = 0;
|
||||
let first, last;
|
||||
|
||||
if (me.options.offset && timestamps.length) {
|
||||
first = me.getDecimalForValue(timestamps[0]);
|
||||
if (this.options.offset && timestamps.length) {
|
||||
first = this.getDecimalForValue(timestamps[0]);
|
||||
if (timestamps.length === 1) {
|
||||
start = 1 - first;
|
||||
} else {
|
||||
start = (me.getDecimalForValue(timestamps[1]) - first) / 2;
|
||||
start = (this.getDecimalForValue(timestamps[1]) - first) / 2;
|
||||
}
|
||||
last = me.getDecimalForValue(timestamps[timestamps.length - 1]);
|
||||
last = this.getDecimalForValue(timestamps[timestamps.length - 1]);
|
||||
if (timestamps.length === 1) {
|
||||
end = last;
|
||||
} else {
|
||||
end = (last - me.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;
|
||||
end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;
|
||||
}
|
||||
}
|
||||
const limit = timestamps.length < 3 ? 0.5 : 0.25;
|
||||
start = _limitValue(start, 0, limit);
|
||||
end = _limitValue(end, 0, limit);
|
||||
|
||||
me._offsets = {start, end, factor: 1 / (start + 1 + end)};
|
||||
this._offsets = {start, end, factor: 1 / (start + 1 + end)};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -397,14 +394,13 @@ export default class TimeScale extends Scale {
|
||||
* @private
|
||||
*/
|
||||
_generate() {
|
||||
const me = this;
|
||||
const adapter = me._adapter;
|
||||
const min = me.min;
|
||||
const max = me.max;
|
||||
const options = me.options;
|
||||
const adapter = this._adapter;
|
||||
const min = this.min;
|
||||
const max = this.max;
|
||||
const options = this.options;
|
||||
const timeOpts = options.time;
|
||||
// @ts-ignore
|
||||
const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, me._getLabelCapacity(min));
|
||||
const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));
|
||||
const stepSize = valueOrDefault(timeOpts.stepSize, 1);
|
||||
const weekday = minor === 'week' ? timeOpts.isoWeekday : false;
|
||||
const hasWeekday = isNumber(weekday) || weekday === true;
|
||||
@ -425,7 +421,7 @@ export default class TimeScale extends Scale {
|
||||
throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);
|
||||
}
|
||||
|
||||
const timestamps = options.ticks.source === 'data' && me.getDataTimestamps();
|
||||
const timestamps = options.ticks.source === 'data' && this.getDataTimestamps();
|
||||
for (time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++) {
|
||||
addTick(ticks, time, timestamps);
|
||||
}
|
||||
@ -443,9 +439,8 @@ export default class TimeScale extends Scale {
|
||||
* @return {string}
|
||||
*/
|
||||
getLabelForValue(value) {
|
||||
const me = this;
|
||||
const adapter = me._adapter;
|
||||
const timeOpts = me.options.time;
|
||||
const adapter = this._adapter;
|
||||
const timeOpts = this.options.time;
|
||||
|
||||
if (timeOpts.tooltipFormat) {
|
||||
return adapter.format(value, timeOpts.tooltipFormat);
|
||||
@ -463,18 +458,17 @@ export default class TimeScale extends Scale {
|
||||
* @private
|
||||
*/
|
||||
_tickFormatFunction(time, index, ticks, format) {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
const options = this.options;
|
||||
const formats = options.time.displayFormats;
|
||||
const unit = me._unit;
|
||||
const majorUnit = me._majorUnit;
|
||||
const unit = this._unit;
|
||||
const majorUnit = this._majorUnit;
|
||||
const minorFormat = unit && formats[unit];
|
||||
const majorFormat = majorUnit && formats[majorUnit];
|
||||
const tick = ticks[index];
|
||||
const major = majorUnit && majorFormat && tick && tick.major;
|
||||
const label = me._adapter.format(time, format || (major ? majorFormat : minorFormat));
|
||||
const label = this._adapter.format(time, format || (major ? majorFormat : minorFormat));
|
||||
const formatter = options.ticks.callback;
|
||||
return formatter ? call(formatter, [label, index, ticks], me) : label;
|
||||
return formatter ? call(formatter, [label, index, ticks], this) : label;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -494,8 +488,7 @@ export default class TimeScale extends Scale {
|
||||
* @return {number}
|
||||
*/
|
||||
getDecimalForValue(value) {
|
||||
const me = this;
|
||||
return value === null ? NaN : (value - me.min) / (me.max - me.min);
|
||||
return value === null ? NaN : (value - this.min) / (this.max - this.min);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -503,10 +496,9 @@ export default class TimeScale extends Scale {
|
||||
* @return {number}
|
||||
*/
|
||||
getPixelForValue(value) {
|
||||
const me = this;
|
||||
const offsets = me._offsets;
|
||||
const pos = me.getDecimalForValue(value);
|
||||
return me.getPixelForDecimal((offsets.start + pos) * offsets.factor);
|
||||
const offsets = this._offsets;
|
||||
const pos = this.getDecimalForValue(value);
|
||||
return this.getPixelForDecimal((offsets.start + pos) * offsets.factor);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -514,10 +506,9 @@ export default class TimeScale extends Scale {
|
||||
* @return {number}
|
||||
*/
|
||||
getValueForPixel(pixel) {
|
||||
const me = this;
|
||||
const offsets = me._offsets;
|
||||
const pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
|
||||
return me.min + pos * (me.max - me.min);
|
||||
const offsets = this._offsets;
|
||||
const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
|
||||
return this.min + pos * (this.max - this.min);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -526,13 +517,12 @@ export default class TimeScale extends Scale {
|
||||
* @private
|
||||
*/
|
||||
_getLabelSize(label) {
|
||||
const me = this;
|
||||
const ticksOpts = me.options.ticks;
|
||||
const tickLabelWidth = me.ctx.measureText(label).width;
|
||||
const angle = toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
|
||||
const ticksOpts = this.options.ticks;
|
||||
const tickLabelWidth = this.ctx.measureText(label).width;
|
||||
const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
|
||||
const cosRotation = Math.cos(angle);
|
||||
const sinRotation = Math.sin(angle);
|
||||
const tickFontSize = me._resolveTickFontOptions(0).size;
|
||||
const tickFontSize = this._resolveTickFontOptions(0).size;
|
||||
|
||||
return {
|
||||
w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation),
|
||||
@ -546,17 +536,16 @@ export default class TimeScale extends Scale {
|
||||
* @private
|
||||
*/
|
||||
_getLabelCapacity(exampleTime) {
|
||||
const me = this;
|
||||
const timeOpts = me.options.time;
|
||||
const timeOpts = this.options.time;
|
||||
const displayFormats = timeOpts.displayFormats;
|
||||
|
||||
// pick the longest format (milliseconds) for guestimation
|
||||
const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
|
||||
const exampleLabel = me._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format);
|
||||
const size = me._getLabelSize(exampleLabel);
|
||||
const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [exampleTime], this._majorUnit), format);
|
||||
const size = this._getLabelSize(exampleLabel);
|
||||
// subtract 1 - if offset then there's one less label than tick
|
||||
// if not offset then one half label padding is added to each end leaving room for one less label
|
||||
const capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h) - 1;
|
||||
const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1;
|
||||
return capacity > 0 ? capacity : 1;
|
||||
}
|
||||
|
||||
@ -564,45 +553,43 @@ export default class TimeScale extends Scale {
|
||||
* @protected
|
||||
*/
|
||||
getDataTimestamps() {
|
||||
const me = this;
|
||||
let timestamps = me._cache.data || [];
|
||||
let timestamps = this._cache.data || [];
|
||||
let i, ilen;
|
||||
|
||||
if (timestamps.length) {
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
const metas = me.getMatchingVisibleMetas();
|
||||
const metas = this.getMatchingVisibleMetas();
|
||||
|
||||
if (me._normalized && metas.length) {
|
||||
return (me._cache.data = metas[0].controller.getAllParsedValues(me));
|
||||
if (this._normalized && metas.length) {
|
||||
return (this._cache.data = metas[0].controller.getAllParsedValues(this));
|
||||
}
|
||||
|
||||
for (i = 0, ilen = metas.length; i < ilen; ++i) {
|
||||
timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(me));
|
||||
timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this));
|
||||
}
|
||||
|
||||
return (me._cache.data = me.normalize(timestamps));
|
||||
return (this._cache.data = this.normalize(timestamps));
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
getLabelTimestamps() {
|
||||
const me = this;
|
||||
const timestamps = me._cache.labels || [];
|
||||
const timestamps = this._cache.labels || [];
|
||||
let i, ilen;
|
||||
|
||||
if (timestamps.length) {
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
const labels = me.getLabels();
|
||||
const labels = this.getLabels();
|
||||
for (i = 0, ilen = labels.length; i < ilen; ++i) {
|
||||
timestamps.push(parse(me, labels[i]));
|
||||
timestamps.push(parse(this, labels[i]));
|
||||
}
|
||||
|
||||
return (me._cache.labels = me._normalized ? timestamps : me.normalize(timestamps));
|
||||
return (this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -51,11 +51,10 @@ class TimeSeriesScale extends TimeScale {
|
||||
* @protected
|
||||
*/
|
||||
initOffsets() {
|
||||
const me = this;
|
||||
const timestamps = me._getTimestampsForTable();
|
||||
const table = me._table = me.buildLookupTable(timestamps);
|
||||
me._minPos = interpolate(table, me.min);
|
||||
me._tableRange = interpolate(table, me.max) - me._minPos;
|
||||
const timestamps = this._getTimestampsForTable();
|
||||
const table = this._table = this.buildLookupTable(timestamps);
|
||||
this._minPos = interpolate(table, this.min);
|
||||
this._tableRange = interpolate(table, this.max) - this._minPos;
|
||||
super.initOffsets(timestamps);
|
||||
}
|
||||
|
||||
@ -110,23 +109,22 @@ class TimeSeriesScale extends TimeScale {
|
||||
* @private
|
||||
*/
|
||||
_getTimestampsForTable() {
|
||||
const me = this;
|
||||
let timestamps = me._cache.all || [];
|
||||
let timestamps = this._cache.all || [];
|
||||
|
||||
if (timestamps.length) {
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
const data = me.getDataTimestamps();
|
||||
const label = me.getLabelTimestamps();
|
||||
const data = this.getDataTimestamps();
|
||||
const label = this.getLabelTimestamps();
|
||||
if (data.length && label.length) {
|
||||
// If combining labels and data (data might not contain all labels),
|
||||
// we need to recheck uniqueness and sort
|
||||
timestamps = me.normalize(data.concat(label));
|
||||
timestamps = this.normalize(data.concat(label));
|
||||
} else {
|
||||
timestamps = data.length ? data : label;
|
||||
}
|
||||
timestamps = me._cache.all = timestamps;
|
||||
timestamps = this._cache.all = timestamps;
|
||||
|
||||
return timestamps;
|
||||
}
|
||||
@ -144,10 +142,9 @@ class TimeSeriesScale extends TimeScale {
|
||||
* @return {number}
|
||||
*/
|
||||
getValueForPixel(pixel) {
|
||||
const me = this;
|
||||
const offsets = me._offsets;
|
||||
const decimal = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
|
||||
return interpolate(me._table, decimal * me._tableRange + me._minPos, true);
|
||||
const offsets = this._offsets;
|
||||
const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
|
||||
return interpolate(this._table, decimal * this._tableRange + this._minPos, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user