Remove const me = this pattern (#9646)

This commit is contained in:
Evert Timberg 2021-09-14 07:37:22 -04:00 committed by GitHub
parent 183a1a9f67
commit 8e68481ec4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1166 additions and 1382 deletions

View File

@ -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);
}
}
}

View File

@ -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);
}
/**

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;
}
}
}

View File

@ -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() {

View File

@ -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);
}
}
}

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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));
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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));
}
/**

View File

@ -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);
}
}