UNPKG

highcharts

Version:
1,318 lines (1,317 loc) 50.3 kB
/* * * * (c) 2009-2021 Highsoft, Black Label * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; import A from '../../Core/Animation/AnimationUtilities.js'; var getDeferredAnimation = A.getDeferredAnimation; import Chart from '../../Core/Chart/Chart.js'; var chartProto = Chart.prototype; import ControllableMixin from './Mixins/ControllableMixin.js'; import ControllableRect from './Controllables/ControllableRect.js'; import ControllableCircle from './Controllables/ControllableCircle.js'; import ControllablePath from './Controllables/ControllablePath.js'; import ControllableImage from './Controllables/ControllableImage.js'; import ControllableLabel from './Controllables/ControllableLabel.js'; import ControlPoint from './ControlPoint.js'; import EventEmitterMixin from './Mixins/EventEmitterMixin.js'; import H from '../../Core/Globals.js'; import MockPoint from './MockPoint.js'; import Pointer from '../../Core/Pointer.js'; import U from '../../Core/Utilities.js'; var addEvent = U.addEvent, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, erase = U.erase, extend = U.extend, find = U.find, fireEvent = U.fireEvent, merge = U.merge, pick = U.pick, splat = U.splat, wrap = U.wrap; /* ********************************************************************* * * ANNOTATION * ******************************************************************** */ /** * Possible directions for draggable annotations. An empty string (`''`) * makes the annotation undraggable. * * @typedef {''|'x'|'xy'|'y'} Highcharts.AnnotationDraggableValue */ /** * @private * @typedef { * Highcharts.AnnotationControllableCircle| * Highcharts.AnnotationControllableImage| * Highcharts.AnnotationControllablePath| * Highcharts.AnnotationControllableRect * } * Highcharts.AnnotationShapeType * @requires modules/annotations */ /** * @private * @typedef { * Highcharts.AnnotationControllableLabel * } * Highcharts.AnnotationLabelType * @requires modules/annotations */ /** * A point-like object, a mock point or a point used in series. * @private * @typedef {Highcharts.AnnotationMockPoint|Highcharts.Point} Highcharts.AnnotationPointType * @requires modules/annotations */ /* eslint-disable no-invalid-this, valid-jsdoc */ /** * An annotation class which serves as a container for items like labels or * shapes. Created items are positioned on the chart either by linking them to * existing points or created mock points * * @class * @name Highcharts.Annotation * * @param {Highcharts.Chart} chart a chart instance * @param {Highcharts.AnnotationsOptions} userOptions the options object */ var Annotation = /** @class */ (function () { /* * * * Constructors * * */ function Annotation(chart, userOptions) { /* * * * Properties * * */ this.annotation = void 0; this.coll = 'annotations'; this.collection = void 0; this.animationConfig = void 0; this.graphic = void 0; this.group = void 0; this.labelCollector = void 0; this.labelsGroup = void 0; this.shapesGroup = void 0; var labelsAndShapes; /** * The chart that the annotation belongs to. * * @type {Highcharts.Chart} */ this.chart = chart; /** * The array of points which defines the annotation. * * @type {Array<Highcharts.Point>} */ this.points = []; /** * The array of control points. * * @private * @name Highcharts.Annotation#controlPoints * @type {Array<Annotation.ControlPoint>} */ this.controlPoints = []; this.coll = 'annotations'; /** * The array of labels which belong to the annotation. * * @private * @name Highcharts.Annotation#labels * @type {Array<Highcharts.AnnotationLabelType>} */ this.labels = []; /** * The array of shapes which belong to the annotation. * * @private * @name Highcharts.Annotation#shapes * @type {Array<Highcharts.AnnotationShapeType>} */ this.shapes = []; /** * The options for the annotations. * * @name Highcharts.Annotation#options * @type {Highcharts.AnnotationsOptions} */ this.options = merge(this.defaultOptions, userOptions); /** * The user options for the annotations. * * @name Highcharts.Annotation#userOptions * @type {Highcharts.AnnotationsOptions} */ this.userOptions = userOptions; // Handle labels and shapes - those are arrays // Merging does not work with arrays (stores reference) labelsAndShapes = this.getLabelsAndShapesOptions(this.options, userOptions); this.options.labels = labelsAndShapes.labels; this.options.shapes = labelsAndShapes.shapes; /** * The callback that reports to the overlapping-labels module which * labels it should account for. * @private * @name Highcharts.Annotation#labelCollector * @type {Function} */ /** * The group svg element. * * @name Highcharts.Annotation#group * @type {Highcharts.SVGElement} */ /** * The group svg element of the annotation's shapes. * * @name Highcharts.Annotation#shapesGroup * @type {Highcharts.SVGElement} */ /** * The group svg element of the annotation's labels. * * @name Highcharts.Annotation#labelsGroup * @type {Highcharts.SVGElement} */ this.init(chart, this.options); } /** * Initialize the annotation. * @private */ Annotation.prototype.init = function () { var chart = this.chart, animOptions = this.options.animation; this.linkPoints(); this.addControlPoints(); this.addShapes(); this.addLabels(); this.setLabelCollector(); this.animationConfig = getDeferredAnimation(chart, animOptions); }; Annotation.prototype.getLabelsAndShapesOptions = function (baseOptions, newOptions) { var mergedOptions = {}; ['labels', 'shapes'].forEach(function (name) { if (baseOptions[name]) { mergedOptions[name] = splat(newOptions[name]).map(function (basicOptions, i) { return merge(baseOptions[name][i], basicOptions); }); } }); return mergedOptions; }; Annotation.prototype.addShapes = function () { (this.options.shapes || []).forEach(function (shapeOptions, i) { var shape = this.initShape(shapeOptions, i); merge(true, this.options.shapes[i], shape.options); }, this); }; Annotation.prototype.addLabels = function () { (this.options.labels || []).forEach(function (labelsOptions, i) { var labels = this.initLabel(labelsOptions, i); merge(true, this.options.labels[i], labels.options); }, this); }; Annotation.prototype.addClipPaths = function () { this.setClipAxes(); if (this.clipXAxis && this.clipYAxis) { this.clipRect = this.chart.renderer.clipRect(this.getClipBox()); } }; Annotation.prototype.setClipAxes = function () { var xAxes = this.chart.xAxis, yAxes = this.chart.yAxis, linkedAxes = (this.options.labels || []) .concat(this.options.shapes || []) .reduce(function (axes, labelOrShape) { return [ xAxes[labelOrShape && labelOrShape.point && labelOrShape.point.xAxis] || axes[0], yAxes[labelOrShape && labelOrShape.point && labelOrShape.point.yAxis] || axes[1] ]; }, []); this.clipXAxis = linkedAxes[0]; this.clipYAxis = linkedAxes[1]; }; Annotation.prototype.getClipBox = function () { if (this.clipXAxis && this.clipYAxis) { return { x: this.clipXAxis.left, y: this.clipYAxis.top, width: this.clipXAxis.width, height: this.clipYAxis.height }; } }; Annotation.prototype.setLabelCollector = function () { var annotation = this; annotation.labelCollector = function () { return annotation.labels.reduce(function (labels, label) { if (!label.options.allowOverlap) { labels.push(label.graphic); } return labels; }, []); }; annotation.chart.labelCollectors.push(annotation.labelCollector); }; /** * Set an annotation options. * @private * @param {Highcharts.AnnotationsOptions} - user options for an annotation */ Annotation.prototype.setOptions = function (userOptions) { this.options = merge(this.defaultOptions, userOptions); }; Annotation.prototype.redraw = function (animation) { this.linkPoints(); if (!this.graphic) { this.render(); } if (this.clipRect) { this.clipRect.animate(this.getClipBox()); } this.redrawItems(this.shapes, animation); this.redrawItems(this.labels, animation); ControllableMixin.redraw.call(this, animation); }; /** * @private * @param {Array<Highcharts.AnnotationControllable>} items * @param {boolean} [animation] */ Annotation.prototype.redrawItems = function (items, animation) { var i = items.length; // needs a backward loop // labels/shapes array might be modified // due to destruction of the item while (i--) { this.redrawItem(items[i], animation); } }; /** * @private * @param {Array<Highcharts.AnnotationControllable>} items */ Annotation.prototype.renderItems = function (items) { var i = items.length; while (i--) { this.renderItem(items[i]); } }; Annotation.prototype.render = function () { var renderer = this.chart.renderer; this.graphic = renderer .g('annotation') .attr({ opacity: 0, zIndex: this.options.zIndex, visibility: this.options.visible ? 'visible' : 'hidden' }) .add(); this.shapesGroup = renderer .g('annotation-shapes') .add(this.graphic) .clip(this.chart.plotBoxClip); this.labelsGroup = renderer .g('annotation-labels') .attr({ // hideOverlappingLabels requires translation translateX: 0, translateY: 0 }) .add(this.graphic); this.addClipPaths(); if (this.clipRect) { this.graphic.clip(this.clipRect); } // Render shapes and labels before adding events (#13070). this.renderItems(this.shapes); this.renderItems(this.labels); this.addEvents(); ControllableMixin.render.call(this); }; /** * Set the annotation's visibility. * @private * @param {boolean} [visible] * Whether to show or hide an annotation. If the param is omitted, the * annotation's visibility is toggled. */ Annotation.prototype.setVisibility = function (visible) { var options = this.options, visibility = pick(visible, !options.visible); this.graphic.attr('visibility', visibility ? 'visible' : 'hidden'); if (!visibility) { this.setControlPointsVisibility(false); } options.visible = visibility; }; Annotation.prototype.setControlPointsVisibility = function (visible) { var setItemControlPointsVisibility = function (item) { item.setControlPointsVisibility(visible); }; ControllableMixin.setControlPointsVisibility.call(this, visible); this.shapes.forEach(setItemControlPointsVisibility); this.labels.forEach(setItemControlPointsVisibility); }; /** * Destroy the annotation. This function does not touch the chart * that the annotation belongs to (all annotations are kept in * the chart.annotations array) - it is recommended to use * {@link Highcharts.Chart#removeAnnotation} instead. * @private */ Annotation.prototype.destroy = function () { var chart = this.chart, destroyItem = function (item) { item.destroy(); }; this.labels.forEach(destroyItem); this.shapes.forEach(destroyItem); this.clipXAxis = null; this.clipYAxis = null; erase(chart.labelCollectors, this.labelCollector); EventEmitterMixin.destroy.call(this); ControllableMixin.destroy.call(this); destroyObjectProperties(this, chart); }; /** * See {@link Highcharts.Chart#removeAnnotation}. * @private */ Annotation.prototype.remove = function () { // Let chart.update() remove annoations on demand return this.chart.removeAnnotation(this); }; /** * Updates an annotation. * * @function Highcharts.Annotation#update * * @param {Partial<Highcharts.AnnotationsOptions>} userOptions * New user options for the annotation. * * @return {void} */ Annotation.prototype.update = function (userOptions, redraw) { var chart = this.chart, labelsAndShapes = this.getLabelsAndShapesOptions(this.userOptions, userOptions), userOptionsIndex = chart.annotations.indexOf(this), options = merge(true, this.userOptions, userOptions); options.labels = labelsAndShapes.labels; options.shapes = labelsAndShapes.shapes; this.destroy(); this.constructor(chart, options); // Update options in chart options, used in exporting (#9767): chart.options.annotations[userOptionsIndex] = options; this.isUpdating = true; if (pick(redraw, true)) { chart.redraw(); } fireEvent(this, 'afterUpdate'); this.isUpdating = false; }; /* ************************************************************* * ITEM SECTION * Contains methods for handling a single item in an annotation **************************************************************** */ /** * Initialisation of a single shape * @private * @param {Object} shapeOptions - a confg object for a single shape */ Annotation.prototype.initShape = function (shapeOptions, index) { var options = merge(this.options.shapeOptions, { controlPointOptions: this.options.controlPointOptions }, shapeOptions), shape = new Annotation.shapesMap[options.type](this, options, index); shape.itemType = 'shape'; this.shapes.push(shape); return shape; }; /** * Initialisation of a single label * @private */ Annotation.prototype.initLabel = function (labelOptions, index) { var options = merge(this.options.labelOptions, { controlPointOptions: this.options.controlPointOptions }, labelOptions), label = new ControllableLabel(this, options, index); label.itemType = 'label'; this.labels.push(label); return label; }; /** * Redraw a single item. * @private * @param {Annotation.Label|Annotation.Shape} item * @param {boolean} [animation] */ Annotation.prototype.redrawItem = function (item, animation) { item.linkPoints(); if (!item.shouldBeDrawn()) { this.destroyItem(item); } else { if (!item.graphic) { this.renderItem(item); } item.redraw(pick(animation, true) && item.graphic.placed); if (item.points.length) { this.adjustVisibility(item); } } }; /** * Hide or show annotaiton attached to points. * @private * @param {Annotation.Label|Annotation.Shape} item */ Annotation.prototype.adjustVisibility = function (item) { var hasVisiblePoints = false, label = item.graphic; item.points.forEach(function (point) { if (point.series.visible !== false && point.visible !== false) { hasVisiblePoints = true; } }); if (!hasVisiblePoints) { label.hide(); } else if (label.visibility === 'hidden') { label.show(); } }; /** * Destroy a single item. * @private * @param {Annotation.Label|Annotation.Shape} item */ Annotation.prototype.destroyItem = function (item) { // erase from shapes or labels array erase(this[item.itemType + 's'], item); item.destroy(); }; /** * @private */ Annotation.prototype.renderItem = function (item) { item.render(item.itemType === 'label' ? this.labelsGroup : this.shapesGroup); }; /** * @private */ Annotation.ControlPoint = ControlPoint; /** * @private */ Annotation.MockPoint = MockPoint; /** * An object uses for mapping between a shape type and a constructor. * To add a new shape type extend this object with type name as a key * and a constructor as its value. */ Annotation.shapesMap = { 'rect': ControllableRect, 'circle': ControllableCircle, 'path': ControllablePath, 'image': ControllableImage }; /** * @private */ Annotation.types = {}; return Annotation; }()); merge(true, Annotation.prototype, ControllableMixin, EventEmitterMixin, // restore original Annotation implementation after mixin overwrite merge(Annotation.prototype, /** @lends Highcharts.Annotation# */ { /** * List of events for `annotation.options.events` that should not be * added to `annotation.graphic` but to the `annotation`. * * @private * @type {Array<string>} */ nonDOMEvents: ['add', 'afterUpdate', 'drag', 'remove'], /** * A basic type of an annotation. It allows to add custom labels * or shapes. The items can be tied to points, axis coordinates * or chart pixel coordinates. * * @sample highcharts/annotations/basic/ * Basic annotations * @sample highcharts/demo/annotations/ * Advanced annotations * @sample highcharts/css/annotations * Styled mode * @sample highcharts/annotations-advanced/controllable * Controllable items * @sample {highstock} stock/annotations/fibonacci-retracements * Custom annotation, Fibonacci retracement * * @type {Array<*>} * @since 6.0.0 * @requires modules/annotations * @optionparent annotations * * @private */ defaultOptions: { /** * Sets an ID for an annotation. Can be user later when * removing an annotation in [Chart#removeAnnotation(id)]( * /class-reference/Highcharts.Chart#removeAnnotation) method. * * @type {number|string} * @apioption annotations.id */ /** * Whether the annotation is visible. * * @sample highcharts/annotations/visible/ * Set annotation visibility */ visible: true, /** * Enable or disable the initial animation when a series is * displayed for the `annotation`. The animation can also be set * as a configuration object. Please note that this option only * applies to the initial animation. * For other animations, see [chart.animation](#chart.animation) * and the animation parameter under the API methods. * The following properties are supported: * * - `defer`: The animation delay time in milliseconds. * * @sample {highcharts} highcharts/annotations/defer/ * Animation defer settings * @type {boolean|Partial<Highcharts.AnimationOptionsObject>} * @since 8.2.0 * @apioption annotations.animation */ animation: {}, /** * The animation delay time in milliseconds. * Set to `0` renders annotation immediately. * As `undefined` inherits defer time from the [series.animation.defer](#plotOptions.series.animation.defer). * * @type {number} * @since 8.2.0 * @apioption annotations.animation.defer */ /** * Allow an annotation to be draggable by a user. Possible * values are `'x'`, `'xy'`, `'y'` and `''` (disabled). * * @sample highcharts/annotations/draggable/ * Annotations draggable: 'xy' * * @type {Highcharts.AnnotationDraggableValue} */ draggable: 'xy', /** * Options for annotation's labels. Each label inherits options * from the labelOptions object. An option from the labelOptions * can be overwritten by config for a specific label. * * @requires modules/annotations */ labelOptions: { /** * The alignment of the annotation's label. If right, * the right side of the label should be touching the point. * * @sample highcharts/annotations/label-position/ * Set labels position * * @type {Highcharts.AlignValue} */ align: 'center', /** * Whether to allow the annotation's labels to overlap. * To make the labels less sensitive for overlapping, * the can be set to 0. * * @sample highcharts/annotations/tooltip-like/ * Hide overlapping labels */ allowOverlap: false, /** * The background color or gradient for the annotation's * label. * * @sample highcharts/annotations/label-presentation/ * Set labels graphic options * * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} */ backgroundColor: 'rgba(0, 0, 0, 0.75)', /** * The border color for the annotation's label. * * @sample highcharts/annotations/label-presentation/ * Set labels graphic options * * @type {Highcharts.ColorString} */ borderColor: 'black', /** * The border radius in pixels for the annotaiton's label. * * @sample highcharts/annotations/label-presentation/ * Set labels graphic options */ borderRadius: 3, /** * The border width in pixels for the annotation's label * * @sample highcharts/annotations/label-presentation/ * Set labels graphic options */ borderWidth: 1, /** * A class name for styling by CSS. * * @sample highcharts/css/annotations * Styled mode annotations * * @since 6.0.5 */ className: '', /** * Whether to hide the annotation's label * that is outside the plot area. * * @sample highcharts/annotations/label-crop-overflow/ * Crop or justify labels */ crop: false, /** * The label's pixel distance from the point. * * @sample highcharts/annotations/label-position/ * Set labels position * * @type {number} * @apioption annotations.labelOptions.distance */ /** * A * [format](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting) * string for the data label. * * @see [plotOptions.series.dataLabels.format](plotOptions.series.dataLabels.format.html) * * @sample highcharts/annotations/label-text/ * Set labels text * * @type {string} * @apioption annotations.labelOptions.format */ /** * Alias for the format option. * * @see [format](annotations.labelOptions.format.html) * * @sample highcharts/annotations/label-text/ * Set labels text * * @type {string} * @apioption annotations.labelOptions.text */ /** * Callback JavaScript function to format the annotation's * label. Note that if a `format` or `text` are defined, * the format or text take precedence and the formatter is * ignored. `This` refers to a point object. * * @sample highcharts/annotations/label-text/ * Set labels text * * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point>} * @default function () { return defined(this.y) ? this.y : 'Annotation label'; } */ formatter: function () { return defined(this.y) ? this.y : 'Annotation label'; }, /** * Whether the annotation is visible in the exported data * table. * * @sample highcharts/annotations/include-in-data-export/ * Do not include in the data export * * @since 8.2.0 * @requires modules/export-data */ includeInDataExport: true, /** * How to handle the annotation's label that flow outside * the plot area. The justify option aligns the label inside * the plot area. * * @sample highcharts/annotations/label-crop-overflow/ * Crop or justify labels * * @validvalue ["allow", "justify"] */ overflow: 'justify', /** * When either the borderWidth or the backgroundColor is * set, this is the padding within the box. * * @sample highcharts/annotations/label-presentation/ * Set labels graphic options */ padding: 5, /** * The shadow of the box. The shadow can be an object * configuration containing `color`, `offsetX`, `offsetY`, * `opacity` and `width`. * * @sample highcharts/annotations/label-presentation/ * Set labels graphic options * * @type {boolean|Highcharts.ShadowOptionsObject} */ shadow: false, /** * The name of a symbol to use for the border around the * label. Symbols are predefined functions on the Renderer * object. * * @sample highcharts/annotations/shapes/ * Available shapes for labels */ shape: 'callout', /** * Styles for the annotation's label. * * @see [plotOptions.series.dataLabels.style](plotOptions.series.dataLabels.style.html) * * @sample highcharts/annotations/label-presentation/ * Set labels graphic options * * @type {Highcharts.CSSObject} */ style: { /** @ignore */ fontSize: '11px', /** @ignore */ fontWeight: 'normal', /** @ignore */ color: 'contrast' }, /** * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html) * to render the annotation's label. */ useHTML: false, /** * The vertical alignment of the annotation's label. * * @sample highcharts/annotations/label-position/ * Set labels position * * @type {Highcharts.VerticalAlignValue} */ verticalAlign: 'bottom', /** * The x position offset of the label relative to the point. * Note that if a `distance` is defined, the distance takes * precedence over `x` and `y` options. * * @sample highcharts/annotations/label-position/ * Set labels position */ x: 0, /** * The y position offset of the label relative to the point. * Note that if a `distance` is defined, the distance takes * precedence over `x` and `y` options. * * @sample highcharts/annotations/label-position/ * Set labels position */ y: -16 }, /** * An array of labels for the annotation. For options that apply * to multiple labels, they can be added to the * [labelOptions](annotations.labelOptions.html). * * @type {Array<*>} * @extends annotations.labelOptions * @apioption annotations.labels */ /** * This option defines the point to which the label will be * connected. It can be either the point which exists in the * series - it is referenced by the point's id - or a new point * with defined x, y properties and optionally axes. * * @sample highcharts/annotations/mock-point/ * Attach annotation to a mock point * * @declare Highcharts.AnnotationMockPointOptionsObject * @type {string|*} * @requires modules/annotations * @apioption annotations.labels.point */ /** * The x position of the point. Units can be either in axis * or chart pixel coordinates. * * @type {number} * @apioption annotations.labels.point.x */ /** * The y position of the point. Units can be either in axis * or chart pixel coordinates. * * @type {number} * @apioption annotations.labels.point.y */ /** * This number defines which xAxis the point is connected to. * It refers to either the axis id or the index of the axis in * the xAxis array. If the option is not configured or the axis * is not found the point's x coordinate refers to the chart * pixels. * * @type {number|string|null} * @apioption annotations.labels.point.xAxis */ /** * This number defines which yAxis the point is connected to. * It refers to either the axis id or the index of the axis in * the yAxis array. If the option is not configured or the axis * is not found the point's y coordinate refers to the chart * pixels. * * @type {number|string|null} * @apioption annotations.labels.point.yAxis */ /** * An array of shapes for the annotation. For options that apply * to multiple shapes, then can be added to the * [shapeOptions](annotations.shapeOptions.html). * * @type {Array<*>} * @extends annotations.shapeOptions * @apioption annotations.shapes */ /** * This option defines the point to which the shape will be * connected. It can be either the point which exists in the * series - it is referenced by the point's id - or a new point * with defined x, y properties and optionally axes. * * @declare Highcharts.AnnotationMockPointOptionsObject * @type {string|Highcharts.AnnotationMockPointOptionsObject} * @extends annotations.labels.point * @apioption annotations.shapes.point */ /** * An array of points for the shape. This option is available * for shapes which can use multiple points such as path. A * point can be either a point object or a point's id. * * @see [annotations.shapes.point](annotations.shapes.point.html) * * @declare Highcharts.AnnotationMockPointOptionsObject * @type {Array<string|*>} * @extends annotations.labels.point * @apioption annotations.shapes.points */ /** * The URL for an image to use as the annotation shape. Note, * type has to be set to `'image'`. * * @see [annotations.shapes.type](annotations.shapes.type) * @sample highcharts/annotations/shape-src/ * Define a marker image url for annotations * * @type {string} * @apioption annotations.shapes.src */ /** * Id of the marker which will be drawn at the final vertex of * the path. Custom markers can be defined in defs property. * * @see [defs.markers](defs.markers.html) * * @sample highcharts/annotations/custom-markers/ * Define a custom marker for annotations * * @type {string} * @apioption annotations.shapes.markerEnd */ /** * Id of the marker which will be drawn at the first vertex of * the path. Custom markers can be defined in defs property. * * @see [defs.markers](defs.markers.html) * * @sample {highcharts} highcharts/annotations/custom-markers/ * Define a custom marker for annotations * * @type {string} * @apioption annotations.shapes.markerStart */ /** * Options for annotation's shapes. Each shape inherits options * from the shapeOptions object. An option from the shapeOptions * can be overwritten by config for a specific shape. * * @requires modules/annotations */ shapeOptions: { /** * The width of the shape. * * @sample highcharts/annotations/shape/ * Basic shape annotation * * @type {number} * @apioption annotations.shapeOptions.width **/ /** * The height of the shape. * * @sample highcharts/annotations/shape/ * Basic shape annotation * * @type {number} * @apioption annotations.shapeOptions.height */ /** * The type of the shape, e.g. circle or rectangle. * * @sample highcharts/annotations/shape/ * Basic shape annotation * * @type {string} * @default rect * @apioption annotations.shapeOptions.type */ /** * The URL for an image to use as the annotation shape. * Note, type has to be set to `'image'`. * * @see [annotations.shapeOptions.type](annotations.shapeOptions.type) * @sample highcharts/annotations/shape-src/ * Define a marker image url for annotations * * @type {string} * @apioption annotations.shapeOptions.src */ /** * Name of the dash style to use for the shape's stroke. * * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/ * Possible values demonstrated * * @type {Highcharts.DashStyleValue} * @apioption annotations.shapeOptions.dashStyle */ /** * The color of the shape's stroke. * * @sample highcharts/annotations/shape/ * Basic shape annotation * * @type {Highcharts.ColorString} */ stroke: 'rgba(0, 0, 0, 0.75)', /** * The pixel stroke width of the shape. * * @sample highcharts/annotations/shape/ * Basic shape annotation */ strokeWidth: 1, /** * The color of the shape's fill. * * @sample highcharts/annotations/shape/ * Basic shape annotation * * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} */ fill: 'rgba(0, 0, 0, 0.75)', /** * The radius of the shape. * * @sample highcharts/annotations/shape/ * Basic shape annotation */ r: 0, /** * Defines additional snapping area around an annotation * making this annotation to focus. Defined in pixels. */ snap: 2 }, /** * Options for annotation's control points. Each control point * inherits options from controlPointOptions object. * Options from the controlPointOptions can be overwritten * by options in a specific control point. * * @declare Highcharts.AnnotationControlPointOptionsObject * @requires modules/annotations * @apioption annotations.controlPointOptions */ controlPointOptions: { /** * @type {Highcharts.AnnotationControlPointPositionerFunction} * @apioption annotations.controlPointOptions.positioner */ symbol: 'circle', width: 10, height: 10, style: { stroke: 'black', 'stroke-width': 2, fill: 'white' }, visible: false, events: {} }, /** * Event callback when annotation is added to the chart. * * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>} * @since 7.1.0 * @apioption annotations.events.add */ /** * Event callback when annotation is updated (e.g. drag and * droppped or resized by control points). * * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>} * @since 7.1.0 * @apioption annotations.events.afterUpdate */ /** * Event callback when annotation is removed from the chart. * * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>} * @since 7.1.0 * @apioption annotations.events.remove */ /** * Events available in annotations. * * @requires modules/annotations */ events: {}, /** * The Z index of the annotation. */ zIndex: 6 } })); H.extendAnnotation = function (Constructor, BaseConstructor, prototype, defaultOptions) { BaseConstructor = BaseConstructor || Annotation; merge(true, Constructor.prototype, BaseConstructor.prototype, prototype); Constructor.prototype.defaultOptions = merge(Constructor.prototype.defaultOptions, defaultOptions || {}); }; /* ********************************************************************* * * EXTENDING CHART PROTOTYPE * ******************************************************************** */ extend(chartProto, /** @lends Highcharts.Chart# */ { initAnnotation: function (userOptions) { var Constructor = Annotation.types[userOptions.type] || Annotation, annotation = new Constructor(this, userOptions); this.annotations.push(annotation); return annotation; }, /** * Add an annotation to the chart after render time. * * @param {Highcharts.AnnotationsOptions} options * The annotation options for the new, detailed annotation. * @param {boolean} [redraw] * * @return {Highcharts.Annotation} - The newly generated annotation. */ addAnnotation: function (userOptions, redraw) { var annotation = this.initAnnotation(userOptions); this.options.annotations.push(annotation.options); if (pick(redraw, true)) { annotation.redraw(); annotation.graphic.attr({ opacity: 1 }); } return annotation; }, /** * Remove an annotation from the chart. * * @param {number|string|Highcharts.Annotation} idOrAnnotation * The annotation's id or direct annotation object. */ removeAnnotation: function (idOrAnnotation) { var annotations = this.annotations, annotation = idOrAnnotation.coll === 'annotations' ? idOrAnnotation : find(annotations, function (annotation) { return annotation.options.id === idOrAnnotation; }); if (annotation) { fireEvent(annotation, 'remove'); erase(this.options.annotations, annotation.options); erase(annotations, annotation); annotation.destroy(); } }, drawAnnotations: function () { this.plotBoxClip.attr(this.plotBox); this.annotations.forEach(function (annotation) { annotation.redraw(); annotation.graphic.animate({ opacity: 1 }, annotation.animationConfig); }); } }); // Let chart.update() update annotations chartProto.collectionsWithUpdate.push('annotations'); // Let chart.update() create annoations on demand chartProto.collectionsWithInit.annotations = [chartProto.addAnnotation]; // Create lookups initially addEvent(Chart, 'afterInit', function () { this.annotations = []; if (!this.options.annotations) { this.options.annotations = []; } }); chartProto.callbacks.push(function (chart) { chart.plotBoxClip = this.renderer.clipRect(this.plotBox); chart.controlPointsGroup = chart.renderer .g('control-points') .attr({ zIndex: 99 }) .clip(chart.plotBoxClip) .add(); chart.options.annotations.forEach(function (annotationOptions, i) { if ( // Verify that it has not been previously added in a responsive rule !chart.annotations.some(function (annotation) { return annotation.options === annotationOptions; })) { var annotation = chart.initAnnotation(annotationOptions); chart.options.annotations[i] = annotation.options; } }); chart.drawAnnotations(); addEvent(chart, 'redraw', chart.drawAnnotations); addEvent(chart, 'destroy', function () { chart.plotBoxClip.destroy(); chart.controlPointsGroup.destroy(); }); addEvent(chart, 'exportData', function (event) { var _a, _b, _c, _d, _e, _f, _g, _h; var annotations = chart.annotations, csvColumnHeaderFormatter = ((this.options.exporting && this.options.exporting.csv) || {}).columnHeaderFormatter, // If second row doesn't have xValues // then it is a title row thus multiple level header is in use. multiLevelHeaders = !event.dataRows[1].xValues, annotationHeader = (_b = (_a = chart.options.lang) === null || _a === void 0 ? void 0 : _a.exportData) === null || _b === void 0 ? void 0 : _b.annotationHeader, columnHeaderFormatter = function (index) { var s; if (csvColumnHeaderFormatter) { s = csvColumnHeaderFormatter(index); if (s !== false) { return s; } } s = annotationHeader + ' ' + index; if (multiLevelHeaders) { return { columnTitle: s, topLevelColumnTitle: s }; } return s; }, startRowLength = event.dataRows[0].length, annotationSeparator = (_e = (_d = (_c = chart.options.exporting) === null || _c === void 0 ? void 0 : _c.csv) === null || _d === void 0 ? void 0 : _d.annotations) === null || _e === void 0 ? void 0 : _e.itemDelimiter, joinAnnotations = (_h = (_g = (_f = chart.options.exporting) === null || _f === void 0 ? void 0 : _f.csv) === null || _g === void 0 ? void 0 : _g.annotations) === null || _h === void 0 ? void 0 : _h.join; annotations.forEach(function (annotation) { if (annotation.options.labelOptions.includeInDataExport) { annotation.labels.forEach(function (label) { if (label.options.text) { var annotationText_1 = label.options.text; label.points.forEach(function (points) { var annotationX = points.x, xAxisIndex = points.series.xAxis ? points.series.xAxis.options.index : -1; var wasAdded = false; // Annotation not connected to any xAxis - // add new row. if (xAxisIndex === -1) { var n = event.dataRows[0].length, newRow = new Array(n); for (var i = 0; i < n; ++i) { newRow[i] = ''; } newRow.push(annotationText_1); newRow.xValues = []; newRow.xValues[xAxisIndex] = annotationX; event.dataRows.push(newRow); wasAdded = true; } // Annotation placed on a exported data point // - add new column if (!wasAdded) { event.dataRows.forEach(function (row, rowIndex) { if (!wasAdded && row.xValues && xAxisIndex !== void 0 && annotationX === row.xValues[xAxisIndex]) { if (joinAnnotations && row.length > startRowLength) { row[row.length - 1] += annotationSeparator + annotationText_1; } else { row.push(annotationText_1); } wasAdded = true; } }); } // Annotation not placed on any exported data point, // but connected to the xAxis - add new row if (!wasAdded) { var n = event.dataRows[0].length, newRow = new Array(n); for (var i = 0; i < n; ++i) { newRow[i] = ''; } newRow[0] = annotationX; newRow.push(annotationText_1); newRow.xValues = []; if (xAxisIndex !== void 0) { newRow.xValues[xAxisIndex] = annotationX; } event.dataRows.push(newRow); } }); } }); } }); var maxRowLen = 0; event.dataRows.forEach(function (row) { maxRowLen = Math.max(maxRowLen, row.length); }); var newRows = maxRowLen - event.dataRows[0].length; for (var i = 0; i < newRows; i++) { var header = columnHeaderFormatter(i + 1); if (multiLevelHeaders) { event.dataRows[0].push(header.topLevelColumnTitle); event.dataRows[1].push(header.columnTitle); }