tupali/librerias/gantt/code/es-modules/modules/boost/boost-overrides.js
2020-05-23 15:45:54 -05:00

366 lines
13 KiB
JavaScript

/* *
*
* Copyright (c) 2019-2020 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import H from '../../parts/Globals.js';
import Point from '../../parts/Point.js';
import U from '../../parts/Utilities.js';
var addEvent = U.addEvent, error = U.error, isArray = U.isArray, isNumber = U.isNumber, pick = U.pick, wrap = U.wrap;
import '../../parts/Series.js';
import '../../parts/Options.js';
import '../../parts/Interaction.js';
import butils from './boost-utils.js';
import boostable from './boostables.js';
import boostableMap from './boostable-map.js';
var boostEnabled = butils.boostEnabled, shouldForceChartSeriesBoosting = butils.shouldForceChartSeriesBoosting, Chart = H.Chart, Series = H.Series, seriesTypes = H.seriesTypes, plotOptions = H.getOptions().plotOptions;
/**
* Returns true if the chart is in series boost mode.
*
* @function Highcharts.Chart#isChartSeriesBoosting
*
* @param {Highcharts.Chart} chart
* the chart to check
*
* @return {boolean}
* true if the chart is in series boost mode
*/
Chart.prototype.isChartSeriesBoosting = function () {
var isSeriesBoosting, threshold = pick(this.options.boost && this.options.boost.seriesThreshold, 50);
isSeriesBoosting = threshold <= this.series.length ||
shouldForceChartSeriesBoosting(this);
return isSeriesBoosting;
};
/* eslint-disable valid-jsdoc */
/**
* Get the clip rectangle for a target, either a series or the chart. For the
* chart, we need to consider the maximum extent of its Y axes, in case of
* Highstock panes and navigator.
*
* @private
* @function Highcharts.Chart#getBoostClipRect
*
* @param {Highcharts.Chart} target
*
* @return {Highcharts.BBoxObject}
*/
Chart.prototype.getBoostClipRect = function (target) {
var clipBox = {
x: this.plotLeft,
y: this.plotTop,
width: this.plotWidth,
height: this.plotHeight
};
if (target === this) {
this.yAxis.forEach(function (yAxis) {
clipBox.y = Math.min(yAxis.pos, clipBox.y);
clipBox.height = Math.max(yAxis.pos - this.plotTop + yAxis.len, clipBox.height);
}, this);
}
return clipBox;
};
/**
* Return a full Point object based on the index.
* The boost module uses stripped point objects for performance reasons.
*
* @function Highcharts.Series#getPoint
*
* @param {object|Highcharts.Point} boostPoint
* A stripped-down point object
*
* @return {Highcharts.Point}
* A Point object as per https://api.highcharts.com/highcharts#Point
*/
Series.prototype.getPoint = function (boostPoint) {
var point = boostPoint, xData = (this.xData || this.options.xData || this.processedXData ||
false);
if (boostPoint && !(boostPoint instanceof this.pointClass)) {
point = (new this.pointClass()).init(// eslint-disable-line new-cap
this, this.options.data[boostPoint.i], xData ? xData[boostPoint.i] : void 0);
point.category = pick(this.xAxis.categories ?
this.xAxis.categories[point.x] :
point.x, // @todo simplify
point.x);
point.dist = boostPoint.dist;
point.distX = boostPoint.distX;
point.plotX = boostPoint.plotX;
point.plotY = boostPoint.plotY;
point.index = boostPoint.i;
point.isInside = this.isPointInside(boostPoint);
}
return point;
};
/* eslint-disable no-invalid-this */
// Return a point instance from the k-d-tree
wrap(Series.prototype, 'searchPoint', function (proceed) {
return this.getPoint(proceed.apply(this, [].slice.call(arguments, 1)));
});
// For inverted series, we need to swap X-Y values before running base methods
wrap(Point.prototype, 'haloPath', function (proceed) {
var halo, point = this, series = point.series, chart = series.chart, plotX = point.plotX, plotY = point.plotY, inverted = chart.inverted;
if (series.isSeriesBoosting && inverted) {
point.plotX = series.yAxis.len - plotY;
point.plotY = series.xAxis.len - plotX;
}
halo = proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (series.isSeriesBoosting && inverted) {
point.plotX = plotX;
point.plotY = plotY;
}
return halo;
});
wrap(Series.prototype, 'markerAttribs', function (proceed, point) {
var attribs, series = this, chart = series.chart, plotX = point.plotX, plotY = point.plotY, inverted = chart.inverted;
if (series.isSeriesBoosting && inverted) {
point.plotX = series.yAxis.len - plotY;
point.plotY = series.xAxis.len - plotX;
}
attribs = proceed.apply(this, Array.prototype.slice.call(arguments, 1));
if (series.isSeriesBoosting && inverted) {
point.plotX = plotX;
point.plotY = plotY;
}
return attribs;
});
/*
* Extend series.destroy to also remove the fake k-d-tree points (#5137).
* Normally this is handled by Series.destroy that calls Point.destroy,
* but the fake search points are not registered like that.
*/
addEvent(Series, 'destroy', function () {
var series = this, chart = series.chart;
if (chart.markerGroup === series.markerGroup) {
series.markerGroup = null;
}
if (chart.hoverPoints) {
chart.hoverPoints = chart.hoverPoints.filter(function (point) {
return point.series === series;
});
}
if (chart.hoverPoint && chart.hoverPoint.series === series) {
chart.hoverPoint = null;
}
});
/*
* Do not compute extremes when min and max are set.
* If we use this in the core, we can add the hook
* to hasExtremes to the methods directly.
*/
wrap(Series.prototype, 'getExtremes', function (proceed) {
if (!this.isSeriesBoosting || (!this.hasExtremes || !this.hasExtremes())) {
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
}
return {};
});
/*
* Override a bunch of methods the same way. If the number of points is
* below the threshold, run the original method. If not, check for a
* canvas version or do nothing.
*
* Note that we're not overriding any of these for heatmaps.
*/
[
'translate',
'generatePoints',
'drawTracker',
'drawPoints',
'render'
].forEach(function (method) {
/**
* @private
*/
function branch(proceed) {
var letItPass = this.options.stacking &&
(method === 'translate' || method === 'generatePoints');
if (!this.isSeriesBoosting ||
letItPass ||
!boostEnabled(this.chart) ||
this.type === 'heatmap' ||
this.type === 'treemap' ||
!boostableMap[this.type] ||
this.options.boostThreshold === 0) {
proceed.call(this);
// If a canvas version of the method exists, like renderCanvas(), run
}
else if (this[method + 'Canvas']) {
this[method + 'Canvas']();
}
}
wrap(Series.prototype, method, branch);
// A special case for some types - their translate method is already wrapped
if (method === 'translate') {
[
'column',
'bar',
'arearange',
'columnrange',
'heatmap',
'treemap'
].forEach(function (type) {
if (seriesTypes[type]) {
wrap(seriesTypes[type].prototype, method, branch);
}
});
}
});
// If the series is a heatmap or treemap, or if the series is not boosting
// do the default behaviour. Otherwise, process if the series has no extremes.
wrap(Series.prototype, 'processData', function (proceed) {
var series = this, dataToMeasure = this.options.data, firstPoint;
/**
* Used twice in this function, first on this.options.data, the second
* time it runs the check again after processedXData is built.
* @private
* @todo Check what happens with data grouping
*/
function getSeriesBoosting(data) {
return series.chart.isChartSeriesBoosting() || ((data ? data.length : 0) >=
(series.options.boostThreshold || Number.MAX_VALUE));
}
if (boostEnabled(this.chart) && boostableMap[this.type]) {
// If there are no extremes given in the options, we also need to
// process the data to read the data extremes. If this is a heatmap, do
// default behaviour.
if (!getSeriesBoosting(dataToMeasure) || // First pass with options.data
this.type === 'heatmap' ||
this.type === 'treemap' ||
this.options.stacking || // processedYData for the stack (#7481)
!this.hasExtremes ||
!this.hasExtremes(true)) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
dataToMeasure = this.processedXData;
}
// Set the isBoosting flag, second pass with processedXData to see if we
// have zoomed.
this.isSeriesBoosting = getSeriesBoosting(dataToMeasure);
// Enter or exit boost mode
if (this.isSeriesBoosting) {
// Force turbo-mode:
firstPoint = this.getFirstValidPoint(this.options.data);
if (!isNumber(firstPoint) && !isArray(firstPoint)) {
error(12, false, this.chart);
}
this.enterBoost();
}
else if (this.exitBoost) {
this.exitBoost();
}
// The series type is not boostable
}
else {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
}
});
addEvent(Series, 'hide', function () {
if (this.canvas && this.renderTarget) {
if (this.ogl) {
this.ogl.clear();
}
this.boostClear();
}
});
/**
* Enter boost mode and apply boost-specific properties.
*
* @function Highcharts.Series#enterBoost
*/
Series.prototype.enterBoost = function () {
this.alteredByBoost = [];
// Save the original values, including whether it was an own property or
// inherited from the prototype.
['allowDG', 'directTouch', 'stickyTracking'].forEach(function (prop) {
this.alteredByBoost.push({
prop: prop,
val: this[prop],
own: Object.hasOwnProperty.call(this, prop)
});
}, this);
this.allowDG = false;
this.directTouch = false;
this.stickyTracking = true;
// Hide series label if any
if (this.labelBySeries) {
this.labelBySeries = this.labelBySeries.destroy();
}
};
/**
* Exit from boost mode and restore non-boost properties.
*
* @function Highcharts.Series#exitBoost
*/
Series.prototype.exitBoost = function () {
// Reset instance properties and/or delete instance properties and go back
// to prototype
(this.alteredByBoost || []).forEach(function (setting) {
if (setting.own) {
this[setting.prop] = setting.val;
}
else {
// Revert to prototype
delete this[setting.prop];
}
}, this);
// Clear previous run
if (this.boostClear) {
this.boostClear();
}
};
/**
* @private
* @function Highcharts.Series#hasExtremes
*
* @param {boolean} checkX
*
* @return {boolean}
*/
Series.prototype.hasExtremes = function (checkX) {
var options = this.options, data = options.data, xAxis = this.xAxis && this.xAxis.options, yAxis = this.yAxis && this.yAxis.options, colorAxis = this.colorAxis && this.colorAxis.options;
return data.length > (options.boostThreshold || Number.MAX_VALUE) &&
// Defined yAxis extremes
isNumber(yAxis.min) &&
isNumber(yAxis.max) &&
// Defined (and required) xAxis extremes
(!checkX ||
(isNumber(xAxis.min) && isNumber(xAxis.max))) &&
// Defined (e.g. heatmap) colorAxis extremes
(!colorAxis ||
(isNumber(colorAxis.min) && isNumber(colorAxis.max)));
};
/**
* If implemented in the core, parts of this can probably be
* shared with other similar methods in Highcharts.
*
* @function Highcharts.Series#destroyGraphics
*/
Series.prototype.destroyGraphics = function () {
var series = this, points = this.points, point, i;
if (points) {
for (i = 0; i < points.length; i = i + 1) {
point = points[i];
if (point && point.destroyElements) {
point.destroyElements(); // #7557
}
}
}
['graph', 'area', 'tracker'].forEach(function (prop) {
if (series[prop]) {
series[prop] = series[prop].destroy();
}
});
};
// Set default options
boostable.forEach(function (type) {
if (plotOptions[type]) {
plotOptions[type].boostThreshold = 5000;
plotOptions[type].boostData = [];
seriesTypes[type].prototype.fillOpacity = true;
}
});