@amcharts/amcharts5
Version:
amCharts 5
1,479 lines • 60.5 kB
JavaScript
import { __awaiter } from "tslib";
import { DataItem } from "../../../core/render/Component";
import { Series } from "../../../core/render/Series";
import { List } from "../../../core/util/List";
import { Container } from "../../../core/render/Container";
import { Graphics } from "../../../core/render/Graphics";
import * as $type from "../../../core/util/Type";
import * as $object from "../../../core/util/Object";
import * as $array from "../../../core/util/Array";
import * as $utils from "../../../core/util/Utils";
/**
* @ignore
*/
function min(left, right) {
if (left == null)
return right;
if (right == null)
return left;
return (right < left) ? right : left;
}
/**
* @ignore
*/
function max(left, right) {
if (left == null)
return right;
if (right == null)
return left;
return (left > right) ? left : right;
}
/**
* A base class for all XY chart series.
*
* @see {@link https://www.amcharts.com/docs/v5/charts/xy-chart/series/} for more info
*/
export class XYSeries extends Series {
constructor() {
super(...arguments);
this._axesDirty = false;
this._stackDirty = false;
this._selectionProcessed = false;
this._dataSets = {};
this._x = 0;
this._y = 0;
this._bullets = {};
/**
* A [[Container]] that us used to put series' elements in.
*
* @default Container.new()
*/
this.mainContainer = this.children.push(Container.new(this._root, {}));
/**
* A list of axis ranges that affect the series.
*
* @see {@link https://www.amcharts.com/docs/v5/charts/xy-chart/axes/axis-ranges/} for more info
*/
this.axisRanges = new List();
this._skipped = false;
this._couldStackTo = [];
this._reallyStackedTo = {};
this._stackedSeries = {};
this._aLocationX0 = 0;
this._aLocationX1 = 1;
this._aLocationY0 = 0;
this._aLocationY1 = 1;
this._showBullets = true;
this.valueXFields = [
"valueX",
"openValueX",
"lowValueX",
"highValueX"
];
this.valueYFields = [
"valueY",
"openValueY",
"lowValueY",
"highValueY"
];
this._emptyDataItem = new DataItem(this, undefined, {});
}
_afterNew() {
this.fields.push("categoryX", "categoryY", "openCategoryX", "openCategoryY");
this.valueFields.push("valueX", "valueY", "openValueX", "openValueY", "lowValueX", "lowValueY", "highValueX", "highValueY");
this._setRawDefault("vcx", 1);
this._setRawDefault("vcy", 1);
// this can't go to themes, as data might be parsed before theme
this._setRawDefault("valueXShow", "valueXWorking");
this._setRawDefault("valueYShow", "valueYWorking");
this._setRawDefault("openValueXShow", "openValueXWorking");
this._setRawDefault("openValueYShow", "openValueYWorking");
this._setRawDefault("lowValueXShow", "lowValueXWorking");
this._setRawDefault("lowValueYShow", "lowValueYWorking");
this._setRawDefault("highValueXShow", "highValueXWorking");
this._setRawDefault("highValueYShow", "highValueYWorking");
this._setRawDefault("lowValueXGrouped", "low");
this._setRawDefault("lowValueYGrouped", "low");
this._setRawDefault("highValueXGrouped", "high");
this._setRawDefault("highValueYGrouped", "high");
const xAxis = this.get("xAxis");
if (xAxis) {
this._xRenderer = xAxis.get("renderer");
}
const yAxis = this.get("yAxis");
if (yAxis) {
this._yRenderer = yAxis.get("renderer");
}
super._afterNew();
this._setC("maskContent", true);
// Re-wire the series when it is reassigned to a different axis after
// creation. Handled via `on()` (fires once on `.set()`, outside the
// validation cycle) so the swap's `set`/markDirty calls can't re-enter.
this._disposers.push(this.on("xAxis", () => {
this._switchAxis("xAxis");
}));
this._disposers.push(this.on("yAxis", () => {
this._switchAxis("yAxis");
}));
this._disposers.push(this.axisRanges.events.onAll((change) => {
if (change.type === "clear") {
$array.each(change.oldValues, (axisRange) => {
this._removeAxisRange(axisRange);
});
}
else if (change.type === "push") {
this._processAxisRange(change.newValue);
}
else if (change.type === "setIndex") {
this._processAxisRange(change.newValue);
}
else if (change.type === "insertIndex") {
this._processAxisRange(change.newValue);
}
else if (change.type === "removeIndex") {
this._removeAxisRange(change.oldValue);
}
else if (change.type === "moveIndex") {
this._processAxisRange(change.value);
}
else {
throw new Error("Unknown IStreamEvent type");
}
}));
this.states._createC("hidden", { opacity: 1, visible: false });
this.onPrivate("startIndex", () => {
this.root.events.once("frameended", () => {
this.updateLegendValue();
});
});
this.onPrivate("endIndex", () => {
this.root.events.once("frameended", () => {
this.updateLegendValue();
});
});
this._makeFieldNames();
// Value fields were historically meant to be set once at creation. Allow
// them to be changed dynamically: rebuild the field map and names
// synchronously when a `*Field` setting changes, so a subsequent
// `data.setAll()` (which parses synchronously) picks up the new field.
const onFieldChange = () => {
this._updateFields();
this._makeFieldNames();
this.markDirtyValues();
};
$array.each([...this.valueXFields, ...this.valueYFields], (key) => {
this.on((key + "Field"), onFieldChange);
});
}
_processAxisRange(axisRange) {
const container = Container.new(this._root, {});
axisRange.container = container;
this.children.push(container);
axisRange.series = this;
const axisDataItem = axisRange.axisDataItem;
axisDataItem.setRaw("isRange", true);
const axis = axisDataItem.component;
if (axis) {
axis._processAxisRange(axisDataItem, ["range", "series"]);
const bullet = axisDataItem.get("bullet");
if (bullet) {
const sprite = bullet.get("sprite");
if (sprite) {
sprite.setPrivate("visible", false);
}
}
const axisFill = axisDataItem.get("axisFill");
if (axisFill) {
container._setC("mask", axisFill);
}
axis._seriesAxisRanges.push(axisDataItem);
}
}
_onDataClear() {
super._onDataClear();
$object.each(this._dataSets, (_key, dataItems) => {
$array.each(dataItems, (dataItem) => {
dataItem.dispose();
});
dataItems.length = 0;
});
}
_removeAxisRange(axisRange) {
const axisDataItem = axisRange.axisDataItem;
const axis = axisDataItem.component;
axisDataItem.dispose();
$array.remove(axis._seriesAxisRanges, axisDataItem);
const container = axisRange.container;
if (container) {
container.dispose();
}
}
_updateFields() {
super._updateFields();
this._valueXFields = [];
this._valueYFields = [];
this._valueXShowFields = [];
this._valueYShowFields = [];
this.__valueXShowFields = [];
this.__valueYShowFields = [];
if (this.valueXFields) {
$array.each(this.valueXFields, (key) => {
const field = this.get((key + "Field"));
if (field) {
this._valueXFields.push(key);
let field = this.get((key + "Show"));
this.__valueXShowFields.push(field);
if (field.indexOf("Working") != -1) {
this._valueXShowFields.push(field.split("Working")[0]);
}
else {
this._valueXShowFields.push(field);
}
}
});
}
if (this.valueYFields) {
$array.each(this.valueYFields, (key) => {
const field = this.get((key + "Field"));
if (field) {
this._valueYFields.push(key);
let field = this.get((key + "Show"));
this.__valueYShowFields.push(field);
if (field.indexOf("Working") != -1) {
this._valueYShowFields.push(field.split("Working")[0]);
}
else {
this._valueYShowFields.push(field);
}
}
});
}
}
_dispose() {
super._dispose();
this._bullets = {};
const chart = this.chart;
if (chart) {
chart.series.removeValue(this);
}
const xAxis = this.get("xAxis");
if (xAxis) {
$array.removeFirst(xAxis.series, this);
}
const yAxis = this.get("yAxis");
if (yAxis) {
$array.removeFirst(yAxis.series, this);
}
}
// TODO use SelectKeys<this["_privateSettings"], number | undefined>
_min(key, value) {
let newValue = min(this.getPrivate(key), value);
this.setPrivate(key, newValue);
}
// TODO use SelectKeys<this["_privateSettings"], number | undefined>
_max(key, value) {
let newValue = max(this.getPrivate(key), value);
this.setPrivate(key, newValue);
}
_shouldMakeBullet(dataItem) {
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const baseAxis = this.get("baseAxis");
if (!xAxis.inited || !yAxis.inited) {
return false;
}
const minBulletDistance = this.get("minBulletDistance", 0);
if (minBulletDistance > 0) {
let startIndex = this.startIndex();
let endIndex = this.endIndex();
let count = endIndex - startIndex;
if (xAxis == baseAxis) {
if (this._xRenderer.axisLength() / count < minBulletDistance / 5) {
return false;
}
}
else if (yAxis == baseAxis) {
if (this._yRenderer.axisLength() / count < minBulletDistance / 5) {
return false;
}
}
}
if (dataItem.get(this._xField) != null && dataItem.get(this._yField) != null) {
return true;
}
return false;
}
_makeFieldNames() {
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const xName = xAxis.getPrivate("name");
const xCapName = $utils.capitalizeFirst(xName);
const yName = yAxis.getPrivate("name");
const yCapName = $utils.capitalizeFirst(yName);
const xLetter = xAxis.get("renderer").getPrivate("letter");
const yLetter = yAxis.get("renderer").getPrivate("letter");
const open = "open";
const low = "low";
const high = "high";
const show = "Show";
if (xAxis.className === "ValueAxis") {
this._xField = this.get((xName + xLetter + show));
this._xOpenField = this.get((open + xCapName + xLetter + show));
this._xLowField = this.get((low + xCapName + xLetter + show));
this._xHighField = this.get((high + xCapName + xLetter + show));
}
else {
this._xField = (xName + xLetter);
this._xOpenField = (open + xCapName + xLetter);
this._xLowField = (low + xCapName + xLetter);
this._xHighField = (high + xCapName + xLetter);
}
if (yAxis.className === "ValueAxis") {
this._yField = this.get((yName + yLetter + show));
this._yOpenField = this.get((open + yCapName + yLetter + show));
this._yLowField = this.get((low + yCapName + yLetter + show));
this._yHighField = this.get((high + yCapName + yLetter + show));
}
else {
this._yField = (yName + yLetter);
this._yOpenField = (open + yCapName + yLetter);
this._yLowField = (low + yCapName + yLetter);
this._yHighField = (high + yCapName + yLetter);
}
}
_fixVC() {
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const baseAxis = this.get("baseAxis");
const hiddenState = this.states.lookup("hidden");
const sequencedInterpolation = this.get("sequencedInterpolation");
if (hiddenState) {
let value = 0;
if (sequencedInterpolation) {
value = 0.999999999999; // makes animate, good for stacked
}
if (xAxis === baseAxis) {
hiddenState._setC("vcy", value);
}
else if (yAxis === baseAxis) {
hiddenState._setC("vcx", value);
}
else {
hiddenState._setC("vcy", value);
hiddenState._setC("vcx", value);
}
}
}
_handleMaskBullets() {
if (this.isDirty("maskBullets")) {
this.bulletsContainer.set("maskContent", this.get("maskBullets"));
}
}
_fixPosition() {
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
this._setC("x", xAxis.x() - $utils.relativeToValue(xAxis.get("centerX", 0), xAxis.width()) - xAxis.parent.get("paddingLeft", 0));
this._setC("y", yAxis.y() - $utils.relativeToValue(yAxis.get("centerY", 0), yAxis.height()) - yAxis.parent.get("paddingTop", 0));
this.bulletsContainer.set("y", this.y());
this.bulletsContainer.set("x", this.x());
}
/**
* Re-wires the series when its `xAxis`/`yAxis` setting is changed to a
* different axis after creation, mirroring `XYChart._processSeries`. Invoked
* from an `on()` handler (fires once on `.set()`) rather than from
* `_prepareChildren`, so setting `baseAxis`/markDirty here can't re-enter.
*/
_switchAxis(key) {
const prevAxis = this._prevSettings[key];
const axis = this.get(key);
if (prevAxis && axis && prevAxis !== axis) {
// Detach from the previous axis
$array.remove(prevAxis.series, this);
const prevDp = key == "xAxis" ? this._posXDp : this._posYDp;
if (prevDp) {
prevDp.dispose();
}
prevAxis.markDirtyExtremes();
// Re-point baseAxis if it referenced the previous axis
if (this.get("baseAxis") === prevAxis) {
if (this._computedProperties["baseAxis"]) {
// the chart's own pick, so make it again the way the chart
// would - carrying the old one over lands somewhere a rebuilt
// chart never would
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
if (xAxis && yAxis) {
const categoryish = yAxis.isType("CategoryAxis") || yAxis.isType("DateAxis");
this._setC("baseAxis", categoryish ? yAxis : xAxis);
}
else {
this._setC("baseAxis", axis);
}
}
else {
this.set("baseAxis", axis);
}
}
// Attach to the new axis
$array.move(axis.series, this);
const dp = this.addDisposer(axis.events.on("positionchanged", () => {
this._fixPosition();
}));
if (key == "xAxis") {
this._posXDp = dp;
}
else {
this._posYDp = dp;
}
axis.markDirtyExtremes();
axis._seriesAdded = true;
this._markDirtyAxes();
}
}
_prepareChildren() {
super._prepareChildren();
this._xRenderer = this.get("xAxis").get("renderer");
this._yRenderer = this.get("yAxis").get("renderer");
this._bullets = {};
if (this.isDirty("valueYShow") || this.isDirty("valueXShow") || this.isDirty("openValueYShow") || this.isDirty("openValueXShow") || this.isDirty("lowValueYShow") || this.isDirty("lowValueXShow") || this.isDirty("highValueYShow") || this.isDirty("highValueXShow")) {
this._updateFields();
this._makeFieldNames();
this._valuesDirty = true;
}
if (this.isDirty("xAxis") || this.isDirty("yAxis")) {
this._valuesDirty = true;
}
// Location settings are read while (re)building points/columns in
// `_updateChildren` (gated by `_valuesDirty`), so force that rebuild when
// they change dynamically.
if (this.isDirty("locationX") || this.isDirty("locationY") || this.isDirty("openLocationX") || this.isDirty("openLocationY") || this.isDirty("exactLocationX") || this.isDirty("exactLocationY")) {
this._valuesDirty = true;
}
this._setC("width", this.get("xAxis").width());
this._setC("height", this.get("yAxis").height());
this._handleMaskBullets();
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const baseAxis = this.get("baseAxis");
const tooltipPositionX = this.get("tooltipPositionX");
let tooltipFieldX;
switch (tooltipPositionX) {
case "open":
tooltipFieldX = this._xOpenField;
break;
case "low":
tooltipFieldX = this._xLowField;
break;
case "high":
tooltipFieldX = this._xHighField;
break;
default:
tooltipFieldX = this._xField;
}
this._tooltipFieldX = tooltipFieldX;
const tooltipPositionY = this.get("tooltipPositionY");
let tooltipFieldY;
switch (tooltipPositionY) {
case "open":
tooltipFieldY = this._yOpenField;
break;
case "low":
tooltipFieldY = this._yLowField;
break;
case "high":
tooltipFieldY = this._yHighField;
break;
default:
tooltipFieldY = this._yField;
}
this._tooltipFieldY = tooltipFieldY;
if (this.isDirty("baseAxis")) {
this._fixVC();
}
this._fixPosition();
const stacked = this.get("stacked");
if (this.isDirty("stacked") || (this.isDirty("stackToNegative") && stacked)) {
if (stacked) {
if (this._valuesDirty && !this._dataProcessed) {
}
else {
this._valuesDirty = true;
this._stack();
}
}
else {
this._valuesDirty = true;
this._unstack();
}
}
if (this._valuesDirty && !this._dataProcessed) {
this._dataProcessed = true;
if (stacked) {
this._stack();
}
let minX = undefined;
let maxX = undefined;
let minY = undefined;
let maxY = undefined;
$array.each(this.dataItems, (dataItem) => {
$array.each(this._valueXShowFields, (key) => {
let value = dataItem.get(key);
if (value != null) {
if (stacked) {
value += this.getStackedXValue(dataItem, key);
}
minX = min(minX, value);
maxX = max(maxX, value);
}
});
$array.each(this._valueYShowFields, (key) => {
let value = dataItem.get(key);
if (value != null) {
if (stacked) {
value += this.getStackedYValue(dataItem, key);
}
minY = min(minY, value);
maxY = max(maxY, value);
}
});
xAxis.processSeriesDataItem(dataItem, this._valueXFields);
yAxis.processSeriesDataItem(dataItem, this._valueYFields);
});
this._min("minX", minX);
this._max("maxX", maxX);
this._min("minY", minY);
this._max("maxY", maxY);
xAxis._seriesValuesDirty = true;
yAxis._seriesValuesDirty = true;
if (!this.get("ignoreMinMax")) {
if (this.isPrivateDirty("minX") || this.isPrivateDirty("maxX")) {
xAxis.markDirtyExtremes();
}
if (this.isPrivateDirty("minY") || this.isPrivateDirty("maxY")) {
yAxis.markDirtyExtremes();
}
}
this._markStakedDirtyStack();
//this.updateLegendMarker(undefined); // causes legend marker to change color instantly when on
if (!this.get("tooltipDataItem")) {
this.updateLegendValue(undefined);
}
}
if (this.isDirty("vcx") || this.isDirty("vcy")) {
this._markStakedDirtyStack();
}
if (!this._dataGrouped) {
xAxis._groupSeriesData(this);
yAxis._groupSeriesData(this);
this._dataGrouped = true;
}
if (this._valuesDirty || this.isPrivateDirty("startIndex") || this.isPrivateDirty("adjustedStartIndex") || this.isPrivateDirty("endIndex") || this.isDirty("vcx") || this.isDirty("vcy") || this._stackDirty || this._sizeDirty || this.isDirty("minBulletDistance")) {
let startIndex = this.startIndex();
let endIndex = this.endIndex();
let minBulletDistance = this.get("minBulletDistance", 0);
if (minBulletDistance > 0 && baseAxis) {
if (baseAxis.get("renderer").axisLength() / (endIndex - startIndex) > minBulletDistance) {
this._showBullets = true;
}
else {
this._showBullets = false;
}
}
if ((this._psi != startIndex || this._pei != endIndex || this.isDirty("vcx") || this.isDirty("vcy") || this.isPrivateDirty("adjustedStartIndex") || this._stackDirty || this._valuesDirty) && !this._selectionProcessed) {
this._selectionProcessed = true;
const vcx = this.get("vcx", 1);
const vcy = this.get("vcy", 1);
const stacked = this.get("stacked", false);
const outOfSelection = this.getPrivate("outOfSelection");
if (baseAxis === xAxis || !baseAxis) {
yAxis._calculateTotals();
this.setPrivateRaw("selectionMinY", undefined);
this.setPrivateRaw("selectionMaxY", undefined);
if (!outOfSelection) {
for (let i = startIndex; i < endIndex; i++) {
this.processYSelectionDataItem(this.dataItems[i], vcy, stacked);
}
}
else {
yAxis.markDirtySelectionExtremes();
}
}
if (baseAxis === yAxis || !baseAxis) {
xAxis._calculateTotals();
this.setPrivateRaw("selectionMinX", undefined);
this.setPrivateRaw("selectionMaxX", undefined);
if (!outOfSelection) {
for (let i = startIndex; i < endIndex; i++) {
this.processXSelectionDataItem(this.dataItems[i], vcx, stacked);
}
}
else {
xAxis.markDirtySelectionExtremes();
}
}
if (baseAxis === xAxis || !baseAxis) {
if (this.get("valueYShow") !== "valueYWorking" || this.get("useSelectionExtremes")) {
const selectionMinY = this.getPrivate("selectionMinY");
if (selectionMinY != null) {
this.setPrivateRaw("minY", selectionMinY);
yAxis.markDirtyExtremes();
}
const selectionMaxY = this.getPrivate("selectionMaxY");
if (selectionMaxY != null) {
this.setPrivateRaw("maxY", selectionMaxY);
yAxis.markDirtyExtremes();
}
}
}
if (baseAxis === yAxis || !baseAxis) {
if (this.get("valueXShow") !== "valueXWorking" || this.get("useSelectionExtremes")) {
const selectionMinX = this.getPrivate("selectionMinX");
if (selectionMinX != null) {
this.setPrivateRaw("minX", selectionMinX);
xAxis.markDirtyExtremes();
}
const selectionMaxX = this.getPrivate("selectionMaxX");
if (selectionMaxX != null) {
this.setPrivateRaw("maxX", selectionMaxX);
xAxis.markDirtyExtremes();
}
}
}
if (this.isPrivateDirty("selectionMinX") || this.isPrivateDirty("selectionMaxX")) {
xAxis.markDirtySelectionExtremes();
}
if (this.isPrivateDirty("selectionMinY") || this.isPrivateDirty("selectionMaxY")) {
yAxis.markDirtySelectionExtremes();
}
// this.updateLegendValue(undefined); flickers while panning
}
}
}
_postUpdateData() {
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
xAxis._seriesValuesDirty = true;
yAxis._seriesValuesDirty = true;
$array.each(this.dataItems, (dataItem) => {
dataItem.open = undefined;
dataItem.close = undefined;
xAxis.processSeriesDataItem(dataItem, this._valueXFields);
yAxis.processSeriesDataItem(dataItem, this._valueYFields);
});
}
_makeRangeMask() {
if (this.axisRanges.length > 0) {
let mainContainerMask = this._mainContainerMask;
if (mainContainerMask == null) {
mainContainerMask = this.children.push(Graphics.new(this._root, {}));
this._mainContainerMask = mainContainerMask;
mainContainerMask.set("draw", (display, target) => {
const parent = this.parent;
if (parent) {
const w = this._root.container.width();
const h = this._root.container.height();
display.moveTo(-w, -h);
display.lineTo(-w, h * 2);
display.lineTo(w * 2, h * 2);
display.lineTo(w * 2, -h);
display.lineTo(-w, -h);
this.axisRanges.each((axisRange) => {
const fill = axisRange.axisDataItem.get("axisFill");
if (parent) {
if (fill) {
let draw = fill.get("draw");
if (draw) {
draw(display, target);
}
}
}
});
}
this.mainContainer._display.mask = mainContainerMask._display;
});
}
mainContainerMask.markDirty();
mainContainerMask._markDirtyKey("fill");
}
else {
this.mainContainer._display.mask = null;
}
}
_updateChildren() {
super._updateChildren();
// save for performance
this._x = this.x();
this._y = this.y();
this._makeRangeMask();
}
_stack() {
const chart = this.chart;
if (chart) {
const seriesIndex = chart.series.indexOf(this);
this._couldStackTo = [];
if (seriesIndex > 0) {
let series;
for (let i = seriesIndex - 1; i >= 0; i--) {
series = chart.series.getIndex(i);
if (series.get("xAxis") === this.get("xAxis") && series.get("yAxis") === this.get("yAxis") && series.className === this.className) {
this._couldStackTo.push(series);
if (!series.get("stacked")) {
break;
}
}
}
}
this._stackDataItems();
}
}
_unstack() {
$object.each(this._reallyStackedTo, (_key, value) => {
delete (value._stackedSeries[this.uid]);
});
this._reallyStackedTo = {};
$array.each(this.dataItems, (dataItem) => {
dataItem.setRaw("stackToItemY", undefined);
dataItem.setRaw("stackToItemX", undefined);
});
}
_handleRemoved() {
const xAxis = this.get("xAxis");
if (xAxis) {
xAxis._handleSeriesRemoved();
}
const yAxis = this.get("yAxis");
if (yAxis) {
yAxis._handleSeriesRemoved();
}
}
_stackDataItems() {
// this works only with the same number of data @todo: search by date/category?
const baseAxis = this.get("baseAxis");
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
let field;
let stackToItemKey;
if (baseAxis === xAxis) {
field = "valueY";
stackToItemKey = "stackToItemY";
}
else if (baseAxis === yAxis) {
field = "valueX";
stackToItemKey = "stackToItemX";
}
let len = this._couldStackTo.length;
let index = 0;
const stackToNegative = this.get("stackToNegative");
this._reallyStackedTo = {};
$array.each(this.dataItems, (dataItem) => {
for (let s = 0; s < len; s++) {
let stackToSeries = this._couldStackTo[s];
let stackToItem = stackToSeries.dataItems[index];
let value = dataItem.get(field);
if (stackToItem) {
let stackValue = stackToItem.get(field);
if (stackToNegative) {
if ($type.isNumber(value)) {
if ($type.isNumber(stackValue)) {
if (s == len - 1) {
dataItem.setRaw(stackToItemKey, undefined);
}
if (value >= 0 && stackValue >= 0) {
dataItem.setRaw(stackToItemKey, stackToItem);
this._reallyStackedTo[stackToSeries.uid] = stackToSeries;
stackToSeries._stackedSeries[this.uid] = this;
break;
}
if (value < 0 && stackValue < 0) {
dataItem.setRaw(stackToItemKey, stackToItem);
this._reallyStackedTo[stackToSeries.uid] = stackToSeries;
stackToSeries._stackedSeries[this.uid] = this;
break;
}
}
}
else {
break;
}
}
else {
if ($type.isNumber(value) && $type.isNumber(stackValue)) {
dataItem.setRaw(stackToItemKey, stackToItem);
this._reallyStackedTo[stackToSeries.uid] = stackToSeries;
stackToSeries._stackedSeries[this.uid] = this;
break;
}
}
}
}
index++;
});
}
processXSelectionDataItem(dataItem, vcx, stacked) {
$array.each(this.__valueXShowFields, (key) => {
let value = dataItem.get(key);
if (value != null) {
if (stacked) {
value += this.getStackedXValueWorking(dataItem, key);
}
this._min("selectionMinX", value);
this._max("selectionMaxX", value * vcx);
}
});
}
processYSelectionDataItem(dataItem, vcy, stacked) {
$array.each(this.__valueYShowFields, (key) => {
let value = dataItem.get(key);
if (value != null) {
if (stacked) {
value += this.getStackedYValueWorking(dataItem, key);
}
this._min("selectionMinY", value);
this._max("selectionMaxY", value * vcy);
}
});
}
/**
* @ignore
*/
getStackedYValueWorking(dataItem, key) {
const stackToItem = dataItem.get("stackToItemY");
if (stackToItem) {
const stackedToSeries = stackToItem.component;
return stackToItem.get(key, 0) * stackedToSeries.get("vcy", 1) + this.getStackedYValueWorking(stackToItem, key);
}
return 0;
}
/**
* @ignore
*/
getStackedXValueWorking(dataItem, key) {
const stackToItem = dataItem.get("stackToItemX");
if (stackToItem) {
const stackedToSeries = stackToItem.component;
return stackToItem.get(key, 0) * stackedToSeries.get("vcx", 1) + this.getStackedXValueWorking(stackToItem, key);
}
return 0;
}
/**
* @ignore
*/
getStackedYValue(dataItem, key) {
const stackToItem = dataItem.get("stackToItemY");
if (stackToItem) {
return stackToItem.get(key, 0) + this.getStackedYValue(stackToItem, key);
}
return 0;
}
/**
* @ignore
*/
getStackedXValue(dataItem, key) {
const stackToItem = dataItem.get("stackToItemX");
if (stackToItem) {
return stackToItem.get(key, 0) + this.getStackedXValue(stackToItem, key);
}
return 0;
}
/**
* @ignore
*/
createLegendMarker(_dataItem) {
this.updateLegendMarker();
}
_markDirtyAxes() {
this._axesDirty = true;
this.markDirty();
}
_markDataSetDirty() {
this._afterDataChange();
this._valuesDirty = true;
this._dataProcessed = false;
this._aggregatesCalculated = false;
this.markDirty();
}
_clearDirty() {
super._clearDirty();
this._axesDirty = false;
this._selectionProcessed = false;
this._stackDirty = false;
this._dataProcessed = false;
}
_positionBullet(bullet) {
let sprite = bullet.get("sprite");
if (sprite) {
let dataItem = sprite.dataItem;
let locationX = bullet.get("locationX", dataItem.get("locationX", 0.5));
let locationY = bullet.get("locationY", dataItem.get("locationY", 0.5));
let xAxis = this.get("xAxis");
let yAxis = this.get("yAxis");
let exactLocationX = this.get("exactLocationX", false);
let exactLocationY = this.get("exactLocationY", false);
let positionX = xAxis.getDataItemPositionX(dataItem, this._xField, locationX, this.get("vcx", 1), exactLocationX);
let positionY = yAxis.getDataItemPositionY(dataItem, this._yField, locationY, this.get("vcy", 1), exactLocationY);
let point = this.getPoint(positionX, positionY);
let left = dataItem.get("left", point.x);
let right = dataItem.get("right", point.x);
let top = dataItem.get("top", point.y);
let bottom = dataItem.get("bottom", point.y);
let x = 0;
let y = 0;
let w = right - left;
let h = bottom - top;
if (this._shouldShowBullet(positionX, positionY)) {
sprite.setPrivate("visible", !bullet.getPrivate("hidden"));
let field = bullet.get("field");
const baseAxis = this.get("baseAxis");
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
if (field != undefined) {
let realField;
if (baseAxis == xAxis) {
if (field == "value") {
realField = this._yField;
}
else if (field == "open") {
realField = this._yOpenField;
}
else if (field == "high") {
realField = this._yHighField;
}
else if (field == "low") {
realField = this._yLowField;
}
if (realField) {
positionY = yAxis.getDataItemPositionY(dataItem, realField, 0, this.get("vcy", 1), exactLocationY);
point = this._yRenderer.positionToPoint(positionY);
y = point.y;
x = left + w * locationX;
}
}
else {
if (field == "value") {
realField = this._xField;
}
else if (field == "open") {
realField = this._xOpenField;
}
else if (field == "high") {
realField = this._xHighField;
}
else if (field == "low") {
realField = this._xLowField;
}
if (realField) {
positionX = xAxis.getDataItemPositionX(dataItem, realField, 0, this.get("vcx", 1), exactLocationX);
point = this._xRenderer.positionToPoint(positionX);
x = point.x;
y = bottom - h * locationY;
}
}
}
else {
x = left + w * locationX;
y = bottom - h * locationY;
}
const stacked = bullet.get("stacked");
if (stacked) {
const chart = this.chart;
if (baseAxis == xAxis) {
let previousBullet = this._bullets[positionX + "_" + positionY];
if (previousBullet) {
let previousBounds = previousBullet.bounds();
let bounds = sprite.localBounds();
let yo = y;
y = previousBounds.top;
if (stacked == "down") {
y = previousBounds.bottom - bounds.top;
}
else if (stacked == "auto") {
if (chart) {
if (yo < chart.plotContainer.height() / 2) {
y = previousBounds.bottom - bounds.top;
}
else {
y += bounds.bottom;
}
}
}
else {
y += bounds.bottom;
}
}
this._bullets[positionX + "_" + positionY] = sprite;
}
else {
let previousBullet = this._bullets[positionX + "_" + positionY];
if (previousBullet) {
let previousBounds = previousBullet.bounds();
let bounds = sprite.localBounds();
let xo = x;
x = previousBounds.right;
if (stacked == "down") {
x = previousBounds.left - bounds.right;
}
else if (stacked == "auto") {
if (chart) {
if (xo < chart.plotContainer.width() / 2) {
x = previousBounds.left - bounds.right;
}
else {
x -= bounds.left;
}
}
}
else {
x -= bounds.left;
}
}
this._bullets[positionX + "_" + positionY] = sprite;
}
}
if (sprite.isType("Label")) {
sprite.setPrivate("maxWidth", Math.abs(w));
sprite.setPrivate("maxHeight", Math.abs(h));
}
sprite.setAll({ x, y });
}
else {
sprite.setPrivate("visible", false);
}
}
}
_shouldShowBullet(_positionX, _positionY) {
return this._showBullets;
}
/**
* @ignore
*/
setDataSet(id) {
if (this._dataSets[id]) {
this._handleDataSetChange();
this._dataItems = this._dataSets[id];
this._markDataSetDirty();
this._dataSetId = id;
const type = "datasetchanged";
if (this.events.isEnabled(type)) {
this.events.dispatch(type, { type: type, target: this, id: id });
}
}
}
/**
* @ignore
*/
resetGrouping() {
$object.each(this._dataSets, (_key, dataSet) => {
if (dataSet != this._mainDataItems) {
$array.each(dataSet, (dataItem) => {
dataItem.dispose();
});
}
});
this._dataSets = {};
this._dataItems = this.mainDataItems;
}
_handleDataSetChange() {
$array.each(this._dataItems, (dataItem) => {
let bullets = dataItem.bullets;
if (bullets) {
$array.each(bullets, (bullet) => {
if (bullet) {
let sprite = bullet.get("sprite");
if (sprite) {
sprite.setPrivate("visible", false);
}
}
});
}
});
this._selectionProcessed = false; // for totals to be calculated
}
/**
* Shows hidden series.
*
* @param duration Duration of animation in milliseconds
* @return Animation promise
*/
show(duration) {
const _super = Object.create(null, {
show: { get: () => super.show }
});
return __awaiter(this, void 0, void 0, function* () {
this._fixVC();
let promises = [];
promises.push(_super.show.call(this, duration).then(() => {
this._isShowing = false;
let xAxis = this.get("xAxis");
let yAxis = this.get("yAxis");
let baseAxis = this.get("baseAxis");
if (yAxis !== baseAxis) {
yAxis.markDirtySelectionExtremes();
}
if (xAxis !== baseAxis) {
xAxis.markDirtySelectionExtremes();
}
}));
promises.push(this.bulletsContainer.show(duration));
promises.push(this._sequencedShowHide(true, duration));
yield Promise.all(promises);
});
}
/**
* Hides series.
*
* @param duration Duration of animation in milliseconds
* @return Animation promise
*/
hide(duration) {
const _super = Object.create(null, {
hide: { get: () => super.hide }
});
return __awaiter(this, void 0, void 0, function* () {
this._fixVC();
let promises = [];
promises.push(_super.hide.call(this, duration).then(() => {
this._isHiding = false;
}));
promises.push(this.bulletsContainer.hide(duration));
promises.push(this._sequencedShowHide(false, duration));
yield Promise.all(promises);
});
}
/**
* Shows series's data item.
*
* @param dataItem Data item
* @param duration Animation duration in milliseconds
* @return Promise
*/
showDataItem(dataItem, duration) {
const _super = Object.create(null, {
showDataItem: { get: () => super.showDataItem }
});
return __awaiter(this, void 0, void 0, function* () {
const promises = [_super.showDataItem.call(this, dataItem, duration)];
if (!$type.isNumber(duration)) {
duration = this.get("stateAnimationDuration", 0);
}
const easing = this.get("stateAnimationEasing");
$array.each(this._valueFields, (key) => {
promises.push(dataItem.animate({ key: key + "Working", to: dataItem.get(key), duration: duration, easing: easing }).waitForStop());
});
yield Promise.all(promises);
});
}
/**
* Hides series's data item.
*
* @param dataItem Data item
* @param duration Animation duration in milliseconds
* @return Promise
*/
hideDataItem(dataItem, duration) {
const _super = Object.create(null, {
hideDataItem: { get: () => super.hideDataItem }
});
return __awaiter(this, void 0, void 0, function* () {
const promises = [_super.hideDataItem.call(this, dataItem, duration)];
let hiddenState = this.states.lookup("hidden");
if (!hiddenState) {
hiddenState = this.states.create("hidden", {});
}
if (!$type.isNumber(duration)) {
duration = hiddenState.get("stateAnimationDuration", this.get("stateAnimationDuration", 0));
}
const easing = hiddenState.get("stateAnimationEasing", this.get("stateAnimationEasing"));
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const baseAxis = this.get("baseAxis");
const stacked = this.get("stacked");
if (baseAxis === xAxis || !baseAxis) {
$array.each(this._valueYFields, (key) => {
let min = yAxis.getPrivate("min");
let baseValue = yAxis.baseValue();
if ($type.isNumber(min) && min > baseValue) {
baseValue = min;
}
if (stacked) {
baseValue = 0;
}
let value = dataItem.get(key);
if (value != null) {
promises.push(dataItem.animate({ key: key + "Working", to: baseValue, duration: duration, easing: easing }).waitForStop());
}
});
}
if (baseAxis === yAxis || !baseAxis) {
let min = xAxis.getPrivate("min");
let baseValue = xAxis.baseValue();
if ($type.isNumber(min) && min > baseValue) {
baseValue = min;
}
if (stacked) {
baseValue = 0;
}
$array.each(this._valueXFields, (key) => {
let value = dataItem.get(key);
if (value != null) {
promises.push(dataItem.animate({ key: key + "Working", to: baseValue, duration: duration, easing: easing }).waitForStop());
}
});
}
yield Promise.all(promises);
});
}
_markDirtyStack() {
this._stackDirty = true;
this.markDirty();
this._markStakedDirtyStack();
}
_markStakedDirtyStack() {
const stackedSeries = this._stackedSeries;
if (stackedSeries) {
$object.each(stackedSeries, (_key, value) => {
if (!value._stackDirty) {
value._markDirtyStack();
}
});
}
}
_afterChanged() {
super._afterChanged();
if (this._skipped) {
this._markDirtyAxes();
this._skipped = false;
}
}
/**
* Shows a tooltip for specific data item.
*
* @param dataItem Data item
*/
showDataItemTooltip(dataItem) {
if (!this.getPrivate("doNotUpdateLegend")) {
this.updateLegendMarker(dataItem);
this.updateLegendValue(dataItem);
}
const tooltip = this.get("tooltip");
const exactLocationX = this.get("exactLocationX", false);
const exactLocationY = this.get("exactLocationY", false);
if (tooltip) {
if (!this.isHidden() && this.get("visible")) {
tooltip._setDataItem(dataItem);
if (dataItem) {
let locationX = this.get("locationX", 0);
let locationY = this.get("locationY", 1);
let itemLocationX = dataItem.get("locationX", locationX);
let itemLocationY = dataItem.get("locationY", locationY);
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const vcx = this.get("vcx", 1);
const vcy = this.get("vcy", 1);
const xPos = xAxis.getDataItemPositionX(dataItem, this._tooltipFieldX, this._aLocationX0 + (this._aLocationX1 - this._aLocationX0) * itemLocationX, vcx, exactLocationX);
const yPos = yAxis.getDataItemPositionY(dataItem, this._tooltipFieldY, this._aLocationY0 + (this._aLocationY1 - this._aLocationY0) * itemLocationY, vcy, exactLocationY);
const point = this.getPoint(xPos, yPos);
let show = true;
$array.each(this._valueFields, (field) => {
if (dataItem.get(field) == null) {
show = false;
}
});
if (show) {
const chart = this.chart;
if (chart && chart.inPlot(point)) {
tooltip.label.text.markDirtyText();
tooltip.set("tooltipTarget", this._getTooltipTarget(dataItem));
tooltip.set("pointTo", this._display.toGlobal({ x: point.x, y: point.y }));
}
else {
tooltip._setDataItem(undefined);
}
}
else {
tooltip._setDataItem(undefined);
}
}
}
else {
this.hideTooltip();
}
}
}
hideTooltip() {
const tooltip = this.get("tooltip");
if (tooltip) {
tooltip.set("tooltipTarget", this);
}
return super.hideTooltip();
}
_getTooltipTarget(dataItem) {
if (this.get("seriesTooltipTarget") == "bullet") {
const bullets = dataItem.bullets;
if (bullets && bullets.length > 0) {
const bullet = bullets[0];
const sprite = bullet.get("sprite");
if (sprite) {
return sprite;
}
}
}
return this;
}
/**
* @ignore
*/
updateLegendValue(dataItem) {
const legendDataItem = this.get("legendDataItem");
if (legendDataItem) {
const label = legendDataItem.get("label");
if (label) {
let txt = "";
if (dataItem) {
label._setDataItem(dataItem);
txt = this.get("legendLabelText", label.get("text", this.get("name", "")));
}
else {
label._setDataItem(this._emptyDataItem);
txt = this.get("legendRangeLabelText", this.get("legendLabelText", label.get("text", this.get("name", ""))));
}
label.set("text", txt);
}
const valueLabel = legendDataItem.get("valueLabel");
if (valueLabel) {
let txt = "";
if (dataItem) {
valueLabel._setDataItem(dataItem);
txt = this.get("legendValueText", valueLabel.get("text", ""));
}
else {
valueLabel._setDataItem(this._emptyDataItem);
txt = this.get("legendRangeValueText", valueLabel.get("text", ""));
}
valueLabel.set("text", txt);
}
}
}
_getItemReaderLabel() {
let text = "X: {" + this._xField;
if (this.get("xAxis").isType("DateAxis")) {
text += ".formatDate()";
}
text += "}; Y: {" + this._yField;
if (this.get("yAxis").isType("DateAxis")) {
text += ".formatDate()";
}
text += "}";
return text;
}
/**
* @ignore
*/
getPoint(positionX, positionY) {
let x = this._xRenderer.positionToCoordinate(positionX);
let y = this._yRenderer.positionToCoordinate(positionY);
// if coordinate is super big, canvas fails to draw line, capping to some big number (won't make any visual difference)
let max = 999999999;
if (y < -max) {
y = -max;
}
else if (y > max) {
y = max;
}
if (x < -max) {
x = -max;
}
else if (x > max) {
x = max;
}
return { x: x, y: y };
}
_shouldInclude(_position) {
return true;
}
/**
* @ignore
*/
handleCursorHide() {
this.hideTooltip();
this.updateLegendValue(undefined);
this.updateLegendMarker(undefined);
}
_afterDataChange() {
super._afterDataChange();
this.get("xAxis")._markDirtyKey("start");
this.get("yAxis")._markDirtyKey("start");
this.resetExtremes();
}
/**
* Resets cached axis scale values.
*/
resetExtremes() {
this.setPrivate("selectionMinX", undefined);
this.setPrivate("selectionMaxX", undefined);
this.setPrivate("selectionMinY", undefined);
this.setPrivate("selectionMaxY", undefined);
this.setPrivate("minX", undefined);
this.setPrivate("minY", undefined);
this.setPrivate("maxX", undefined);
this.setPrivate("maxY", undefined);
}
/**
* Creates and returns an axis range object.
*
* @see {@link https://www.amcharts.com/docs/v5/charts/xy-chart/axes/axis-ranges/} for more info
* @param axisDataItem Axis data item
* @return Axis range
*/
createAxisRange(axisDataItem) {
return this.axisRanges.push({
axisDataItem: axisDataItem
});
}
/**
* A list of series's main (ungrouped) data items.
*
* @return Data items
*/
get mainDataItems() {
return this._mainDataItems;
}
/**
* @ignore
*/
_adjustStartIndex(index) {
const xAxis = this.get("xAxis");
const baseAxis = this.get("baseAxis");
if (baseAxis == xAxis && xAxis.isType("DateAxis")) {
const baseDuration = xAxis.baseDuration();
const minSelection = xAxis.getPrivate("selectionMin", xAxis.getPrivate("min", 0));
const dl = baseDuration * this.get("locationX", 0.5);
let value = -Infinity;
while (value < minSelection) {
const dataItem = this.dataItems[index];
if (dataItem) {
const open = dataItem.open;
if (open) {
value = open["valueX"];
}
else {
value = dataItem.get("valueX", 0);
}
value += dl;
if (value < minSelection) {
index++;
}
else {
break;
}
}
else {
break;
}
}
}
return index;
}
}
XYSeries.className = "XYSeries";
XYSeries.classNames = Series.classNames.concat([XYSeries.className]);
//# sourceMappingURL=XYSeries.js.map