/* * * * (c) 2009-2021 Highsoft, Black Label * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); import A from '../../Core/Animation/AnimationUtilities.js'; var getDeferredAnimation = A.getDeferredAnimation; import AnnotationChart from './AnnotationChart.js'; import AnnotationDefaults from './AnnotationDefaults.js'; import Controllable from './Controllables/Controllable.js'; var controllableProto = Controllable.prototype; import ControllableRect from './Controllables/ControllableRect.js'; import ControllableCircle from './Controllables/ControllableCircle.js'; import ControllableEllipse from './Controllables/ControllableEllipse.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 EventEmitter from './EventEmitter.js'; import MockPoint from './MockPoint.js'; import NavigationBindings from './NavigationBindings.js'; import PopupComposition from './Popup/PopupComposition.js'; import U from '../../Core/Utilities.js'; var addEvent = U.addEvent, 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; /* * * * Functions * * */ /** * Hide or show annotaiton attached to points. * @private */ function adjustVisibility(item) { var label = item.graphic, hasVisiblePoints = item.points.some(function (point) { return (point.series.visible !== false && point.visible !== false); }); if (label) { if (!hasVisiblePoints) { label.hide(); } else if (label.visibility === 'hidden') { label.show(); } } } /** * @private */ function getLabelsAndShapesOptions(baseOptions, newOptions) { var mergedOptions = {}; ['labels', 'shapes'].forEach(function (name) { var someBaseOptions = baseOptions[name]; if (someBaseOptions) { if (newOptions[name]) { mergedOptions[name] = splat(newOptions[name]).map(function (basicOptions, i) { return merge(someBaseOptions[i], basicOptions); }); } else { mergedOptions[name] = baseOptions[name]; } } }); return mergedOptions; } /* * * * Class * * */ /** * 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 annotation options */ var Annotation = /** @class */ (function (_super) { __extends(Annotation, _super); /* * * * Constructors * * */ function Annotation(chart, userOptions) { var _this = _super.call(this) || this; /* * * * 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; /** * The chart that the annotation belongs to. * * @name Highcharts.Annotation#chart * @type {Highcharts.Chart} */ _this.chart = chart; /** * The array of points which defines the annotation. * @private * @name Highcharts.Annotation#points * @type {Array} */ _this.points = []; /** * The array of control points. * @private * @name Highcharts.Annotation#controlPoints * @type {Array} */ _this.controlPoints = []; _this.coll = 'annotations'; /** * The array of labels which belong to the annotation. * @private * @name Highcharts.Annotation#labels * @type {Array} */ _this.labels = []; /** * The array of shapes which belong to the annotation. * @private * @name Highcharts.Annotation#shapes * @type {Array} */ _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) var labelsAndShapes = 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); return _this; } /* * * * Static Functions * * */ /** * @private */ Annotation.compose = function (ChartClass, PointerClass, SVGRendererClass) { AnnotationChart.compose(Annotation, ChartClass, PointerClass); ControllableLabel.compose(SVGRendererClass); ControllablePath.compose(ChartClass, SVGRendererClass); NavigationBindings.compose(Annotation, ChartClass); PopupComposition.compose(NavigationBindings, PointerClass); }; /* * * * Functions * * */ /** * @private */ Annotation.prototype.addClipPaths = function () { this.setClipAxes(); if (this.clipXAxis && this.clipYAxis && this.options.crop // #15399 ) { this.clipRect = this.chart.renderer.clipRect(this.getClipBox()); } }; /** * @private */ Annotation.prototype.addLabels = function () { var _this = this; var labelsOptions = (this.options.labels || []); labelsOptions.forEach(function (labelOptions, i) { var label = _this.initLabel(labelOptions, i); merge(true, labelsOptions[i], label.options); }); }; /** * @private */ Annotation.prototype.addShapes = function () { var _this = this; var shapes = this.options.shapes || []; shapes.forEach(function (shapeOptions, i) { var shape = _this.initShape(shapeOptions, i); merge(true, shapes[i], shape.options); }); }; /** * 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); _super.prototype.destroy.call(this); controllableProto.destroy.call(this); destroyObjectProperties(this, chart); }; /** * Destroy a single item. * @private */ Annotation.prototype.destroyItem = function (item) { // erase from shapes or labels array erase(this[item.itemType + 's'], item); item.destroy(); }; /** * @private */ 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 }; } }; /** * Initialize the annotation. * @private */ Annotation.prototype.init = function (_annotationOrChart, _userOptions, _index) { var chart = this.chart, animOptions = this.options.animation; this.linkPoints(); this.addControlPoints(); this.addShapes(); this.addLabels(); this.setLabelCollector(); this.animationConfig = getDeferredAnimation(chart, animOptions); }; /** * 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; }; /** * Initialisation of a single shape * @private * @param {Object} shapeOptions * a confg object for a single shape * @param {number} index * annotation may have many shapes, this is the shape's index saved in * shapes.index. */ 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; }; /** * @private */ 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); controllableProto.redraw.call(this, animation); }; /** * Redraw a single item. * @private */ 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) { adjustVisibility(item); } } }; /** * @private */ 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); } }; /** * See {@link Highcharts.Chart#removeAnnotation}. * @private */ Annotation.prototype.remove = function () { // Let chart.update() remove annoations on demand return this.chart.removeAnnotation(this); }; /** * @private */ 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 ? 'inherit' : 'hidden' }) .add(); this.shapesGroup = renderer .g('annotation-shapes') .add(this.graphic); if (this.options.crop) { // #15399 this.shapesGroup.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(); controllableProto.render.call(this); }; /** * @private */ Annotation.prototype.renderItem = function (item) { item.render(item.itemType === 'label' ? this.labelsGroup : this.shapesGroup); }; /** * @private */ Annotation.prototype.renderItems = function (items) { var i = items.length; while (i--) { this.renderItem(items[i]); } }; /** * @private */ 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) { var point = labelOrShape && (labelOrShape.point || (labelOrShape.points && labelOrShape.points[0])); return [ xAxes[point && point.xAxis] || axes[0], yAxes[point && point.yAxis] || axes[1] ]; }, []); this.clipXAxis = linkedAxes[0]; this.clipYAxis = linkedAxes[1]; }; /** * @private */ Annotation.prototype.setControlPointsVisibility = function (visible) { var setItemControlPointsVisibility = function (item) { item.setControlPointsVisibility(visible); }; controllableProto.setControlPointsVisibility.call(this, visible); this.shapes.forEach(setItemControlPointsVisibility); this.labels.forEach(setItemControlPointsVisibility); }; /** * @private */ 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} userOptions * User options for an annotation */ Annotation.prototype.setOptions = function (userOptions) { this.options = merge(this.defaultOptions, userOptions); }; /** * 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, navigation = this.chart.navigationBindings, visibility = pick(visible, !options.visible); this.graphic.attr('visibility', visibility ? 'inherit' : 'hidden'); if (!visibility) { this.setControlPointsVisibility(false); if (navigation.activeAnnotation === this && navigation.popup && navigation.popup.formType === 'annotation-toolbar') { fireEvent(navigation, 'closePopup'); } } options.visible = visibility; }; /** * Updates an annotation. * * @function Highcharts.Annotation#update * * @param {Partial} userOptions * New user options for the annotation. * */ Annotation.prototype.update = function (userOptions, redraw) { var chart = this.chart, labelsAndShapes = 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; }; /* * * * Static Properties * * */ /** * @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, 'ellipse': ControllableEllipse, 'path': ControllablePath, 'image': ControllableImage }; /** * @private */ Annotation.types = {}; return Annotation; }(EventEmitter)); merge(true, Annotation.prototype, Controllable.prototype, // restore original Annotation implementation after mixin overwrite: merge(Annotation.prototype, { /** * List of events for `annotation.options.events` that should not be * added to `annotation.graphic` but to the `annotation`. * * @private * @type {Array} */ nonDOMEvents: ['add', 'afterUpdate', 'drag', 'remove'], defaultOptions: AnnotationDefaults })); /* * * * Default Export * * */ export default Annotation; /* * * * API Options * * */ /** * Possible directions for draggable annotations. An empty string (`''`) * makes the annotation undraggable. * * @typedef {''|'x'|'xy'|'y'} Highcharts.AnnotationDraggableValue * @requires modules/annotations */ /** * @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 */ /** * Shape point as string, object or function. * * @typedef { * string| * Highcharts.AnnotationMockPointOptionsObject| * Highcharts.AnnotationMockPointFunction * } Highcharts.AnnotationShapePointOptions */ (''); // keeps doclets above in JS file