760 lines
26 KiB
JavaScript
760 lines
26 KiB
JavaScript
/**
|
|
* @license Highcharts JS v7.0.0 (2018-12-11)
|
|
* Timeline series
|
|
*
|
|
* (c) 2010-2018 Highsoft AS
|
|
* Author: Daniel Studencki
|
|
*
|
|
* License: www.highcharts.com/license
|
|
*/
|
|
'use strict';
|
|
(function (factory) {
|
|
if (typeof module === 'object' && module.exports) {
|
|
module.exports = factory;
|
|
} else if (typeof define === 'function' && define.amd) {
|
|
define(function () {
|
|
return factory;
|
|
});
|
|
} else {
|
|
factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
|
|
}
|
|
}(function (Highcharts) {
|
|
(function (H) {
|
|
/* *
|
|
*
|
|
* Experimental Timeline Series.
|
|
* Note: This API is in alpha stage and will be changed before final release.
|
|
*
|
|
* (c) 2010-2018 Highsoft AS
|
|
*
|
|
* Author: Daniel Studencki
|
|
*
|
|
* License: www.highcharts.com/license
|
|
*
|
|
* */
|
|
|
|
|
|
|
|
var addEvent = H.addEvent,
|
|
extend = H.extend,
|
|
defined = H.defined,
|
|
LegendSymbolMixin = H.LegendSymbolMixin,
|
|
TrackerMixin = H.TrackerMixin,
|
|
merge = H.merge,
|
|
pick = H.pick,
|
|
Point = H.Point,
|
|
Series = H.Series,
|
|
undocumentedSeriesType = H.seriesType,
|
|
wrap = H.wrap;
|
|
|
|
/* *
|
|
* The timeline series type.
|
|
*
|
|
* @private
|
|
* @class
|
|
* @name Highcharts.seriesTypes.timeline
|
|
*
|
|
* @augments Highcharts.Series
|
|
*/
|
|
undocumentedSeriesType('timeline', 'line'
|
|
|
|
/* *
|
|
* The timeline series presents given events along a drawn line.
|
|
*
|
|
* @sample highcharts/series-timeline/alternate-labels Timeline series
|
|
*
|
|
* @extends plotOptions.line
|
|
* @since 7.0.0
|
|
* @product highcharts
|
|
* @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
|
|
* cropThreshold, dashStyle, findNearestPointBy,
|
|
* getExtremesFromAll, lineWidth, negativeColor, pointInterval,
|
|
* pointIntervalUnit, pointPlacement, pointStart, softThreshold,
|
|
* stacking, step, threshold, turboThreshold, zoneAxis, zones
|
|
* @optionparent plotOptions.timeline
|
|
*/
|
|
, {
|
|
colorByPoint: true,
|
|
stickyTracking: false,
|
|
ignoreHiddenPoint: true,
|
|
legendType: 'point',
|
|
lineWidth: 0,
|
|
tooltip: {
|
|
headerFormat: '<span style="color:{point.color}">● </span>' +
|
|
'<span style="font-weight: bold;">{point.point.date}</span><br/>',
|
|
pointFormat: '{point.description}'
|
|
},
|
|
states: {
|
|
hover: {
|
|
lineWidthPlus: 5,
|
|
halo: {
|
|
size: 0
|
|
}
|
|
}
|
|
},
|
|
dataLabels: {
|
|
enabled: true,
|
|
allowOverlap: true,
|
|
/* *
|
|
* The width of the line connecting the data label to the point.
|
|
*
|
|
*
|
|
* In styled mode, the connector stroke width is given in the
|
|
* `.highcharts-data-label-connector` class.
|
|
*
|
|
* @type {Number}
|
|
* @default 1
|
|
* @sample {highcharts} highcharts/series-timeline/connector-styles
|
|
* Custom connector width and color
|
|
*/
|
|
connectorWidth: 1,
|
|
/* *
|
|
* The color of the line connecting the data label to the point.
|
|
*
|
|
* In styled mode, the connector stroke is given in the
|
|
* `.highcharts-data-label-connector` class.
|
|
*
|
|
* @type {String}
|
|
* @sample {highcharts} highcharts/series-timeline/connector-styles
|
|
* Custom connector width and color
|
|
*/
|
|
connectorColor: '#000000',
|
|
backgroundColor: '#ffffff',
|
|
/* *
|
|
* @type {Highcharts.FormatterCallbackFunction<object>}
|
|
* @default function () {
|
|
* var format;
|
|
*
|
|
* if (!this.series.chart.styledMode) {
|
|
* format = '<span style="color:' + this.point.color +
|
|
* '">● </span><span style="font-weight: bold;" > ' +
|
|
* (this.point.date || '') + '</span><br/>' +
|
|
* (this.point.label || '');
|
|
* } else {
|
|
* format = '<span>● </span>' +
|
|
* '<span>' + (this.point.date || '') +
|
|
* '</span><br/>' + (this.point.label || '');
|
|
* }
|
|
* return format;
|
|
* }
|
|
* @apioption plotOptions.timeline.dataLabels.formatter
|
|
*/
|
|
formatter: function () {
|
|
var format;
|
|
|
|
if (!this.series.chart.styledMode) {
|
|
format = '<span style="color:' + this.point.color +
|
|
'">● </span><span style="font-weight: bold;" > ' +
|
|
(this.point.date || '') + '</span><br/>' +
|
|
(this.point.label || '');
|
|
} else {
|
|
format = '<span>● </span>' +
|
|
'<span>' + (this.point.date || '') +
|
|
'</span><br/>' + (this.point.label || '');
|
|
}
|
|
return format;
|
|
},
|
|
borderWidth: 1,
|
|
borderColor: '#666666',
|
|
/* *
|
|
* A pixel value defining the distance between the data label
|
|
* and the point. Negative numbers puts the label on top
|
|
* of the point.
|
|
*
|
|
* @type {Number}
|
|
* @default 100
|
|
*/
|
|
distance: 100,
|
|
/* *
|
|
* Whether to position data labels alternately. For example, if
|
|
* [distance](#plotOptions.timeline.dataLabels.distance) is set
|
|
* equal to `100`, then the first data label 's distance will be
|
|
* set equal to `100`, the second one equal to `-100`, and so on.
|
|
*
|
|
* @type {Boolean}
|
|
* @default true
|
|
* @sample {highcharts} highcharts/series-timeline/alternate-disabled
|
|
* Alternate disabled
|
|
*/
|
|
alternate: true,
|
|
verticalAlign: 'middle',
|
|
color: '#333333'
|
|
},
|
|
marker: {
|
|
enabledThreshold: 0,
|
|
symbol: 'square',
|
|
height: 15
|
|
}
|
|
}
|
|
/* *
|
|
* @lends Highcharts.Series#
|
|
*/
|
|
, {
|
|
requireSorting: false,
|
|
trackerGroups: ['markerGroup', 'dataLabelsGroup'],
|
|
// Use a simple symbol from LegendSymbolMixin
|
|
drawLegendSymbol: LegendSymbolMixin.drawRectangle,
|
|
// Use a group of trackers from TrackerMixin
|
|
drawTracker: TrackerMixin.drawTrackerPoint,
|
|
init: function () {
|
|
var series = this;
|
|
|
|
Series.prototype.init.apply(series, arguments);
|
|
|
|
// Distribute data labels before rendering them. Distribution is based
|
|
// on the 'dataLabels.distance' and 'dataLabels.alternate' property.
|
|
addEvent(series, 'drawDataLabels', function () {
|
|
// Delete the oldTextWidth parameter, in order to force adjusting
|
|
// data label wrapper box width. It's needed only when useHTML
|
|
// is enabled. This prevents the data label text getting out
|
|
// of the box range.
|
|
if (series.options.dataLabels.useHTML) {
|
|
series.points.forEach(function (p) {
|
|
if (p.visible && p.dataLabel) {
|
|
delete p.dataLabel.text.oldTextWidth;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Distribute data labels basing on defined algorithm.
|
|
series.distributeDL();
|
|
});
|
|
|
|
addEvent(series, 'afterDrawDataLabels', function () {
|
|
var seriesOptions = series.options,
|
|
options = seriesOptions.dataLabels,
|
|
hasRendered = series.hasRendered || 0,
|
|
defer = pick(options.defer, !!seriesOptions.animation),
|
|
connectorsGroup = series.connectorsGroup,
|
|
dataLabel;
|
|
|
|
// Create (or redraw) the group for all connectors.
|
|
connectorsGroup = series.plotGroup(
|
|
'connectorsGroup',
|
|
'data-labels-connectors',
|
|
defer && !hasRendered ? 'hidden' : 'visible',
|
|
options.zIndex || 5
|
|
);
|
|
|
|
// Draw or align connector for each point.
|
|
series.points.forEach(function (point) {
|
|
dataLabel = point.dataLabel;
|
|
|
|
if (dataLabel) {
|
|
// Within this wrap method is necessary to save the current
|
|
// animation params, because the data label target position
|
|
// (after animation) is needed to align connectors.
|
|
wrap(dataLabel, 'animate', function (proceed, params) {
|
|
if (this.targetPosition) {
|
|
this.targetPosition = params;
|
|
}
|
|
return proceed.apply(
|
|
this,
|
|
Array.prototype.slice.call(arguments, 1)
|
|
);
|
|
});
|
|
|
|
// Initiate the targetPosition field within data label
|
|
// object. It's necessary because there is need to know
|
|
// expected position of specific data label, when aligning
|
|
// connectors. This field is overrided inside of
|
|
// SVGElement.animate() wrapped method.
|
|
if (!dataLabel.targetPosition) {
|
|
dataLabel.targetPosition = {};
|
|
}
|
|
|
|
return !point.connector ?
|
|
point.drawConnector() :
|
|
point.alignConnector();
|
|
}
|
|
});
|
|
// Animate connectors group. It's animated in the same way like
|
|
// dataLabels, and also depends on dataLabels.defer parameter.
|
|
if (defer) {
|
|
connectorsGroup.attr({
|
|
opacity: +hasRendered
|
|
});
|
|
if (!hasRendered) {
|
|
addEvent(series, 'afterAnimate', function () {
|
|
if (series.visible) {
|
|
connectorsGroup.show(true);
|
|
}
|
|
connectorsGroup[
|
|
seriesOptions.animation ? 'animate' : 'attr'
|
|
]({
|
|
opacity: 1
|
|
}, {
|
|
duration: 200
|
|
});
|
|
});
|
|
}
|
|
}
|
|
});
|
|
},
|
|
alignDataLabel: function (point, dataLabel) {
|
|
var series = this,
|
|
isInverted = series.chart.inverted,
|
|
visiblePoints = series.visibilityMap.filter(function (point) {
|
|
return point;
|
|
}),
|
|
visiblePointsCount = series.visiblePointsCount,
|
|
pointIndex = visiblePoints.indexOf(point),
|
|
isFirstOrLast = !pointIndex ||
|
|
pointIndex === visiblePointsCount - 1,
|
|
dataLabelsOptions = series.options.dataLabels,
|
|
userDLOptions = point.userDLOptions || {},
|
|
// Define multiplier which is used to calculate data label width.
|
|
// If data labels are alternate, they have two times more space to
|
|
// adapt (excepting first and last ones, which has only one
|
|
// and half), than in case of placing all data labels side by side.
|
|
multiplier = dataLabelsOptions.alternate ?
|
|
(isFirstOrLast ? 1.5 : 2) :
|
|
1,
|
|
distance,
|
|
availableSpace = Math.floor(series.xAxis.len / visiblePointsCount),
|
|
pad = dataLabel.padding,
|
|
targetDLWidth,
|
|
styles;
|
|
|
|
// Adjust data label width to the currently available space.
|
|
if (point.visible) {
|
|
distance = Math.abs(userDLOptions.x || point.options.dataLabels.x);
|
|
if (isInverted) {
|
|
targetDLWidth = (distance - pad) * 2 - (point.itemHeight / 2);
|
|
styles = {
|
|
width: targetDLWidth,
|
|
// Apply ellipsis when data label height is exceeded.
|
|
textOverflow: dataLabel.width / targetDLWidth *
|
|
dataLabel.height / 2 > availableSpace * multiplier ?
|
|
'ellipsis' : 'none'
|
|
};
|
|
} else {
|
|
styles = {
|
|
width: userDLOptions.width ||
|
|
dataLabelsOptions.width ||
|
|
availableSpace * multiplier - (pad * 2)
|
|
};
|
|
}
|
|
dataLabel.css(styles);
|
|
|
|
if (!series.chart.styledMode) {
|
|
dataLabel.shadow({});
|
|
}
|
|
}
|
|
Series.prototype.alignDataLabel.apply(series, arguments);
|
|
},
|
|
processData: function () {
|
|
var series = this,
|
|
xMap = [],
|
|
base,
|
|
visiblePoints = 0,
|
|
i;
|
|
|
|
series.visibilityMap = series.getVisibilityMap();
|
|
|
|
// Calculate currently visible points.
|
|
series.visibilityMap.forEach(function (point) {
|
|
if (point) {
|
|
visiblePoints++;
|
|
}
|
|
});
|
|
|
|
series.visiblePointsCount = visiblePoints;
|
|
base = series.xAxis.options.max / visiblePoints;
|
|
|
|
// Generate xData map.
|
|
for (i = 1; i <= visiblePoints; i++) {
|
|
xMap.push(
|
|
(base * i) - (base / 2)
|
|
);
|
|
}
|
|
|
|
// Set all hidden points y values as negatives, in order to move them
|
|
// away from plot area. It is necessary to avoid hiding data labels,
|
|
// when dataLabels.allowOverlap is set to false.
|
|
series.visibilityMap.forEach(function (vis, i) {
|
|
if (!vis) {
|
|
xMap.splice(i, 0, series.yData[i] === null ? null : -99);
|
|
}
|
|
});
|
|
|
|
series.xData = xMap;
|
|
series.yData = xMap.map(function (data) {
|
|
return defined(data) ? 1 : null;
|
|
});
|
|
|
|
Series.prototype.processData.call(this, arguments);
|
|
},
|
|
generatePoints: function () {
|
|
var series = this;
|
|
|
|
Series.prototype.generatePoints.apply(series);
|
|
series.points.forEach(function (point, i) {
|
|
point.applyOptions({
|
|
x: series.xData[i]
|
|
});
|
|
});
|
|
},
|
|
getVisibilityMap: function () {
|
|
var series = this,
|
|
map = (series.data.length ?
|
|
series.data : series.userOptions.data
|
|
).map(function (point) {
|
|
return (
|
|
point &&
|
|
point.visible !== false &&
|
|
!point.isNull
|
|
) ? point : false;
|
|
});
|
|
|
|
return map;
|
|
},
|
|
distributeDL: function () {
|
|
var series = this,
|
|
dataLabelsOptions = series.options.dataLabels,
|
|
options,
|
|
pointDLOptions,
|
|
newOptions = {},
|
|
visibilityIndex = 1,
|
|
distance = dataLabelsOptions.distance;
|
|
|
|
series.points.forEach(function (point) {
|
|
if (point.visible && !point.isNull) {
|
|
options = point.options;
|
|
pointDLOptions = point.options.dataLabels;
|
|
|
|
if (!series.hasRendered) {
|
|
point.userDLOptions = merge({}, pointDLOptions);
|
|
}
|
|
|
|
newOptions[series.chart.inverted ? 'x' : 'y'] =
|
|
dataLabelsOptions.alternate && visibilityIndex % 2 ?
|
|
-distance : distance;
|
|
|
|
options.dataLabels = merge(newOptions, point.userDLOptions);
|
|
visibilityIndex++;
|
|
}
|
|
});
|
|
},
|
|
markerAttribs: function (point, state) {
|
|
var series = this,
|
|
seriesMarkerOptions = series.options.marker,
|
|
seriesStateOptions,
|
|
pointMarkerOptions = point.marker || {},
|
|
symbol = pointMarkerOptions.symbol || seriesMarkerOptions.symbol,
|
|
pointStateOptions,
|
|
width = pick(
|
|
pointMarkerOptions.width,
|
|
seriesMarkerOptions.width,
|
|
series.xAxis.len / series.visiblePointsCount
|
|
),
|
|
height = pick(
|
|
pointMarkerOptions.height,
|
|
seriesMarkerOptions.height
|
|
),
|
|
radius = 0,
|
|
attribs;
|
|
|
|
// Handle hover and select states
|
|
if (state) {
|
|
seriesStateOptions = seriesMarkerOptions.states[state] || {};
|
|
pointStateOptions = pointMarkerOptions.states &&
|
|
pointMarkerOptions.states[state] || {};
|
|
|
|
radius = pick(
|
|
pointStateOptions.radius,
|
|
seriesStateOptions.radius,
|
|
radius + (
|
|
seriesStateOptions.radiusPlus ||
|
|
0
|
|
)
|
|
);
|
|
}
|
|
|
|
point.hasImage = symbol && symbol.indexOf('url') === 0;
|
|
|
|
attribs = {
|
|
x: Math.floor(point.plotX) - (width / 2) - (radius / 2),
|
|
y: point.plotY - (height / 2) - (radius / 2),
|
|
width: width + radius,
|
|
height: height + radius
|
|
};
|
|
|
|
return attribs;
|
|
|
|
},
|
|
bindAxes: function () {
|
|
var series = this,
|
|
timelineXAxis = {
|
|
gridLineWidth: 0,
|
|
lineWidth: 0,
|
|
min: 0,
|
|
dataMin: 0,
|
|
minPadding: 0,
|
|
max: 100,
|
|
dataMax: 100,
|
|
maxPadding: 0,
|
|
title: null,
|
|
tickPositions: []
|
|
},
|
|
timelineYAxis = {
|
|
gridLineWidth: 0,
|
|
min: 0.5,
|
|
dataMin: 0.5,
|
|
minPadding: 0,
|
|
max: 1.5,
|
|
dataMax: 1.5,
|
|
maxPadding: 0,
|
|
title: null,
|
|
labels: {
|
|
enabled: false
|
|
}
|
|
};
|
|
Series.prototype.bindAxes.call(series);
|
|
extend(series.xAxis.options, timelineXAxis);
|
|
extend(series.yAxis.options, timelineYAxis);
|
|
}
|
|
}
|
|
/* *
|
|
* @lends Highcharts.Point#
|
|
*/
|
|
, {
|
|
init: function () {
|
|
var point = Point.prototype.init.apply(this, arguments);
|
|
point.name = pick(point.name, point.date, 'Event');
|
|
point.y = 1;
|
|
|
|
return point;
|
|
},
|
|
// The setVisible method is taken from Pie series prototype, in order to
|
|
// prevent importing whole Pie series.
|
|
setVisible: function (vis, redraw) {
|
|
var point = this,
|
|
series = point.series,
|
|
chart = series.chart,
|
|
ignoreHiddenPoint = series.options.ignoreHiddenPoint;
|
|
|
|
redraw = pick(redraw, ignoreHiddenPoint);
|
|
|
|
if (vis !== point.visible) {
|
|
|
|
// If called without an argument, toggle visibility
|
|
point.visible = point.options.visible = vis =
|
|
vis === undefined ? !point.visible : vis;
|
|
// update userOptions.data
|
|
series.options.data[series.data.indexOf(point)] = point.options;
|
|
|
|
// Show and hide associated elements. This is performed regardless
|
|
// of redraw or not, because chart.redraw only handles full series.
|
|
['graphic', 'dataLabel', 'connector'].forEach(
|
|
function (key) {
|
|
if (point[key]) {
|
|
point[key][vis ? 'show' : 'hide'](true);
|
|
}
|
|
}
|
|
);
|
|
|
|
if (point.legendItem) {
|
|
chart.legend.colorizeItem(point, vis);
|
|
}
|
|
|
|
// #4170, hide halo after hiding point
|
|
if (!vis && point.state === 'hover') {
|
|
point.setState('');
|
|
}
|
|
|
|
// Handle ignore hidden slices
|
|
if (ignoreHiddenPoint) {
|
|
series.isDirty = true;
|
|
}
|
|
|
|
if (redraw) {
|
|
chart.redraw();
|
|
}
|
|
}
|
|
},
|
|
setState: function () {
|
|
var proceed = Series.prototype.pointClass.prototype.setState;
|
|
|
|
// Prevent triggering the setState method on null points.
|
|
if (!this.isNull) {
|
|
proceed.apply(this, arguments);
|
|
}
|
|
},
|
|
getConnectorPath: function () {
|
|
var point = this,
|
|
chart = point.series.chart,
|
|
xAxisLen = point.series.xAxis.len,
|
|
inverted = chart.inverted,
|
|
direction = inverted ? 'x2' : 'y2',
|
|
dl = point.dataLabel,
|
|
targetDLPos = dl.targetPosition,
|
|
coords = {
|
|
x1: point.plotX,
|
|
y1: point.plotY,
|
|
x2: point.plotX,
|
|
y2: targetDLPos.y || dl.y
|
|
},
|
|
negativeDistance = coords[direction] < point.series.yAxis.len / 2,
|
|
path;
|
|
|
|
// Recalculate coords when the chart is inverted.
|
|
if (inverted) {
|
|
coords = {
|
|
x1: point.plotY,
|
|
y1: xAxisLen - point.plotX,
|
|
x2: targetDLPos.x || dl.x,
|
|
y2: xAxisLen - point.plotX
|
|
};
|
|
}
|
|
|
|
// Subtract data label width or height from expected coordinate so that
|
|
// the connector would start from the appropriate edge.
|
|
if (negativeDistance) {
|
|
coords[direction] += dl[inverted ? 'width' : 'height'];
|
|
}
|
|
|
|
path = chart.renderer.crispLine([
|
|
'M',
|
|
coords.x1,
|
|
coords.y1,
|
|
'L',
|
|
coords.x2,
|
|
coords.y2
|
|
], dl.options.connectorWidth || 1);
|
|
|
|
return path;
|
|
},
|
|
drawConnector: function () {
|
|
var point = this,
|
|
series = point.series,
|
|
dlOptions = point.dataLabel.options = merge(
|
|
{}, series.options.dataLabels,
|
|
point.options.dataLabels
|
|
);
|
|
|
|
point.connector = series.chart.renderer.path(point.getConnectorPath())
|
|
.add(series.connectorsGroup);
|
|
|
|
if (!series.chart.styledMode) {
|
|
point.connector.attr({
|
|
stroke: dlOptions.connectorColor,
|
|
'stroke-width': dlOptions.connectorWidth,
|
|
opacity: point.dataLabel.opacity
|
|
});
|
|
}
|
|
},
|
|
alignConnector: function () {
|
|
var point = this,
|
|
connector = point.connector,
|
|
bBox = connector.getBBox(),
|
|
isVisible = bBox.y > 0;
|
|
|
|
connector[isVisible ? 'animate' : 'attr']({
|
|
d: point.getConnectorPath()
|
|
});
|
|
}
|
|
});
|
|
|
|
// Hide/show connector related with a specific data label, after overlapping
|
|
// detected.
|
|
addEvent(H.Chart, 'afterHideOverlappingLabels', function () {
|
|
var series = this.series,
|
|
dataLabel,
|
|
connector;
|
|
|
|
series.forEach(function (series) {
|
|
if (series.points) {
|
|
series.points.forEach(function (point) {
|
|
dataLabel = point.dataLabel;
|
|
connector = point.connector;
|
|
|
|
if (
|
|
dataLabel &&
|
|
dataLabel.targetPosition &&
|
|
connector
|
|
) {
|
|
connector.attr({
|
|
opacity: dataLabel.targetPosition.opacity ||
|
|
dataLabel.newOpacity
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
/* *
|
|
* The `timeline` series. If the [type](#series.timeline.type) option is
|
|
* not specified, it is inherited from [chart.type](#chart.type).
|
|
*
|
|
* @extends series,plotOptions.timeline
|
|
* @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
|
|
* cropThreshold, dashStyle, dataParser, dataURL, findNearestPointBy,
|
|
* getExtremesFromAll, lineWidth, negativeColor,
|
|
* pointInterval, pointIntervalUnit, pointPlacement, pointStart,
|
|
* softThreshold, stacking, stack, step, threshold, turboThreshold,
|
|
* zoneAxis, zones
|
|
* @product highcharts
|
|
* @apioption series.timeline
|
|
*/
|
|
|
|
/* *
|
|
* An array of data points for the series. For the `timeline` series type,
|
|
* points can be given with three general parameters, `date`, `label`,
|
|
* and `description`:
|
|
*
|
|
* Example:
|
|
*
|
|
* ```js
|
|
* series: [{
|
|
* type: 'timeline',
|
|
* data: [{
|
|
* date: 'Jan 2018',
|
|
* label: 'Some event label',
|
|
* description: 'Description to show in tooltip'
|
|
* }]
|
|
* }]
|
|
* ```
|
|
*
|
|
* @sample {highcharts} highcharts/series-timeline/alternate-labels
|
|
* Alternate labels
|
|
*
|
|
* @type {Array<number|*>}
|
|
* @extends series.line.data
|
|
* @excluding marker, x, y
|
|
* @product highcharts
|
|
* @apioption series.timeline.data
|
|
*/
|
|
|
|
/* *
|
|
* The date of event.
|
|
*
|
|
* @type {string}
|
|
* @product highcharts
|
|
* @apioption series.timeline.data.date
|
|
*/
|
|
|
|
/* *
|
|
* The label of event.
|
|
*
|
|
* @type {string}
|
|
* @product highcharts
|
|
* @apioption series.timeline.data.label
|
|
*/
|
|
|
|
/* *
|
|
* The description of event. This description will be shown in tooltip.
|
|
*
|
|
* @type {string}
|
|
* @product highcharts
|
|
* @apioption series.timeline.data.description
|
|
*/
|
|
|
|
}(Highcharts));
|
|
return (function () {
|
|
|
|
|
|
}());
|
|
}));
|