devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,192 lines (1,180 loc) • 46.1 kB
JavaScript
/**
* DevExtreme (esm/viz/series/base_series.js)
* Version: 24.2.6
* Build date: Mon Mar 17 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
const seriesNS = {};
import {
isFunction,
isDefined as _isDefined,
isEmptyObject as _isEmptyObject
} from "../../core/utils/type";
import {
extend as _extend
} from "../../core/utils/extend";
import {
each as _each
} from "../../core/utils/iterator";
import {
Point
} from "./points/base_point";
import {
normalizeEnum as _normalizeEnum
} from "../core/utils";
import {
noop as _noop
} from "../../core/utils/common";
import consts from "../components/consts";
const states = consts.states;
import rangeCalculator from "./helpers/range_data_calculator";
import * as scatterSeries from "./scatter_series";
import * as lineSeries from "./line_series";
import * as areaSeries from "./area_series";
import * as barSeries from "./bar_series";
import {
chart as rangeSeriesChart
} from "./range_series";
import {
chart as bubbleSeriesChart
} from "./bubble_series";
import * as pieSeries from "./pie_series";
import * as financialSeries from "./financial_series";
import * as stackedSeries from "./stacked_series";
const DISCRETE = "discrete";
const SELECTED_STATE = states.selectedMark;
const HOVER_STATE = states.hoverMark;
const HOVER = states.hover;
const NORMAL = states.normal;
const SELECTION = states.selection;
const APPLY_SELECTED = states.applySelected;
const APPLY_HOVER = states.applyHover;
const RESET_ITEM = states.resetItem;
const NONE_MODE = "none";
const INCLUDE_POINTS = "includepoints";
const NEAREST_POINT = "nearestpoint";
const SERIES_SELECTION_CHANGED = "seriesSelectionChanged";
const POINT_SELECTION_CHANGED = "pointSelectionChanged";
const SERIES_HOVER_CHANGED = "seriesHoverChanged";
const POINT_HOVER_CHANGED = "pointHoverChanged";
const ALL_SERIES_POINTS = "allseriespoints";
const ALL_ARGUMENT_POINTS = "allargumentpoints";
const POINT_HOVER = "pointHover";
const CLEAR_POINT_HOVER = "clearPointHover";
const SERIES_SELECT = "seriesSelect";
const POINT_SELECT = "pointSelect";
const POINT_DESELECT = "pointDeselect";
const getEmptyBusinessRange = function() {
return {
arg: {},
val: {}
}
};
function triggerEvent(element, event, point) {
element && element.trigger(event, point)
}
seriesNS.mixins = {
chart: {},
pie: {},
polar: {}
};
seriesNS.mixins.chart.scatter = scatterSeries.chart;
seriesNS.mixins.polar.scatter = scatterSeries.polar;
_extend(seriesNS.mixins.pie, pieSeries);
_extend(seriesNS.mixins.chart, lineSeries.chart, areaSeries.chart, barSeries.chart, rangeSeriesChart, bubbleSeriesChart, financialSeries, stackedSeries.chart);
_extend(seriesNS.mixins.polar, lineSeries.polar, areaSeries.polar, barSeries.polar, stackedSeries.polar);
function includePointsMode(mode) {
mode = _normalizeEnum(mode);
return mode === INCLUDE_POINTS || "allseriespoints" === mode
}
function getLabelOptions(labelOptions, defaultColor) {
const opt = labelOptions || {};
const labelFont = _extend({}, opt.font) || {};
const labelBorder = opt.border || {};
const labelConnector = opt.connector || {};
const backgroundAttr = {
fill: opt.backgroundColor || defaultColor,
"stroke-width": labelBorder.visible ? labelBorder.width || 0 : 0,
stroke: labelBorder.visible && labelBorder.width ? labelBorder.color : "none",
dashStyle: labelBorder.dashStyle
};
const connectorAttr = {
stroke: labelConnector.visible && labelConnector.width ? labelConnector.color || defaultColor : "none",
"stroke-width": labelConnector.visible ? labelConnector.width || 0 : 0
};
labelFont.color = "none" === opt.backgroundColor && "#ffffff" === _normalizeEnum(labelFont.color) && "inside" !== opt.position ? defaultColor : labelFont.color;
return {
alignment: opt.alignment,
format: opt.format,
argumentFormat: opt.argumentFormat,
customizeText: isFunction(opt.customizeText) ? opt.customizeText : void 0,
attributes: {
font: labelFont
},
visible: 0 !== labelFont.size ? opt.visible : false,
showForZeroValues: opt.showForZeroValues,
horizontalOffset: opt.horizontalOffset,
verticalOffset: opt.verticalOffset,
radialOffset: opt.radialOffset,
background: backgroundAttr,
position: opt.position,
connector: connectorAttr,
rotationAngle: opt.rotationAngle,
wordWrap: opt.wordWrap,
textOverflow: opt.textOverflow,
cssClass: opt.cssClass,
displayFormat: opt.displayFormat
}
}
function setPointHoverState(point, legendCallback) {
point.fullState |= HOVER_STATE;
point.applyView(legendCallback)
}
function releasePointHoverState(point, legendCallback) {
point.fullState &= ~HOVER_STATE;
point.applyView(legendCallback);
point.releaseHoverState()
}
function setPointSelectedState(point, legendCallback) {
point.fullState |= SELECTED_STATE;
point.applyView(legendCallback)
}
function releasePointSelectedState(point, legendCallback) {
point.fullState &= ~SELECTED_STATE;
point.applyView(legendCallback)
}
function mergePointOptionsCore(base, extra) {
const options = _extend({}, base, extra);
options.border = _extend({}, base && base.border, extra && extra.border);
return options
}
function mergePointOptions(base, extra) {
const options = mergePointOptionsCore(base, extra);
options.image = _extend(true, {}, base.image, extra.image);
options.selectionStyle = mergePointOptionsCore(base.selectionStyle, extra.selectionStyle);
options.hoverStyle = mergePointOptionsCore(base.hoverStyle, extra.hoverStyle);
return options
}
export function Series(settings, options) {
this.fullState = 0;
this._extGroups = settings;
this._renderer = settings.renderer;
this._group = settings.renderer.g().attr({
class: "dxc-series"
});
this._eventTrigger = settings.eventTrigger;
this._eventPipe = settings.eventPipe;
this._incidentOccurred = settings.incidentOccurred;
this._legendCallback = _noop;
this.updateOptions(options, settings)
}
function getData(pointData) {
return pointData.data
}
function getValueChecker(axisType, axis) {
if (!axis || "logarithmic" !== axisType || false !== axis.getOptions().allowNegatives) {
return () => true
} else {
return value => value > 0
}
}
Series.prototype = {
constructor: Series,
_createLegendState: _noop,
getLegendStyles: function() {
return this._styles.legendStyles
},
_createStyles: function(options) {
const that = this;
const mainSeriesColor = options.mainSeriesColor;
const colorId = this._getColorId(options);
const hoverStyle = options.hoverStyle || {};
const selectionStyle = options.selectionStyle || {};
if (colorId) {
that._turnOffHatching(hoverStyle, selectionStyle)
}
that._styles = {
labelColor: mainSeriesColor,
normal: that._parseStyle(options, mainSeriesColor, mainSeriesColor),
hover: that._parseStyle(hoverStyle, colorId || mainSeriesColor, mainSeriesColor),
selection: that._parseStyle(selectionStyle, colorId || mainSeriesColor, mainSeriesColor),
legendStyles: {
normal: that._createLegendState(options, colorId || mainSeriesColor),
hover: that._createLegendState(hoverStyle, colorId || mainSeriesColor),
selection: that._createLegendState(selectionStyle, colorId || mainSeriesColor)
}
}
},
setClippingParams(baseId, wideId, forceClipping) {
let clipLabels = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : true;
this._paneClipRectID = baseId;
this._widePaneClipRectID = wideId;
this._forceClipping = forceClipping;
this._clipLabels = clipLabels
},
applyClip: function() {
this._group.attr({
"clip-path": this._paneClipRectID
})
},
resetClip: function() {
this._group.attr({
"clip-path": null
})
},
getTagField: function() {
return this._options.tagField || "tag"
},
getValueFields: _noop,
getSizeField: _noop,
getArgumentField: _noop,
getPoints: function() {
return this._points
},
getPointsInViewPort: function() {
return rangeCalculator.getPointsInViewPort(this)
},
_createPoint: function(data, index, oldPoint) {
data.index = index;
const that = this;
const pointsByArgument = that.pointsByArgument;
const options = that._getCreatingPointOptions(data);
const arg = data.argument.valueOf();
let point = oldPoint;
if (point) {
point.update(data, options)
} else {
point = new Point(that, data, options);
if (that.isSelected() && includePointsMode(that.lastSelectionMode)) {
point.setView(SELECTION)
}
}
const pointByArgument = pointsByArgument[arg];
if (pointByArgument) {
pointByArgument.push(point)
} else {
pointsByArgument[arg] = [point]
}
if (point.hasValue()) {
that.customizePoint(point, data)
}
return point
},
getRangeData: function() {
return this._visible ? this._getRangeData() : getEmptyBusinessRange()
},
getArgumentRange: function() {
return this._visible ? rangeCalculator.getArgumentRange(this) : getEmptyBusinessRange()
},
getViewport: function() {
return rangeCalculator.getViewport(this)
},
_deleteGroup: function(groupName) {
const group = this[groupName];
if (group) {
group.dispose();
this[groupName] = null
}
},
updateOptions(newOptions, settings) {
const that = this;
const widgetType = newOptions.widgetType;
const oldType = that.type;
const newType = newOptions.type;
that.type = newType && _normalizeEnum(newType.toString());
if (!that._checkType(widgetType) || that._checkPolarBarType(widgetType, newOptions)) {
that.dispose();
that.isUpdated = false;
return
}
if (oldType !== that.type) {
that._firstDrawing = true;
that._resetType(oldType, widgetType);
that._setType(that.type, widgetType)
} else {
that._defineDrawingState()
}
that._options = newOptions;
that._pointOptions = null;
that.name = newOptions.name;
that.pane = newOptions.pane;
that.tag = newOptions.tag;
if (settings) {
that._seriesModes = settings.commonSeriesModes || that._seriesModes;
that._valueAxis = settings.valueAxis || that._valueAxis;
that.axis = that._valueAxis && that._valueAxis.name;
that._argumentAxis = settings.argumentAxis || that._argumentAxis
}
that._createStyles(newOptions);
that._stackName = null;
that._updateOptions(newOptions);
that._visible = newOptions.visible;
that.isUpdated = true;
that.stack = newOptions.stack;
that.barOverlapGroup = newOptions.barOverlapGroup;
that._createGroups();
that._processEmptyValue = newOptions.ignoreEmptyPoints ? x => null === x ? void 0 : x : x => x
},
_defineDrawingState() {
this._firstDrawing = true
},
_disposePoints: function(points) {
_each(points || [], (function(_, p) {
p.dispose()
}))
},
updateDataType: function(settings) {
this.argumentType = settings.argumentType;
this.valueType = settings.valueType;
this.argumentAxisType = settings.argumentAxisType;
this.valueAxisType = settings.valueAxisType;
this.showZero = settings.showZero;
this._argumentChecker = getValueChecker(settings.argumentAxisType, this.getArgumentAxis());
this._valueChecker = getValueChecker(settings.valueAxisType, this.getValueAxis());
return this
},
_argumentChecker: function() {
return true
},
_valueChecker: function() {
return true
},
getOptions: function() {
return this._options
},
_getOldPoint: function(data, oldPointsByArgument, index) {
const arg = data.argument && data.argument.valueOf();
const point = (oldPointsByArgument[arg] || [])[0];
if (point) {
oldPointsByArgument[arg].splice(0, 1)
}
return point
},
updateData: function(data) {
const that = this;
const options = that._options;
const nameField = options.nameField;
data = data || [];
if (data.length) {
that._canRenderCompleteHandle = true
}
const dataSelector = this._getPointDataSelector();
let itemsWithoutArgument = 0;
that._data = data.reduce(((data, dataItem, index) => {
const pointDataItem = dataSelector(dataItem);
if (_isDefined(pointDataItem.argument)) {
if (!nameField || dataItem[nameField] === options.nameFieldValue) {
pointDataItem.index = index;
data.push(pointDataItem)
}
} else {
itemsWithoutArgument++
}
return data
}), []);
if (itemsWithoutArgument && itemsWithoutArgument === data.length) {
that._incidentOccurred("W2002", [that.name, that.getArgumentField()])
}
that._endUpdateData()
},
_getData() {
let data = this._data || [];
if (this.useAggregation()) {
const argumentRange = this.argumentAxisType !== DISCRETE ? this.getArgumentRange() : {};
const aggregationInfo = this.getArgumentAxis().getAggregationInfo(this._useAllAggregatedPoints, argumentRange);
data = this._resample(aggregationInfo, data)
}
return data
},
useAggregation: function() {
const aggregation = this.getOptions().aggregation;
return aggregation && aggregation.enabled
},
autoHidePointMarkersEnabled: _noop,
usePointsToDefineAutoHiding: _noop,
createPoints(useAllAggregatedPoints) {
this._normalizeUsingAllAggregatedPoints(useAllAggregatedPoints);
this._createPoints()
},
_normalizeUsingAllAggregatedPoints: function(useAllAggregatedPoints) {
this._useAllAggregatedPoints = this.useAggregation() && (this.argumentAxisType === DISCRETE || (this._data || []).length > 1 && !!useAllAggregatedPoints)
},
_createPoints: function() {
const that = this;
const oldPointsByArgument = that.pointsByArgument || {};
const data = that._getData();
that.pointsByArgument = {};
that._calculateErrorBars(data);
const skippedFields = {};
const points = data.reduce(((points, pointDataItem) => {
if (that._checkData(pointDataItem, skippedFields)) {
const pointIndex = points.length;
const oldPoint = that._getOldPoint(pointDataItem, oldPointsByArgument, pointIndex);
const point = that._createPoint(pointDataItem, pointIndex, oldPoint);
points.push(point)
}
return points
}), []);
for (const field in skippedFields) {
if (skippedFields[field] === data.length) {
that._incidentOccurred("W2002", [that.name, field])
}
}
Object.keys(oldPointsByArgument).forEach((key => that._disposePoints(oldPointsByArgument[key])));
that._points = points
},
_removeOldSegments: function() {
const that = this;
const startIndex = that._segments.length;
_each(that._graphics.splice(startIndex, that._graphics.length) || [], (function(_, elem) {
that._removeElement(elem)
}));
if (that._trackers) {
_each(that._trackers.splice(startIndex, that._trackers.length) || [], (function(_, elem) {
elem.remove()
}))
}
},
_prepareSegmentsPosition() {
const points = this._points || [];
const isCloseSegment = points[0] && points[0].hasValue() && this._options.closed;
const segments = points.reduce((function(segments, p) {
const segment = segments.at(-1);
if (!p.translated) {
p.setDefaultCoords()
}
if (p.hasValue() && p.hasCoords()) {
segment.push(p)
} else if (!p.hasValue() && segment.length) {
segments.push([])
}
return segments
}), [
[]
]);
this._drawSegments(segments, isCloseSegment, false)
},
_drawElements(animationEnabled, firstDrawing) {
const that = this;
const points = that._points || [];
const isCloseSegment = points[0] && points[0].hasValue() && that._options.closed;
const groupForPoint = {
markers: that._markersGroup,
errorBars: that._errorBarGroup
};
that._drawnPoints = [];
that._graphics = that._graphics || [];
that._segments = [];
const segments = points.reduce((function(segments, p) {
const segment = segments.at(-1);
if (p.hasValue() && p.hasCoords()) {
that._drawPoint({
point: p,
groups: groupForPoint,
hasAnimation: animationEnabled,
firstDrawing: firstDrawing
});
segment.push(p)
} else if (!p.hasValue()) {
segment.length && segments.push([])
} else {
p.setInvisibility()
}
return segments
}), [
[]
]);
that._drawSegments(segments, isCloseSegment, animationEnabled);
that._firstDrawing = !points.length;
that._removeOldSegments();
animationEnabled && that._animate(firstDrawing)
},
_drawSegments(segments, closeSegment, animationEnabled) {
segments.forEach(((segment, index) => {
if (segment.length) {
const lastSegment = closeSegment && index === segments.length - 1;
this._drawSegment(segment, animationEnabled, index, lastSegment)
}
}))
},
draw(animationEnabled, hideLayoutLabels, legendCallback) {
const that = this;
const firstDrawing = that._firstDrawing;
that._legendCallback = legendCallback || that._legendCallback;
if (!that._visible) {
that._group.remove();
return
}
that._appendInGroup();
if (!that._isAllPointsTranslated) {
that.prepareCoordinatesForPoints()
}
that._setGroupsSettings(animationEnabled, firstDrawing);
!firstDrawing && !that._resetApplyingAnimation && that._prepareSegmentsPosition();
that._drawElements(animationEnabled, firstDrawing);
hideLayoutLabels && that.hideLabels();
if (that.isSelected()) {
that._changeStyle(that.lastSelectionMode, void 0, true)
} else if (that.isHovered()) {
that._changeStyle(that.lastHoverMode, void 0, true)
} else {
that._applyStyle(that._styles.normal)
}
that._isAllPointsTranslated = false;
that._resetApplyingAnimation = false
},
_translatePoints() {
const points = this._points ?? [];
points.forEach((p => {
p.translate()
}))
},
prepareCoordinatesForPoints() {
this._applyVisibleArea();
this._translatePoints();
this._isAllPointsTranslated = true
},
_setLabelGroupSettings: function(animationEnabled) {
const settings = {
class: "dxc-labels",
"pointer-events": "none"
};
this._clipLabels && this._applyElementsClipRect(settings);
this._applyClearingSettings(settings);
animationEnabled && (settings.opacity = .001);
this._labelsGroup.attr(settings).append(this._extGroups.labelsGroup)
},
_checkType: function(widgetType) {
return !!seriesNS.mixins[widgetType][this.type]
},
_checkPolarBarType: function(widgetType, options) {
return "polar" === widgetType && options.spiderWidget && -1 !== this.type.indexOf("bar")
},
_resetType: function(seriesType, widgetType) {
let methodName;
let methods;
if (seriesType) {
methods = seriesNS.mixins[widgetType][seriesType];
for (methodName in methods) {
delete this[methodName]
}
}
},
_setType: function(seriesType, widgetType) {
let methodName;
const methods = seriesNS.mixins[widgetType][seriesType];
for (methodName in methods) {
this[methodName] = methods[methodName]
}
},
_setPointsView: function(view, target) {
this.getPoints().forEach((function(point) {
if (target !== point) {
point.setView(view)
}
}))
},
_resetPointsView: function(view, target) {
this.getPoints().forEach((function(point) {
if (target !== point) {
point.resetView(view)
}
}))
},
_resetNearestPoint: function() {
this._nearestPoint && null !== this._nearestPoint.series && this._nearestPoint.resetView(HOVER);
this._nearestPoint = null
},
_setSelectedState: function(mode) {
const that = this;
that.lastSelectionMode = _normalizeEnum(mode || that._options.selectionMode);
that.fullState = that.fullState | SELECTED_STATE;
that._resetNearestPoint();
that._changeStyle(that.lastSelectionMode);
if ("none" !== that.lastSelectionMode && that.isHovered() && includePointsMode(that.lastHoverMode)) {
that._resetPointsView(HOVER)
}
},
_releaseSelectedState: function() {
const that = this;
that.fullState = that.fullState & ~SELECTED_STATE;
that._changeStyle(that.lastSelectionMode, SELECTION);
if ("none" !== that.lastSelectionMode && that.isHovered() && includePointsMode(that.lastHoverMode)) {
that._setPointsView(HOVER)
}
},
isFullStackedSeries: function() {
return 0 === this.type.indexOf("fullstacked")
},
isStackedSeries: function() {
return 0 === this.type.indexOf("stacked")
},
resetApplyingAnimation: function(isFirstDrawing) {
this._resetApplyingAnimation = true;
if (isFirstDrawing) {
this._firstDrawing = true
}
},
isFinancialSeries: function() {
return "stock" === this.type || "candlestick" === this.type
},
_canChangeView: function() {
return !this.isSelected() && "none" !== _normalizeEnum(this._options.hoverMode)
},
_changeStyle: function(mode, resetView, skipPoints) {
const that = this;
let state = that.fullState;
const styles = [NORMAL, HOVER, SELECTION, SELECTION];
if ("none" === that.lastHoverMode) {
state &= ~HOVER_STATE
}
if ("none" === that.lastSelectionMode) {
state &= ~SELECTED_STATE
}
if (includePointsMode(mode) && !skipPoints) {
if (!resetView) {
that._setPointsView(styles[state])
} else {
that._resetPointsView(resetView)
}
}
that._legendCallback([RESET_ITEM, APPLY_HOVER, APPLY_SELECTED, APPLY_SELECTED][state]);
that._applyStyle(that._styles[styles[state]])
},
updateHover: function(x, y) {
const that = this;
const currentNearestPoint = that._nearestPoint;
const point = that.isHovered() && that.lastHoverMode === NEAREST_POINT && that.getNeighborPoint(x, y);
if (point !== currentNearestPoint && !(that.isSelected() && "none" !== that.lastSelectionMode)) {
that._resetNearestPoint();
if (point) {
point.setView(HOVER);
that._nearestPoint = point
}
}
},
_getMainAxisName: function() {
return this._options.rotated ? "X" : "Y"
},
areLabelsVisible: function() {
return !_isDefined(this._options.maxLabelCount) || this._points.length <= this._options.maxLabelCount
},
getLabelVisibility: function() {
return this.areLabelsVisible() && this._options.label && this._options.label.visible
},
customizePoint: function(point, pointData) {
const that = this;
const options = that._options;
const customizePoint = options.customizePoint;
let customizeObject;
let pointOptions;
let customLabelOptions;
let customOptions;
const customizeLabel = options.customizeLabel;
let useLabelCustomOptions;
let usePointCustomOptions;
if (customizeLabel && customizeLabel.call) {
customizeObject = _extend({
seriesName: that.name
}, pointData);
customizeObject.series = that;
customLabelOptions = customizeLabel.call(customizeObject, customizeObject);
useLabelCustomOptions = customLabelOptions && !_isEmptyObject(customLabelOptions);
customLabelOptions = useLabelCustomOptions ? _extend(true, {}, options.label, customLabelOptions) : null
}
if (customizePoint && customizePoint.call) {
customizeObject = customizeObject || _extend({
seriesName: that.name
}, pointData);
customizeObject.series = that;
customOptions = customizePoint.call(customizeObject, customizeObject);
usePointCustomOptions = customOptions && !_isEmptyObject(customOptions)
}
if (useLabelCustomOptions || usePointCustomOptions) {
pointOptions = that._parsePointOptions(that._preparePointOptions(customOptions), customLabelOptions || options.label, pointData, point);
pointOptions.styles.useLabelCustomOptions = useLabelCustomOptions;
pointOptions.styles.usePointCustomOptions = usePointCustomOptions;
point.updateOptions(pointOptions)
}
},
show: function() {
if (!this._visible) {
this._changeVisibility(true)
}
},
hide: function() {
if (this._visible) {
this._changeVisibility(false)
}
},
_changeVisibility: function(visibility) {
this._visible = this._options.visible = visibility;
this._updatePointsVisibility();
this.hidePointTooltip();
this._options.visibilityChanged(this)
},
_updatePointsVisibility: _noop,
hideLabels: function() {
_each(this._points, (function(_, point) {
point._label.draw(false)
}))
},
_turnOffHatching(hoverStyle, selectionStyle) {
if (hoverStyle.hatching) {
hoverStyle.hatching.direction = "none"
}
if (selectionStyle.hatching) {
selectionStyle.hatching.direction = "none"
}
},
_parsePointOptions: function(pointOptions, labelOptions, data, point) {
const options = this._options;
const styles = this._createPointStyles(pointOptions, data, point);
const parsedOptions = _extend({}, pointOptions, {
type: options.type,
rotated: options.rotated,
styles: styles,
widgetType: options.widgetType,
visibilityChanged: options.visibilityChanged
});
parsedOptions.label = getLabelOptions(labelOptions, styles.labelColor);
if (this.areErrorBarsVisible()) {
parsedOptions.errorBars = options.valueErrorBar
}
return parsedOptions
},
_preparePointOptions: function(customOptions) {
const pointOptions = this._getOptionsForPoint();
return customOptions ? mergePointOptions(pointOptions, customOptions) : pointOptions
},
_getMarkerGroupOptions: function() {
return _extend(false, {}, this._getOptionsForPoint(), {
hoverStyle: {},
selectionStyle: {}
})
},
_getAggregationMethod: function(isDiscrete, aggregateByCategory) {
const options = this.getOptions().aggregation;
const method = _normalizeEnum(options.method);
const customAggregator = "custom" === method && options.calculate;
let aggregator;
if (isDiscrete && !aggregateByCategory) {
aggregator = _ref => {
let {
data: data
} = _ref;
return data[0]
}
} else {
aggregator = this._aggregators[method] || this._aggregators[this._defaultAggregator]
}
return customAggregator || aggregator
},
_resample(_ref2, data) {
let {
interval: interval,
ticks: ticks,
aggregateByCategory: aggregateByCategory
} = _ref2;
const that = this;
const isDiscrete = that.argumentAxisType === DISCRETE || that.valueAxisType === DISCRETE;
let dataIndex = 0;
const dataSelector = this._getPointDataSelector();
const options = that.getOptions();
const addAggregatedData = (target, data, aggregationInfo) => {
if (!data) {
return
}
const processData = d => {
const pointData = d && dataSelector(d, options);
if (pointData && that._checkData(pointData)) {
pointData.aggregationInfo = aggregationInfo;
target.push(pointData)
}
};
if (Array.isArray(data)) {
data.forEach(processData)
} else {
processData(data)
}
};
const aggregationMethod = this._getAggregationMethod(isDiscrete, aggregateByCategory);
if (isDiscrete) {
if (aggregateByCategory) {
const categories = this.getArgumentAxis().getTranslator().getBusinessRange().categories;
const groups = categories.reduce(((g, category) => {
g[category.valueOf()] = [];
return g
}), {});
data.forEach((dataItem => {
groups[dataItem.argument.valueOf()].push(dataItem)
}));
return categories.reduce(((result, c) => {
addAggregatedData(result, aggregationMethod({
aggregationInterval: null,
intervalStart: c,
intervalEnd: c,
data: groups[c.valueOf()].map(getData)
}, that));
return result
}), [])
} else {
return data.reduce(((result, dataItem, index, data) => {
result[1].push(dataItem);
if (index === data.length - 1 || (index + 1) % interval === 0) {
const dataInInterval = result[1];
const aggregationInfo = {
aggregationInterval: interval,
data: dataInInterval.map(getData)
};
addAggregatedData(result[0], aggregationMethod(aggregationInfo, that));
result[1] = []
}
return result
}), [
[],
[]
])[0]
}
}
const aggregatedData = [];
if (1 === ticks.length) {
const aggregationInfo = {
intervalStart: ticks[0],
intervalEnd: ticks[0],
aggregationInterval: null,
data: data.map(getData)
};
addAggregatedData(aggregatedData, aggregationMethod(aggregationInfo, that), aggregationInfo)
} else {
for (let i = 1; i < ticks.length; i++) {
const intervalEnd = ticks[i];
const intervalStart = ticks[i - 1];
const dataInInterval = [];
while (data[dataIndex] && data[dataIndex].argument < intervalEnd) {
if (data[dataIndex].argument >= intervalStart) {
dataInInterval.push(data[dataIndex])
}
dataIndex++
}
const aggregationInfo = {
intervalStart: intervalStart,
intervalEnd: intervalEnd,
aggregationInterval: interval,
data: dataInInterval.map(getData)
};
addAggregatedData(aggregatedData, aggregationMethod(aggregationInfo, that), aggregationInfo)
}
}
that._endUpdateData();
return aggregatedData
},
canRenderCompleteHandle: function() {
const result = this._canRenderCompleteHandle;
delete this._canRenderCompleteHandle;
return !!result
},
isHovered: function() {
return !!(1 & this.fullState)
},
isSelected: function() {
return !!(2 & this.fullState)
},
isVisible: function() {
return this._visible
},
getAllPoints: function() {
this._createAllAggregatedPoints();
return (this._points || []).slice()
},
getPointByPos: function(pos) {
this._createAllAggregatedPoints();
return (this._points || [])[pos]
},
getVisiblePoints: function() {
return (this._drawnPoints || []).slice()
},
selectPoint: function(point) {
if (!point.isSelected()) {
setPointSelectedState(point, this._legendCallback);
this._eventPipe({
action: POINT_SELECT,
target: point
});
this._eventTrigger("pointSelectionChanged", {
target: point
})
}
},
deselectPoint: function(point) {
if (point.isSelected()) {
releasePointSelectedState(point, this._legendCallback);
this._eventPipe({
action: POINT_DESELECT,
target: point
});
this._eventTrigger("pointSelectionChanged", {
target: point
})
}
},
hover: function(mode) {
const eventTrigger = this._eventTrigger;
if (this.isHovered()) {
return
}
this.lastHoverMode = _normalizeEnum(mode || this._options.hoverMode);
this.fullState = this.fullState | HOVER_STATE;
this._changeStyle(this.lastHoverMode, void 0, this.isSelected() && "none" !== this.lastSelectionMode);
eventTrigger("seriesHoverChanged", {
target: this
})
},
clearHover: function() {
const eventTrigger = this._eventTrigger;
if (!this.isHovered()) {
return
}
this._resetNearestPoint();
this.fullState = this.fullState & ~HOVER_STATE;
this._changeStyle(this.lastHoverMode, HOVER, this.isSelected() && "none" !== this.lastSelectionMode);
eventTrigger("seriesHoverChanged", {
target: this
})
},
hoverPoint: function(point) {
const that = this;
if (!point.isHovered()) {
point.clearHover();
setPointHoverState(point, that._legendCallback);
that._canChangeView() && that._applyStyle(that._styles.hover);
that._eventPipe({
action: POINT_HOVER,
target: point
});
that._eventTrigger("pointHoverChanged", {
target: point
})
}
},
clearPointHover: function() {
const that = this;
that.getPoints().some((function(currentPoint) {
if (currentPoint.isHovered()) {
releasePointHoverState(currentPoint, that._legendCallback);
that._canChangeView() && that._applyStyle(that._styles.normal);
that._eventPipe({
action: "clearPointHover",
target: currentPoint
});
that._eventTrigger("pointHoverChanged", {
target: currentPoint
});
return true
}
return false
}))
},
showPointTooltip: function(point) {
triggerEvent(this._extGroups.seriesGroup, "showpointtooltip", point)
},
hidePointTooltip: function(point) {
triggerEvent(this._extGroups.seriesGroup, "hidepointtooltip", point)
},
select: function() {
const that = this;
if (!that.isSelected()) {
that._setSelectedState(that._options.selectionMode);
that._eventPipe({
action: SERIES_SELECT,
target: that
});
that._group.toForeground();
that._eventTrigger("seriesSelectionChanged", {
target: that
})
}
},
clearSelection: function() {
const that = this;
if (that.isSelected()) {
that._releaseSelectedState();
that._eventTrigger("seriesSelectionChanged", {
target: that
})
}
},
getPointsByArg: function(arg, skipPointsCreation) {
const that = this;
const argValue = arg.valueOf();
let points = that.pointsByArgument[argValue];
if (!points && !skipPointsCreation && that._createAllAggregatedPoints()) {
points = that.pointsByArgument[argValue]
}
return points || []
},
_createAllAggregatedPoints: function() {
if (this.useAggregation() && !this._useAllAggregatedPoints) {
this.createPoints(true);
return true
}
return false
},
getPointsByKeys: function(arg) {
return this.getPointsByArg(arg)
},
notify: function(data) {
const that = this;
const action = data.action;
const seriesModes = that._seriesModes;
const target = data.target;
const targetOptions = target.getOptions();
const pointHoverMode = _normalizeEnum(targetOptions.hoverMode);
const selectionModeOfPoint = _normalizeEnum(targetOptions.selectionMode);
if (action === POINT_HOVER) {
that._hoverPointHandler(target, pointHoverMode, data.notifyLegend)
} else if ("clearPointHover" === action) {
that._clearPointHoverHandler(target, pointHoverMode, data.notifyLegend)
} else if (action === SERIES_SELECT) {
target !== that && "single" === seriesModes.seriesSelectionMode && that.clearSelection()
} else if (action === POINT_SELECT) {
if ("single" === seriesModes.pointSelectionMode) {
that.getPoints().some((function(currentPoint) {
if (currentPoint !== target && currentPoint.isSelected()) {
that.deselectPoint(currentPoint);
return true
}
return false
}))
}
that._selectPointHandler(target, selectionModeOfPoint)
} else if (action === POINT_DESELECT) {
that._deselectPointHandler(target, selectionModeOfPoint)
}
},
_selectPointHandler: function(target, mode) {
const that = this;
if ("allseriespoints" === mode) {
target.series === that && that._setPointsView(SELECTION, target)
} else if ("allargumentpoints" === mode) {
that.getPointsByKeys(target.argument, target.argumentIndex).forEach((function(currentPoint) {
currentPoint !== target && currentPoint.setView(SELECTION)
}))
}
},
_deselectPointHandler: function(target, mode) {
if ("allseriespoints" === mode) {
target.series === this && this._resetPointsView(SELECTION, target)
} else if ("allargumentpoints" === mode) {
this.getPointsByKeys(target.argument, target.argumentIndex).forEach((function(currentPoint) {
currentPoint !== target && currentPoint.resetView(SELECTION)
}))
}
},
_hoverPointHandler: function(target, mode, notifyLegend) {
const that = this;
if (target.series !== that && "allargumentpoints" === mode) {
that.getPointsByKeys(target.argument, target.argumentIndex).forEach((function(currentPoint) {
currentPoint.setView(HOVER)
}));
notifyLegend && that._legendCallback(target)
} else if ("allseriespoints" === mode && target.series === that) {
that._setPointsView(HOVER, target)
}
},
_clearPointHoverHandler: function(target, mode, notifyLegend) {
const that = this;
if ("allargumentpoints" === mode) {
target.series !== that && that.getPointsByKeys(target.argument, target.argumentIndex).forEach((function(currentPoint) {
currentPoint.resetView(HOVER)
}));
notifyLegend && that._legendCallback(target)
} else if ("allseriespoints" === mode && target.series === that) {
that._resetPointsView(HOVER, target)
}
},
_deletePoints: function() {
this._disposePoints(this._points);
this._points = this._drawnPoints = null
},
_deleteTrackers: function() {
_each(this._trackers || [], (function(_, tracker) {
tracker.remove()
}));
this._trackersGroup && this._trackersGroup.dispose();
this._trackers = this._trackersGroup = null
},
dispose: function() {
this._deletePoints();
this._group.dispose();
this._labelsGroup && this._labelsGroup.dispose();
this._errorBarGroup && this._errorBarGroup.dispose();
this._deleteTrackers();
this._group = this._extGroups = this._markersGroup = this._elementsGroup = this._bordersGroup = this._labelsGroup = this._errorBarGroup = this._graphics = this._rangeData = this._renderer = this._styles = this._options = this._pointOptions = this._drawnPoints = this.pointsByArgument = this._segments = this._prevSeries = null
},
correctPosition: _noop,
drawTrackers: _noop,
getNeighborPoint: _noop,
areErrorBarsVisible: _noop,
_getColorId: _noop,
getMarginOptions: function() {
return this._patchMarginOptions({
percentStick: this.isFullStackedSeries()
})
},
getColor: function() {
return this.getLegendStyles().normal.fill
},
getOpacity: function() {
return this._options.opacity
},
getStackName: function() {
return this._stackName
},
getBarOverlapGroup: function() {
return this._options.barOverlapGroup
},
getPointByCoord: function(x, y) {
const point = this.getNeighborPoint(x, y);
return null !== point && void 0 !== point && point.coordsIn(x, y) ? point : null
},
getValueAxis: function() {
return this._valueAxis
},
getArgumentAxis: function() {
return this._argumentAxis
},
getMarkersGroup() {
return this._markersGroup
},
getRenderer() {
return this._renderer
},
removePointElements() {
if (this._markersGroup) {
_each(this._points, ((_, p) => p.deleteMarker()));
this._markersGroup.dispose();
this._markersGroup = null
}
},
removeGraphicElements() {
const that = this;
if (that._elementsGroup) {
that._elementsGroup.dispose();
that._elementsGroup = null
}
_each(that._graphics || [], ((_, elem) => {
that._removeElement(elem)
}));
that._graphics = null
},
removeBordersGroup() {
if (this._bordersGroup) {
this._bordersGroup.dispose();
this._bordersGroup = null
}
}
};
export const mixins = seriesNS.mixins;