| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231 | /** * (c) 2009-2017 Highsoft, Black Label * * License: www.highcharts.com/license */'use strict';import H from '../parts/Globals.js';import '../parts/Utilities.js';import '../parts/Chart.js';import controllableMixin from './controllable/controllableMixin.js';import ControllableRect from './controllable/ControllableRect.js';import ControllableCircle from './controllable/ControllableCircle.js';import ControllablePath from './controllable/ControllablePath.js';import ControllableImage from './controllable/ControllableImage.js';import ControllableLabel from './controllable/ControllableLabel.js';import eventEmitterMixin from './eventEmitterMixin.js';import MockPoint from './MockPoint.js';import ControlPoint from './ControlPoint.js';var merge = H.merge,    addEvent = H.addEvent,    defined = H.defined,    erase = H.erase,    find = H.find,    isString = H.isString,    pick = H.pick,    reduce = H.reduce,    splat = H.splat,    destroyObjectProperties = H.destroyObjectProperties;/* ********************************************************************* * * ANNOTATION * ******************************************************************** *//** * @typedef { *          Annotation.ControllableCircle| *          Annotation.ControllableImage| *          Annotation.ControllablePath| *          Annotation.ControllableRect *          } *          Annotation.Shape *//** * @typedef {Annotation.ControllableLabel} Annotation.Label *//** * 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 * @mixes Annotation.controllableMixin * @mixes Annotation.eventEmitterMixin * * @param {Highcharts.Chart} chart a chart instance * @param {AnnotationOptions} options the options object */var Annotation = H.Annotation = function (chart, options) {    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<Annotation.PointLike>}     */    this.points = [];    /**     * The array of control points.     *     * @type {Array<Annotation.ControlPoint>}     */    this.controlPoints = [];    this.coll = 'annotations';    /**     * The array of labels which belong to the annotation.     *     * @type {Array<Annotation.Label>}     */    this.labels = [];    /**     * The array of shapes which belong to the annotation.     *     * @type {Array<Annotation.Shape>}     */    this.shapes = [];    /**     * The options for the annotations.     *     * @type {AnnotationOptions}     */    // this.options = merge(this.defaultOptions, userOptions);    this.options = options;    /**     * The user options for the annotations.     *     * @type {AnnotationOptions}     */    this.userOptions = merge(true, {}, options);    // Handle labels and shapes - those are arrays    // Merging does not work with arrays (stores reference)    labelsAndShapes = this.getLabelsAndShapesOptions(        this.userOptions,        options    );    this.userOptions.labels = labelsAndShapes.labels;    this.userOptions.shapes = labelsAndShapes.shapes;    /**     * The callback that reports to the overlapping-labels module which     * labels it should account for.     *     * @name labelCollector     * @memberOf Annotation#     * @type {Function}     */    /**     * The group svg element.     *     * @name group     * @memberOf Annotation#     * @type {Highcharts.SVGElement}     */    /**     * The group svg element of the annotation's shapes.     *     * @name shapesGroup     * @memberOf Annotation#     * @type {Highcharts.SVGElement}     */    /**     * The group svg element of the annotation's labels.     *     * @name labelsGroup     * @memberOf Annotation#     * @type {Highcharts.SVGElement}     */    this.init(chart, options);};merge(    true,    Annotation.prototype,    controllableMixin,    eventEmitterMixin, /** @lends Annotation# */ {        /**         * 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.         *         * @private         * @type {Object}         * @ignore-options base, annotations.crookedLine         * @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         * @since 6.0.0         * @optionparent annotations.crookedLine         */        defaultOptions: {            /**             * Whether the annotation is visible.             *             * @sample highcharts/annotations/visible/             *         Set annotation visibility             */            visible: true,            /**             * Allow an annotation to be draggable by a user. Possible             * values are `"x"`, `"xy"`, `"y"` and `""` (disabled).             *             * @type {string}             * @validvalue ["x", "xy", "y", ""]             */            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.             */            labelOptions: {                /**                 * The alignment of the annotation's label. If right,                 * the right side of the label should be touching the point.                 *                 * @validvalue ["left", "center", "right"]                 * @sample highcharts/annotations/label-position/                 *         Set labels position                 */                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.                 *                 * @type {Color}                 * @sample highcharts/annotations/label-presentation/                 *         Set labels graphic options                 */                backgroundColor: 'rgba(0, 0, 0, 0.75)',                /**                 * The border color for the annotation's label.                 *                 * @type {Color}                 * @sample highcharts/annotations/label-presentation/                 *         Set labels graphic options                 */                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.                 *                 * @type {number}                 * @sample highcharts/annotations/label-position/                 *         Set labels position                 * @default undefined                 * @apioption annotations.crookedLine.labelOptions.distance                 */                /**                 * A [format](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting) string for the data label.                 *                 * @type {string}                 * @see    [plotOptions.series.dataLabels.format](                 *         plotOptions.series.dataLabels.format.html)                 * @sample highcharts/annotations/label-text/                 *         Set labels text                 * @default undefined                 * @apioption annotations.crookedLine.labelOptions.format                 */                /**                 * Alias for the format option.                 *                 * @type {string}                 * @see [format](annotations.labelOptions.format.html)                 * @sample highcharts/annotations/label-text/                 *         Set labels text                 * @default undefined                 * @apioption annotations.crookedLine.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.                 *                 * @type {function}                 * @sample highcharts/annotations/label-text/                 *         Set labels text                 * @default function () {                 *  return defined(this.y) ? this.y : 'Annotation label';                 * }                 */                formatter: function () {                    return defined(this.y) ? this.y : 'Annotation label';                },                /**                 * How to handle the annotation's label that flow                 * outside the plot area. The justify option aligns the label                 * inside the plot area.                 *                 * @validvalue ["none", "justify"]                 * @sample highcharts/annotations/label-crop-overflow/                 *         Crop or justify labels                 **/                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`.                 *                 * @type {Boolean|Object}                 * @sample highcharts/annotations/label-presentation/                 *         Set labels graphic options                 */                shadow: false,                /**                 * The name of a symbol to use for the border around the label.                 * Symbols are predefined functions on the Renderer object.                 *                 * @type {string}                 * @sample highcharts/annotations/shapes/                 *         Available shapes for labels                 */                shape: 'callout',                /**                 * Styles for the annotation's label.                 *                 * @type {CSSObject}                 * @sample highcharts/annotations/label-presentation/                 *         Set labels graphic options                 * @see    [plotOptions.series.dataLabels.style](                 *         plotOptions.series.dataLabels.style.html)                 */                style: {                    fontSize: '11px',                    fontWeight: 'normal',                    color: 'contrast'                },                /**                 * Whether to [use HTML](http://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)                 * to render the annotation's label.                 *                 * @type {boolean}                 * @default false                 */                useHTML: false,                /**                 * The vertical alignment of the annotation's label.                 *                 * @type {string}                 * @validvalue ["top", "middle", "bottom"]                 * @sample highcharts/annotations/label-position/                 *         Set labels position                 */                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<Object>}             * @extends annotations.crookedLine.labelOptions             * @apioption annotations.crookedLine.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             * properies and optionally axes.             *             * @type {string|MockPointOptions}             * @sample highcharts/annotations/mock-point/             *         Attach annotation to a mock point             * @apioption annotations.crookedLine.labels.point             */            /**             * The x position of the point. Units can be either in axis             * or chart pixel coordinates.             *             * @type {number}             * @apioption annotations.crookedLine.labels.point.x             */            /**             * The y position of the point. Units can be either in axis             * or chart pixel coordinates.             *             * @type {number}             * @apioption annotations.crookedLine.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}             * @apioption annotations.crookedLine.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}             * @apioption annotations.crookedLine.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<Object>}             * @extends annotations.crookedLine.shapeOptions             * @apioption annotations.crookedLine.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.             *             * @type {string|MockPointOptions}             * @extends annotations.crookedLine.labels.point             * @apioption annotations.crookedLine.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.             *             * @type {Array<string|Highcharts.MockPoint.Options>}             * @see [annotations.shapes.point](annotations.shapes.point.html)             * @apioption annotations.crookedLine.shapes.points             */            /**             * Id of the marker which will be drawn at the final             * vertex of the path.             * Custom markers can be defined in defs property.             *             * @type {string}             * @see [defs.markers](defs.markers.html)             * @sample highcharts/annotations/custom-markers/             *         Define a custom marker for annotations             * @apioption annotations.crookedLine.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.             *             * @type {string}             * @see [defs.markers](defs.markers.html)             * @sample {highcharts} highcharts/annotations/custom-markers/             *         Define a custom marker for annotations             * @apioption annotations.crookedLine.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.             *             * @type {Object}             */            shapeOptions: {                /**                 * The width of the shape.                 *                 * @type {number}                 * @sample highcharts/annotations/shape/                 *         Basic shape annotation                 * @apioption annotations.crookedLine.shapeOptions.width                 **/                /**                 * The height of the shape.                 *                 * @type {number}                 * @sample highcharts/annotations/shape/                 *         Basic shape annotation                 * @apioption annotations.crookedLine.shapeOptions.height                 */                /**                 * The color of the shape's stroke.                 *                 * @type {Color}                 * @sample highcharts/annotations/shape/                 *         Basic shape annotation                 */                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.                 *                 * @type {Color}                 * @sample highcharts/annotations/shape/                 *         Basic shape annotation                 */                fill: 'rgba(0, 0, 0, 0.75)',                /**                 * The type of the shape, e.g. circle or rectangle.                 *                 * @type {string}                 * @sample highcharts/annotations/shape/                 *         Basic shape annotation                 * @default 'rect'                 * @apioption annotations.crookedLine.shapeOptions.type                 */                /**                 * 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.             *             * @type {Annotation.ControlPoint.Options}             * @apioption annotations.crookedLine.controlPointOptions             */            controlPointOptions: {                symbol: 'circle',                width: 10,                height: 10,                style: {                    stroke: 'black',                    'stroke-width': 2,                    fill: 'white'                },                visible: false,                /**                 * @function {Annotation.ControlPoint.Positioner}                 * @apioption annotations.crookedLine.controlPointOptions.positioner                 */                events: {}            },            /**             * @type {Object}             */            events: {},            /**             * The Z index of the annotation.             *             * @type {number}             * @default 6             */            zIndex: 6        },        /**         * Initialize the annotation.         *         * @param {Highcharts.Chart} - the chart         * @param {AnnotationOptions} - the user options for the annotation         */        init: function () {            this.linkPoints();            this.addControlPoints();            this.addShapes();            this.addLabels();            this.addClipPaths();            this.setLabelCollector();        },        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;        },        addShapes: function () {            (this.options.shapes || []).forEach(function (shapeOptions, i) {                var shape = this.initShape(shapeOptions, i);                this.options.shapes[i] = shape.options;            }, this);        },        addLabels: function () {            (this.options.labels || []).forEach(function (labelOptions, i) {                var label = this.initLabel(labelOptions, i);                this.options.labels[i] = label.options;            }, this);        },        addClipPaths: function () {            this.setClipAxes();            if (this.clipXAxis && this.clipYAxis) {                this.clipRect = this.chart.renderer.clipRect(                    this.getClipBox()                );            }        },        setClipAxes: function () {            var xAxes = this.chart.xAxis,                yAxes = this.chart.yAxis,                linkedAxes = reduce(                    (this.options.labels || [])                        .concat(this.options.shapes || []),                    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];        },        getClipBox: function () {            return {                x: this.clipXAxis.left,                y: this.clipYAxis.top,                width: this.clipXAxis.width,                height: this.clipYAxis.height            };        },        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.         *         * @param {AnnotationOptions} - user options for an annotation         */        setOptions: function (userOptions) {            this.options = merge(this.defaultOptions, userOptions);        },        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);        },        /**         * @param {Array<(Annotation.Label|Annotation.Shape)>} items         * @param {boolean} [animation]         */        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);            }        },        render: function () {            var renderer = this.chart.renderer;            this.graphic = renderer                .g('annotation')                .attr({                    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);            if (this.clipRect) {                this.graphic.clip(this.clipRect);            }            this.addEvents();            controllableMixin.render.call(this);        },        /**         * Set the annotation's visibility.         *         * @param {Boolean} [visible] - Whether to show or hide an annotation.         * If the param is omitted, the annotation's visibility is toggled.         */        setVisibility: function (visibility) {            var options = this.options,                visible = pick(visibility, !options.visible);            this.graphic.attr(                'visibility',                visible ? 'visible' : 'hidden'            );            if (!visible) {                this.setControlPointsVisibility(false);            }            options.visible = visible;        },        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.         */        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.Annotation#destroy}.         */        remove: function () {            return this.destroy();        },        update: function (userOptions) {            var chart = this.chart,                labelsAndShapes = this.getLabelsAndShapesOptions(                    this.userOptions,                    userOptions                ),                userOptionsIndex = chart.annotations.indexOf(this),                options = H.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.redraw();        },        /* *************************************************************         * ITEM SECTION         * Contains methods for handling a single item in an annotation         **************************************************************** */        /**         * Initialisation of a single shape         *         * @param {Object} shapeOptions - a confg object for a single shape         **/        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         *         * @param {Object} labelOptions         **/        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.         *         * @param {Annotation.Label|Annotation.Shape} item         * @param {boolean} [animation]         */        redrawItem: function (item, animation) {            item.linkPoints();            if (!item.shouldBeDrawn()) {                this.destroyItem(item);            } else {                if (!item.graphic) {                    this.renderItem(item);                }                item.redraw(                    H.pick(animation, true) && item.graphic.placed                );                if (item.points.length) {                    this.adjustVisibility(item);                }            }        },        /**         * Hide or show annotaiton attached to points.         *         * @param {Annotation.Label|Annotation.Shape} item         */        adjustVisibility: function (item) { // #9481            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.         *         * @param {Annotation.Label|Annotation.Shape} item         */        destroyItem: function (item) {            // erase from shapes or labels array            erase(this[item.itemType + 's'], item);            item.destroy();        },        /*         * @private         */        renderItem: function (item) {            item.render(                item.itemType === 'label' ?                    this.labelsGroup :                    this.shapesGroup            );        }    });/** * 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};Annotation.types = {};Annotation.MockPoint = MockPoint;Annotation.ControlPoint = ControlPoint;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 * ******************************************************************** */// Let chart.update() work with annotationsH.Chart.prototype.collectionsWithUpdate.push('annotations');H.extend(H.Chart.prototype, /** @lends Highcharts.Chart# */ {    initAnnotation: function (userOptions) {        var Constructor =            Annotation.types[userOptions.type] || Annotation,            options = H.merge(                Constructor.prototype.defaultOptions,                userOptions            ),            annotation = new Constructor(this, options);        this.annotations.push(annotation);        return annotation;    },    /**     * Add an annotation to the chart after render time.     *     * @param  {AnnotationOptions} 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();        }        return annotation;    },    /**     * Remove an annotation from the chart.     *     * @param {String|Annotation} idOrAnnotation - The annotation's id or     *      direct annotation object.     */    removeAnnotation: function (idOrAnnotation) {        var annotations = this.annotations,            annotation = isString(idOrAnnotation) ? find(                annotations,                function (annotation) {                    return annotation.options.id === idOrAnnotation;                }            ) : idOrAnnotation;        if (annotation) {            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();        });    }});H.Chart.prototype.callbacks.push(function (chart) {    chart.annotations = [];    if (!chart.options.annotations) {        chart.options.annotations = [];    }    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) {        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();    });});
 |