
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import { DateAxis } from "./DateAxis";
import { DataItem } from "../../../core/render/Component";
import * as $array from "../../../core/util/Array";
import * as $order from "../../../core/util/Order";
import * as $time from "../../../core/util/Time";
import * as $type from "../../../core/util/Type";
import * as $math from "../../../core/util/Math";
/**
* A version of a [[DateAxis]] which removes intervals that don't have any data
* items in them.
*
* @see {@link https://www.amcharts.com/docs/v5/charts/xy-chart/axes/gapless-date-axis/} for more info
* @important
*/
export class GaplessDateAxis extends DateAxis {
constructor() {
super(...arguments);
Object.defineProperty(this, "_frequency", {
enumerable: true,
configurable: true,
writable: true,
value: 1
});
Object.defineProperty(this, "_m", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
Object.defineProperty(this, "_dates", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "_customDates", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
_afterNew() {
this.valueFields.push("date");
super._afterNew();
}
_getDates() {
if (this._customDates) {
return this._customDates;
}
return this._dates;
}
_updateDates(date, series) {
if (!series.get("ignoreMinMax")) {
const dates = this._getDates();
const result = $array.getSortedIndex(dates, (x) => $order.compare(x, date));
if (!result.found) {
$array.insertIndex(dates, result.index, date);
}
}
}
_updateAllDates() {
if (!this._customDates) {
const dates = this._dates;
dates.length = 0;
$array.each(this.series, (series) => {
let field = "valueX";
if (series.get("yAxis") == this) {
field = "valueY";
}
$array.each(series.dataItems, (dataItem) => {
let value = dataItem.get(field);
if ($type.isNumber(value)) {
if (dataItem.open) {
this._updateDates(dataItem.open[field], series);
}
}
});
});
const extraMax = this.get("extraMax", 0);
const extraMin = this.get("extraMin", 0);
let len = dates.length;
const baseInterval = this.getPrivate("baseInterval");
const baseCount = baseInterval.count;
const timeUnit = baseInterval.timeUnit;
if (extraMax > 0) {
const extra = len * extraMax;
let time = dates[len - 1];
if ($type.isNumber(time)) {
for (let i = len - 1; i < len + extra; i++) {
time += $time.getDuration(timeUnit, baseCount * this._getM(timeUnit));
//time = $time.round(new Date(time), timeUnit, baseInterval.count, firstDay, utc, undefined, timezone).getTime();
time = $time.roun(time, timeUnit, baseCount, this._root);
dates.push(time);
}
}
}
if (extraMin > 0) {
const extra = len * extraMin;
let time = dates[0];
if ($type.isNumber(time)) {
for (let i = 0; i < extra; i++) {
time -= $time.getDuration(timeUnit, baseCount);
//time = $time.round(new Date(time), timeUnit, baseCount, firstDay, utc, undefined, timezone).getTime();
time = $time.roun(time, timeUnit, baseCount, this._root);
dates.unshift(time);
}
}
}
}
}
/**
* Convers value to a relative position on axis.
*
* @param value Value
* @return Relative position
*/
valueToPosition(value) {
const dates = this._getDates();
const startLocation = this.get("startLocation", 0);
const endLocation = this.get("endLocation", 1);
const len = dates.length - startLocation - (1 - endLocation);
const result = $array.getSortedIndex(dates, (x) => $order.compare(x, value));
let index = result.index;
if (result.found) {
return (index - startLocation) / len;
}
else {
if (index > 0) {
index -= 1;
}
let itemValue = dates[index];
let d = 0;
if (itemValue > value && value > this.getPrivate("min", 0)) {
d = itemValue - value;
}
else {
d = value - itemValue;
}
return (index - startLocation) / len + d / this.baseDuration() / len;
}
}
/**
* Converts numeric value from axis scale to index.
*
* @param value Value
* @return Index
*/
valueToIndex(value) {
const dates = this._getDates();
const result = $array.getSortedIndex(dates, (x) => $order.compare(x, value));
let index = result.index;
if (result.found) {
return index;
}
else {
if (index > 0) {
index -= 1;
}
return index;
}
}
/**
* Converts a relative position to a corresponding numeric value from axis
* scale.
*
* @param position Relative position
* @return Value
*/
positionToValue(position) {
const startLocation = this.get("startLocation", 0);
const endLocation = this.get("endLocation", 1);
const dates = this._getDates();
let len = Math.round(dates.length - startLocation - (1 - endLocation));
let index = position * len;
let findex = Math.floor(index);
if (findex < 0) {
findex = 0;
}
if (findex > len - 1) {
findex = len - 1;
}
return dates[findex] + (index - findex + startLocation) * this.baseDuration();
}
_fixZoomFactor() {
this.setPrivateRaw("maxZoomFactor", this._getDates().length - this.get("startLocation", 0) - (1 - this.get("endLocation", 1)));
}
/**
* Zooms the axis to specific `start` and `end` dates.
*
* Optional `duration` specifies duration of zoom animation in milliseconds.
*
* @param start Start Date
* @param end End Date
* @param duration Duration in milliseconds
*/
zoomToDates(start, end, duration) {
const dates = this._getDates();
const len = dates.length;
let result = $array.getSortedIndex(dates, (x) => $order.compare(x, start.getTime()));
let startValue = dates[Math.min(result.index, len - 1)];
result = $array.getSortedIndex(dates, (x) => $order.compare(x, end.getTime()));
let endValue = dates[result.index];
if (result.index >= len) {
endValue = dates[len - 1] + this.baseDuration();
}
this.zoomToValues(startValue, endValue, duration);
}
/**
* Zooms the axis to specific `start` and `end` values.
*
* Optional `duration` specifies duration of zoom animation in milliseconds.
*
* @param start Start value
* @param end End value
* @param duration Duration in milliseconds
*/
zoomToValues(start, end, duration) {
const min = this.getPrivate("min", 0);
const max = this.getPrivate("max", 0);
start = $math.fitToRange(start, min, max);
end = $math.fitToRange(end, min, max);
this.zoom(this.valueToPosition(start), this.valueToPosition(end), duration);
}
_prepareAxisItems() {
let startTime = this.getPrivate("selectionMin", 0);
let endTime = this.getPrivate("selectionMax", 0);
if ($type.isNumber(startTime) && $type.isNumber(endTime)) {
if (this._seriesValuesDirty) {
this._seriesValuesDirty = false;
this._updateAllDates();
}
const root = this._root;
const utc = root.utc;
const timezone = root.timezone;
const dates = this._getDates();
const renderer = this.get("renderer");
const len = dates.length;
const baseDuration = this.baseDuration();
let startIndex = this.valueToIndex(startTime);
if (startIndex > 0) {
startIndex--;
}
let endIndex = this.valueToIndex(endTime);
if (endIndex < len - 1) {
endIndex++;
}
let maxCount = renderer.axisLength() / Math.max(renderer.get("minGridDistance"), 1 / Number.MAX_SAFE_INTEGER);
let frequency = Math.min(len, Math.ceil((endIndex - startIndex) / maxCount));
frequency = Math.max(1, frequency);
startIndex = Math.floor(startIndex / frequency) * frequency;
this._frequency = frequency;
$array.each(this.dataItems, (dataItem) => {
this._toggleDataItem(dataItem, false);
});
$array.each(this.minorDataItems, (dataItem) => {
this._toggleDataItem(dataItem, false);
});
let realDuration = (endTime - startTime) - ((endTime - startTime) / baseDuration - (endIndex - startIndex)) * baseDuration;
// if all items are on axis
let gridInterval = $time.chooseInterval(0, realDuration, maxCount, this.get("gridIntervals"));
const baseInterval = this.getPrivate("baseInterval");
let intervalDuration = $time.getIntervalDuration(gridInterval);
if (intervalDuration < baseDuration) {
gridInterval = Object.assign({}, baseInterval);
intervalDuration = $time.getIntervalDuration(gridInterval);
}
this._intervalDuration = intervalDuration;
const timeUnit = gridInterval.timeUnit;
const formats = this.get("dateFormats");
let firstTime = Date.now();
if (dates[0]) {
firstTime = dates[0];
}
//let value = $time.round(new Date(this.getPrivate("selectionMin", 0)), timeUnit, gridInterval.count, firstDay, utc, firstDate, timezone).getTime();
let value = $time.roun(this.getPrivate("selectionMin", 0), timeUnit, gridInterval.count, root, firstTime);
const minorLabelsEnabled = renderer.get("minorLabelsEnabled");
const minorGridEnabled = renderer.get("minorGridEnabled", minorLabelsEnabled);
let minorGridInterval;
let minorDuration = 0;
let previousDataItem;
if (minorGridEnabled) {
minorGridInterval = this._getMinorInterval(gridInterval);
minorDuration = $time.getIntervalDuration(minorGridInterval);
}
let selectedItems = this._getIndexes(value, this.getPrivate("selectionMax", value) + intervalDuration, gridInterval, this.getPrivate("min", value));
if (selectedItems.length > 0) {
let i = 0;
this._m = 0;
let previousValue = value - intervalDuration * 10;
const nextGridUnit = $time.getNextUnit(timeUnit);
// MINOR GRID
if (minorGridInterval) {
let first = dates[selectedItems[0]];
this._addMinorGrid(first - intervalDuration, first, minorDuration, minorGridInterval);
}
let minDistance = renderer.axisLength() / renderer.gridCount() * 0.5;
$array.each(selectedItems, (index) => {
var _a;
let dataItem;
if (this.dataItems.length < i + 1) {
dataItem = new DataItem(this, undefined, {});
this._dataItems.push(dataItem);
this.processDataItem(dataItem);
}
else {
dataItem = this.dataItems[i];
}
let value = dates[index];
let date = new Date(value);
let endValue = value;
if (i < selectedItems.length - 1) {
endValue = dates[selectedItems[i + 1]];
}
else {
endValue += intervalDuration;
}
dataItem.setRaw("value", value);
dataItem.setRaw("endValue", endValue);
dataItem.setRaw("index", i);
dataItem.setRaw("labelEndValue", undefined);
let format = formats[timeUnit];
if (nextGridUnit && this.get("markUnitChange") && $type.isNumber(previousValue)) {
if (timeUnit != "year") {
if ($time.checkChange(value, previousValue, nextGridUnit, utc, timezone)) {
format = this.get("periodChangeDateFormats")[timeUnit];
}
}
}
this._createAssets(dataItem, []);
const label = dataItem.get("label");
if (label) {
label.set("text", root.dateFormatter.format(date, format));
}
this._toggleDataItem(dataItem, true);
let count = gridInterval.count;
// so that labels of week would always be at the beginning of the grid
if (timeUnit == "week") {
dataItem.setRaw("labelEndValue", value);
}
if (minorGridEnabled) {
let timeUnit2 = gridInterval.timeUnit;
if (timeUnit2 == "week") {
timeUnit2 = "day";
}
if (count > 1 || gridInterval.timeUnit == "week") {
//let labelEndValue = $time.round(new Date(value), timeUnit2, 1, firstDay, utc, undefined, timezone).getTime() + $time.getDuration(timeUnit2, this._getM(timeUnit2));
let labelEndValue = $time.roun(value, timeUnit2, 1, root) + $time.getDuration(timeUnit2, this._getM(timeUnit2));
let index = this.valueToIndex(labelEndValue);
labelEndValue = dates[index];
if (labelEndValue == value) {
let next = dates[index + 1];
if (next) {
labelEndValue = next;
}
else {
labelEndValue += minorDuration;
}
}
dataItem.setRaw("labelEndValue", labelEndValue);
}
count = 1;
}
this._prepareDataItem(dataItem, count);
if (label && previousDataItem) {
if (renderer.getPrivate("letter") == "X") {
let previousLabel = previousDataItem.get("label");
if (previousLabel) {
let x = label.x();
let previousX = previousLabel.x();
if (x - previousX < minDistance) {
let worse = this._pickWorse(previousDataItem, dataItem, gridInterval);
if (worse) {
(_a = worse.get("label")) === null || _a === void 0 ? void 0 : _a.setPrivate("visible", false);
}
}
}
}
// todo y?
}
// MINOR GRID
if (minorGridInterval) {
this._addMinorGrid(value, endValue, minorDuration, minorGridInterval);
}
i++;
if (label && label.getPrivate("visible")) {
previousDataItem = dataItem;
}
previousValue = value;
});
}
$array.each(this.series, (series) => {
if (series.inited) {
series._markDirtyAxes();
}
});
}
this._updateGhost();
}
_pickWorse(dataItemA, dataItemB, interval) {
const timeUnit = interval.timeUnit;
const valueA = dataItemA.get("value", 0);
const valueB = dataItemB.get("value", 0);
if (timeUnit == "hour") {
if (new Date(valueA).getDate() != new Date(valueB).getDate()) {
return dataItemA;
}
}
return dataItemB;
}
_addMinorGrid(startValue, endValue, minorDuration, gridInterval) {
const minorFormats = this.get("minorDateFormats", this.get("dateFormats"));
const mTimeUnit = gridInterval.timeUnit;
let value = startValue + $time.getDuration(mTimeUnit, this._getM(mTimeUnit));
//value = $time.round(new Date(value), mTimeUnit, 1, firstDay, utc, undefined, timezone).getTime();
value = $time.roun(value, mTimeUnit, 1, this._root);
let maxValue = endValue - minorDuration * 0.5;
let minorSelectedItems = this._getIndexes(value, maxValue, gridInterval, value);
const dates = this._getDates();
$array.each(minorSelectedItems, (index) => {
let minorDataItem;
if (this.minorDataItems.length < this._m + 1) {
minorDataItem = new DataItem(this, undefined, {});
this.minorDataItems.push(minorDataItem);
this.processDataItem(minorDataItem);
}
else {
minorDataItem = this.minorDataItems[this._m];
}
value = dates[index];
minorDataItem.setRaw("value", value);
minorDataItem.setRaw("endValue", value + minorDuration);
minorDataItem.setRaw("index", index);
this._createAssets(minorDataItem, ["minor"], true);
const label = minorDataItem.get("label");
if (label) {
if (this.get("renderer").get("minorLabelsEnabled")) {
let date = new Date(value);
let format = minorFormats[mTimeUnit];
label.set("text", this._root.dateFormatter.format(date, format));
}
else {
label.setPrivate("visible", false);
}
}
this._toggleDataItem(minorDataItem, true);
this._prepareDataItem(minorDataItem, 1);
this._m++;
});
}
_getIndexes(value, maxValue, interval, firstValue) {
const items = [];
const timeUnit = interval.timeUnit;
const count = interval.count;
const mmm = this._getM(timeUnit);
const baseInterval = this.getPrivate("baseInterval");
const root = this._root;
const dates = this._getDates();
let c = count - 1;
let previousValue = -Infinity;
let duration = $time.getDuration(timeUnit, mmm);
let fullDuration = $time.getDuration(timeUnit, count * mmm);
let originalValue = value;
if (timeUnit == "day") {
value = firstValue;
}
while (value <= maxValue) {
//value = $time.round(new Date(value), timeUnit, count, firstDay, utc, undefined, timezone).getTime();
value = $time.roun(value, timeUnit, count, root);
let index = this.valueToIndex(value);
let realValue = dates[index];
if (timeUnit == "day" && baseInterval.timeUnit == "day") {
if (this._hasDate(value)) {
c++;
}
if (c == count) {
if (value >= originalValue - fullDuration * 2) {
$array.move(items, index);
}
c = 0;
}
value += duration;
//value = $time.round(new Date(value), timeUnit, 1, firstDay, utc, undefined, timezone).getTime();
value = $time.roun(value, timeUnit, 1, root);
}
else {
if (realValue < value) {
for (let i = index, len = dates.length; i < len; i++) {
realValue = dates[i];
if (realValue >= value) {
index = i;
break;
}
}
}
$array.move(items, index);
value += fullDuration;
//value = $time.round(new Date(value), timeUnit, count, firstDay, utc, undefined, timezone).getTime();
value = $time.roun(value, timeUnit, count, root);
}
if (value == previousValue) {
value += fullDuration + duration;
//value = $time.round(new Date(value), timeUnit, count, firstDay, utc, undefined, timezone).getTime();
value = $time.roun(value, timeUnit, count, root);
}
if (value == previousValue) {
break;
}
previousValue = value;
}
return items;
}
_hasDate(time) {
const result = $array.getSortedIndex(this._getDates(), (date) => {
return $order.compareNumber(date, time);
});
return result.found;
}
}
Object.defineProperty(GaplessDateAxis, "className", {
enumerable: true,
configurable: true,
writable: true,
value: "GaplessDateAxis"
});
Object.defineProperty(GaplessDateAxis, "classNames", {
enumerable: true,
configurable: true,
writable: true,
value: DateAxis.classNames.concat([GaplessDateAxis.className])
});
//# sourceMappingURL=GaplessDateAxis.js.map