PageSourceSearch

https://www.grimaud-provence.com/app/dist/grimaud/addons/woody-lib…/code/es-modules/Core/Series/Series.js

js grimaud-provence.com collected 2026-09-26 04:49:14 UTC 148,780 bytes, 3,727 lines download raw bytes

1/* *
2 *
3 *  (c) 2010-2024 Torstein Honsi
4 *
5 *  License: www.highcharts.com/license
6 *
7 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
8 *
9 * */
10'use strict';
11import A from '../Animation/AnimationUtilities.js';
12const { animObject, setAnimation } = A;
13import DataTableCore from '../../Data/DataTableCore.js';
14import D from '../Defaults.js';
15const { defaultOptions } = D;
16import F from '../Foundation.js';
17const { registerEventOptions } = F;
18import H from '../Globals.js';
19const { svg, win } = H;
20import LegendSymbol from '../Legend/LegendSymbol.js';
21import Point from './Point.js';
22import SeriesDefaults from './SeriesDefaults.js';
23import SeriesRegistry from './SeriesRegistry.js';
24const { seriesTypes } = SeriesRegistry;
25import SVGElement from '../Renderer/SVG/SVGElement.js';
26import U from '../Utilities.js';
27const { arrayMax, arrayMin, clamp, correctFloat, crisp, defined, destroyObjectProperties, diffObjects, erase, error, extend, find, fireEvent, getClosestDistance, getNestedProperty, insertItem, isArray, isNumber, isString, merge, objectEach, pick, removeEvent, syncTimeout } = U;
28/* *
29 *
30 *  Class
31 *
32 * */
33/**
34 * This is the base series prototype that all other series types inherit from.
35 * A new series is initialized either through the
36 * [series](https://api.highcharts.com/highcharts/series)
37 * option structure, or after the chart is initialized, through
38 * {@link Highcharts.Chart#addSeries}.
39 *
40 * The object can be accessed in a number of ways. All series and point event
41 * handlers give a reference to the `series` object. The chart object has a
42 * {@link Highcharts.Chart#series|series} property that is a collection of all
43 * the chart's series. The point objects and axis objects also have the same
44 * reference.
45 *
46 * Another way to reference the series programmatically is by `id`. Add an id
47 * in the series configuration options, and get the series object by
48 * {@link Highcharts.Chart#get}.
49 *
50 * Configuration options for the series are given in three levels. Options for
51 * all series in a chart are given in the
52 * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
53 * object. Then options for all series of a specific type
54 * are given in the plotOptions of that type, for example `plotOptions.line`.
55 * Next, options for one single series are given in the series array, or as
56 * arguments to `chart.addSeries`.
57 *
58 * The data in the series is stored in various arrays.
59 *
60 * - First, `series.options.data` contains all the original config options for
61 *   each point whether added by options or methods like `series.addPoint`.
62 *
63 * - The `series.dataTable` refers to an instance of [DataTableCore](https://api.highcharts.com/class-reference/Highcharts.Data)
64 *   or `DataTable` that contains the data in a tabular format. Individual
65 *   columns can be read from `series.getColumn()`.
66 *
67 * - Next, `series.data` contains those values converted to points, but in case
68 *   the series data length exceeds the `cropThreshold`, or if the data is
69 *   grouped, `series.data` doesn't contain all the points. It only contains the
70 *   points that have been created on demand.
71 *
72 * - Then there's `series.points` that contains all currently visible point
73 *   objects. In case of cropping, the cropped-away points are not part of this
74 *   array. The `series.points` array starts at `series.cropStart` compared to
75 *   `series.data` and `series.options.data`. If however the series data is
76 *   grouped, these can't be correlated one to one.
77 *
78 * @class
79 * @name Highcharts.Series
80 *
81 * @param {Highcharts.Chart} chart
82 * The chart instance.
83 *
84 * @param {Highcharts.SeriesOptionsType|object} options
85 * The series options.
86 */
87class Series {
88    constructor() {
89        /* *
90         *
91         *  Static Properties
92         *
93         * */
94        this.zoneAxis = 'y';
95        // eslint-enable valid-jsdoc
96    }
97    /* *
98     *
99     *  Functions
100     *
101     * */
102    /* eslint-disable valid-jsdoc */
103    init(chart, userOptions) {
104        fireEvent(this, 'init', { options: userOptions });
105        // Create the data table
106        this.dataTable ?? (this.dataTable = new DataTableCore());
107        const series = this, chartSeries = chart.series;
108        // The 'eventsToUnbind' property moved from prototype into the
109        // Series init to avoid reference to the same array between
110        // the different series and charts. #12959, #13937
111        this.eventsToUnbind = [];
112        /**
113         * Read only. The chart that the series belongs to.
114         *
115         * @name Highcharts.Series#chart
116         * @type {Highcharts.Chart}
117         */
118        series.chart = chart;
119        /**
120         * Read only. The series' type, like "line", "area", "column" etc.
121         * The type in the series options anc can be altered using
122         * {@link Series#update}.
123         *
124         * @name Highcharts.Series#type
125         * @type {string}
126         */
127        /**
128         * Read only. The series' current options. To update, use
129         * {@link Series#update}.
130         *
131         * @name Highcharts.Series#options
132         * @type {Highcharts.SeriesOptionsType}
133         */
134        series.options = series.setOptions(userOptions);
135        const options = series.options, visible = options.visible !== false;
136        /**
137         * All child series that are linked to the current series through the
138         * [linkedTo](https://api.highcharts.com/highcharts/series.line.linkedTo)
139         * option.
140         *
141         * @name Highcharts.Series#linkedSeries
142         * @type {Array<Highcharts.Series>}
143         * @readonly
144         */
145        series.linkedSeries = [];
146        // Bind the axes
147        series.bindAxes();
148        extend(series, {
149            /**
150             * The series name as given in the options. Defaults to
151             * "Series {n}".
152             *
153             * @name Highcharts.Series#name
154             * @type {string}
155             */
156            name: options.name,
157            state: '',
158            /**
159             * Read only. The series' visibility state as set by {@link
160             * Series#show}, {@link Series#hide}, or in the initial
161             * configuration.
162             *
163             * @name Highcharts.Series#visible
164             * @type {boolean}
165             */
166            visible, // True by default
167            /**
168             * Read only. The series' selected state as set by {@link
169             * Highcharts.Series#select}.
170             *
171             * @name Highcharts.Series#selected
172             * @type {boolean}
173             */
174            selected: options.selected === true // False by default
175        });
176        registerEventOptions(this, options);
177        const events = options.events;
178        if ((events && events.click) ||
179            (options.point &&
180                options.point.events &&
181                options.point.events.click) ||
182            options.allowPointSelect) {
183            chart.runTrackerClick = true;
184        }
185        series.getColor();
186        series.getSymbol();
187        // Mark cartesian
188        if (series.isCartesian) {
189            chart.hasCartesianSeries = true;
190        }
191        // Get the index and register the series in the chart. The index is
192        // one more than the current latest series index (#5960).
193        let lastSeries;
194        if (chartSeries.length) {
195            lastSeries = chartSeries[chartSeries.length - 1];
196        }
197        series._i = pick(lastSeries && lastSeries._i, -1) + 1;
198        series.opacity = series.options.opacity;
199        // Insert the series and re-order all series above the insertion
200        // point.
201        chart.orderItems('series', insertItem(this, chartSeries));
202        // Set options for series with sorting and set data later.
203        if (options.dataSorting && options.dataSorting.enabled) {
204            series.setDataSortingOptions();
205        }
206        else if (!series.points && !series.data) {
207            series.setData(options.data, false);
208        }
209        fireEvent(this, 'afterInit');
210    }
211    /**
212     * Check whether the series item is itself or inherits from a certain
213     * series type.
214     *
215     * @function Highcharts.Series#is
216     * @param {string} type The type of series to check for, can be either
217     *        featured or custom series types. For example `column`, `pie`,
218     *        `ohlc` etc.
219     *
220     * @return {boolean}
221     *        True if this item is or inherits from the given type.
222     */
223    is(type) {
224        return seriesTypes[type] && this instanceof seriesTypes[type];
225    }
226    /**
227     * Set the xAxis and yAxis properties of cartesian series, and register
228     * the series in the `axis.series` array.
229     *
230     * @private
231     * @function Highcharts.Series#bindAxes
232     */
233    bindAxes() {
234        const series = this, seriesOptions = series.options, chart = series.chart;
235        let axisOptions;
236        fireEvent(this, 'bindAxes', null, function () {
237            // Repeat for xAxis and yAxis
238            (series.axisTypes || []).forEach(function (coll) {
239                // Loop through the chart's axis objects
240                (chart[coll] || []).forEach(function (axis) {
241                    axisOptions = axis.options;
242                    // Apply if the series xAxis or yAxis option matches
243                    // the number of the axis, or if undefined, use the
244                    // first axis
245                    if (pick(seriesOptions[coll], 0) === axis.index ||
246                        (typeof seriesOptions[coll] !==
247                            'undefined' &&
248                            seriesOptions[coll] === axisOptions.id)) {
249                        // Register this series in the axis.series lookup
250                        insertItem(series, axis.series);
251                        // Set this series.xAxis or series.yAxis reference
252                        /**
253                         * Read only. The unique xAxis object associated
254                         * with the series.
255                         *
256                         * @name Highcharts.Series#xAxis
257                         * @type {Highcharts.Axis}
258                         */
259                        /**
260                         * Read only. The unique yAxis object associated
261                         * with the series.
262                         *
263                         * @name Highcharts.Series#yAxis
264                         * @type {Highcharts.Axis}
265                         */
266                        series[coll] = axis;
267                        // Mark dirty for redraw
268                        axis.isDirty = true;
269                    }
270                });
271                // The series needs an X and an Y axis
272                if (!series[coll] &&
273                    series.optionalAxis !== coll) {
274                    error(18, true, chart);
275                }
276            });
277        });
278        fireEvent(this, 'afterBindAxes');
279    }
280    /**
281     * Define hasData functions for series. These return true if there
282     * are data points on this series within the plot area.
283     *
284     * @private
285     * @function Highcharts.Series#hasData
286     */
287    hasData() {
288        return ((this.visible &&
289            typeof this.dataMax !== 'undefined' &&
290            typeof this.dataMin !== 'undefined') || ( // #3703
291        this.visible &&
292            this.dataTable.rowCount > 0 // #9758
293        ));
294    }
295    /**
296     * Determine whether the marker in a series has changed.
297     *
298     * @private
299     * @function Highcharts.Series#hasMarkerChanged
300     */
301    hasMarkerChanged(options, oldOptions) {
302        const marker = options.marker, oldMarker = oldOptions.marker || {};
303        return marker && ((oldMarker.enabled && !marker.enabled) ||
304            oldMarker.symbol !== marker.symbol || // #10870, #15946
305            oldMarker.height !== marker.height || // #16274
306            oldMarker.width !== marker.width // #16274
307        );
308    }
309    /**
310     * Return an auto incremented x value based on the pointStart and
311     * pointInterval options. This is only used if an x value is not given
312     * for the point that calls autoIncrement.
313     *
314     * @private
315     * @function Highcharts.Series#autoIncrement
316     */
317    autoIncrement(x) {
318        const options = this.options, { pointIntervalUnit, relativeXValue } = this.options, time = this.chart.time, xIncrement = this.xIncrement ??
319            time.parse(options.pointStart) ??
320            0;
321        let pointInterval;
322        this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
323        if (relativeXValue && isNumber(x)) {
324            pointInterval *= x;
325        }
326        // Added code for pointInterval strings
327        if (pointIntervalUnit) {
328            const d = time.toParts(xIncrement);
329            if (pointIntervalUnit === 'day') {
330                d[2] += pointInterval;
331            }
332            else if (pointIntervalUnit === 'month') {
333                d[1] += pointInterval;
334            }
335            else if (pointIntervalUnit === 'year') {
336                d[0] += pointInterval;
337            }
338            pointInterval = time.makeTime.apply(time, d) - xIncrement;
339        }
340        if (relativeXValue && isNumber(x)) {
341            return xIncrement + pointInterval;
342        }
343        this.xIncrement = xIncrement + pointInterval;
344        return xIncrement;
345    }
346    /**
347     * Internal function to set properties for series if data sorting is
348     * enabled.
349     *
350     * @private
351     * @function Highcharts.Series#setDataSortingOptions
352     */
353    setDataSortingOptions() {
354        const options = this.options;
355        extend(this, {
356            requireSorting: false,
357            sorted: false,
358            enabledDataSorting: true,
359            allowDG: false
360        });
361        // To allow unsorted data for column series.
362        if (!defined(options.pointRange)) {
363            options.pointRange = 1;
364        }
365    }
366    /**
367     * Set the series options by merging from the options tree. Called
368     * internally on initializing and updating series. This function will
369     * not redraw the series. For API usage, use {@link Series#update}.
370     * @private
371     * @function Highcharts.Series#setOptions
372     * @param {Highcharts.SeriesOptionsType} itemOptions
373     * The series options.
374     * @emits Highcharts.Series#event:afterSetOptions
375     */
376    setOptions(itemOptions) {
377        const chart = this.chart, chartOptions = chart.options, plotOptions = chartOptions.plotOptions, userOptions = chart.userOptions || {}, seriesUserOptions = merge(itemOptions), styledMode = chart.styledMode, e = {
378            plotOptions: plotOptions,
379            userOptions: seriesUserOptions
380        };
381        let zone;
382        fireEvent(this, 'setOptions', e);
383        // These may be modified by the event
384        const typeOptions = e.plotOptions[this.type], userPlotOptions = (userOptions.plotOptions || {}), userPlotOptionsSeries = userPlotOptions.series || {}, defaultPlotOptionsType = (defaultOptions.plotOptions[this.type] || {}), userPlotOptionsType = userPlotOptions[this.type] || {};
385        // Use copy to prevent undetected changes (#9762)
386        /**
387         * Contains series options by the user without defaults.
388         * @name Highcharts.Series#userOptions
389         * @type {Highcharts.SeriesOptionsType}
390         */
391        this.userOptions = e.userOptions;
392        const options = merge(typeOptions, plotOptions.series, 
393        // #3881, chart instance plotOptions[type] should trump
394        // plotOptions.series
395        userPlotOptionsType, seriesUserOptions);
396        // The tooltip options are merged between global and series specific
397        // options. Importance order asscendingly:
398        // globals: (1)tooltip, (2)plotOptions.series,
399        // (3)plotOptions[this.type]
400        // init userOptions with possible later updates: 4-6 like 1-3 and
401        // (7)this series options
402        this.tooltipOptions = merge(defaultOptions.tooltip, // 1
403        defaultOptions.plotOptions.series?.tooltip, // 2
404        defaultPlotOptionsType?.tooltip, // 3
405        chart.userOptions.tooltip, // 4
406        userPlotOptions.series?.tooltip, // 5
407        userPlotOptionsType.tooltip, // 6
408        seriesUserOptions.tooltip // 7
409        );
410        // When shared tooltip, stickyTracking is true by default,
411        // unless user says otherwise.
412        this.stickyTracking = pick(seriesUserOptions.stickyTracking, userPlotOptionsType.stickyTracking, userPlotOptionsSeries.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
413            true :
414            options.stickyTracking));
415        // Delete marker object if not allowed (#1125)
416        if (typeOptions.marker === null) {
417            delete options.marker;
418        }
419        // Handle color zones
420        this.zoneAxis = options.zoneAxis || 'y';
421        const zones = this.zones = // #20440, create deep copy of zones options
422            (options.zones || []).map((z) => ({ ...z }));
423        if ((options.negativeColor || options.negativeFillColor) &&
424            !options.zones) {
425            zone = {
426                value: options[this.zoneAxis + 'Threshold'] ||
427                    options.threshold ||
428                    0,
429                className: 'highcharts-negative'
430            };
431            if (!styledMode) {
432                zone.color = options.negativeColor;
433                zone.fillColor = options.negativeFillColor;
434            }
435            zones.push(zone);
436        }
437        // Push one extra zone for the rest
438        if (zones.length && defined(zones[zones.length - 1].value)) {
439            zones.push(styledMode ? {} : {
440                color: this.color,
441                fillColor: this.fillColor
442            });
443        }
444        fireEvent(this, 'afterSetOptions', { options: options });
445        return options;
446    }
447    /**
448     * Return series name in "Series {Number}" format or the one defined by
449     * a user. This method can be simply overridden as series name format
450     * can vary (e.g. technical indicators).
451     *
452     * @function Highcharts.Series#getName
453     *
454     * @return {string}
455     * The series name.
456     */
457    getName() {
458        // #4119
459        return pick(this.options.name, 'Series ' + (this.index + 1));
460    }
461    /**
462     * @private
463     * @function Highcharts.Series#getCyclic
464     */
465    getCyclic(prop, value, defaults) {
466        const chart = this.chart, indexName = `${prop}Index`, counterName = `${prop}Counter`, len = (
467        // Symbol count
468        defaults?.length ||
469            // Color count
470            chart.options.chart.colorCount);
471        let i, setting;
472        if (!value) {
473            // Pick up either the colorIndex option, or the series.colorIndex
474            // after Series.update()
475            setting = pick(prop === 'color' ? this.options.colorIndex : void 0, this[indexName]);
476            if (defined(setting)) { // After Series.update()
477                i = setting;
478            }
479            else {
480                // #6138
481                if (!chart.series.length) {
482                    chart[counterName] = 0;
483                }
484                i = chart[counterName] % len;
485                chart[counterName] += 1;
486            }
487            if (defaults) {
488                value = defaults[i];
489            }
490        }
491        // Set the colorIndex
492        if (typeof i !== 'undefined') {
493            this[indexName] = i;
494        }
495        this[prop] = value;
496    }
497    /**
498     * Get the series' color based on either the options or pulled from
499     * global options.
500     *
501     * @private
502     * @function Highcharts.Series#getColor
503     */
504    getColor() {
505        if (this.chart.styledMode) {
506            this.getCyclic('color');
507        }
508        else if (this.options.colorByPoint) {
509            this.color = "#cccccc" /* Palette.neutralColor20 */;
510        }
511        else {
512            this.getCyclic('color', this.options.color ||
513                defaultOptions.plotOptions[this.type].color, this.chart.options.colors);
514        }
515    }
516    /**
517     * Get all points' instances created for this series.
518     *
519     * @private
520     * @function Highcharts.Series#getPointsCollection
521     */
522    getPointsCollection() {
523        return (this.hasGroupedData ? this.points : this.data) || [];
524    }
525    /**
526     * Get the series' symbol based on either the options or pulled from
527     * global options.
528     *
529     * @private
530     * @function Highcharts.Series#getSymbol
531     */
532    getSymbol() {
533        const seriesMarkerOption = this.options.marker;
534        this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
535    }
536    /**
537     * Shorthand to get one of the series' data columns from `Series.dataTable`.
538     *
539     * @private
540     * @function Highcharts.Series#getColumn
541     */
542    getColumn(columnName, modified) {
543        return (modified ? this.dataTable.modified : this.dataTable)
544            .getColumn(columnName, true) || [];
545    }
546    /**
547     * Finds the index of an existing point that matches the given point
548     * options.
549     *
550     * @private
551     * @function Highcharts.Series#findPointIndex
552     * @param {Highcharts.PointOptionsObject} optionsObject
553     * The options of the point.
554     * @param {number} fromIndex
555     * The index to start searching from, used for optimizing series with
556     * required sorting.
557     * @return {number|undefined}
558     * Returns the index of a matching point, or undefined if no match is found.
559     */
560    findPointIndex(optionsObject, fromIndex) {
561        const id = optionsObject.id, x = optionsObject.x, oldData = this.points, dataSorting = this.options.dataSorting;
562        let matchingPoint, matchedById, pointIndex;
563        if (id) {
564            const item = this.chart.get(id);
565            if (item instanceof Point) {
566                matchingPoint = item;
567            }
568        }
569        else if (this.linkedParent ||
570            this.enabledDataSorting ||
571            this.options.relativeXValue) {
572            let matcher = (oldPoint) => !oldPoint.touched &&
573                oldPoint.index === optionsObject.index;
574            if (dataSorting && dataSorting.matchByName) {
575                matcher = (oldPoint) => !oldPoint.touched &&
576                    oldPoint.name === optionsObject.name;
577            }
578            else if (this.options.relativeXValue) {
579                matcher = (oldPoint) => !oldPoint.touched &&
580                    oldPoint.options.x === optionsObject.x;
581            }
582            matchingPoint = find(oldData, matcher);
583            // Add unmatched point as a new point
584            if (!matchingPoint) {
585                return void 0;
586            }
587        }
588        if (matchingPoint) {
589            pointIndex = matchingPoint && matchingPoint.index;
590            if (typeof pointIndex !== 'undefined') {
591                matchedById = true;
592            }
593        }
594        // Search for the same X in the existing data set
595        if (typeof pointIndex === 'undefined' && isNumber(x)) {
596            pointIndex = this.getColumn('x').indexOf(x, fromIndex);
597        }
598        // Reduce pointIndex if data is cropped
599        if (pointIndex !== -1 &&
600            typeof pointIndex !== 'undefined' &&
601            this.cropped) {
602            pointIndex = (pointIndex >= this.cropStart) ?
603                pointIndex - this.cropStart : pointIndex;
604        }
605        if (!matchedById &&
606            isNumber(pointIndex) &&
607            oldData[pointIndex] && oldData[pointIndex].touched) {
608            pointIndex = void 0;
609        }
610        return pointIndex;
611    }
612    /**
613     * Internal function called from setData. If the point count is the same
614     * as it was, or if there are overlapping X values, just run
615     * Point.update which is cheaper, allows animation, and keeps references
616     * to points. This also allows adding or removing points if the X-es
617     * don't match.
618     *
619     * @private
620     * @function Highcharts.Series#updateData
621     */
622    updateData(data, animation) {
623        const options = this.options, dataSorting = options.dataSorting, oldData = this.points, pointsToAdd = [], requireSorting = this.requireSorting, equalLength = data.length === oldData.length;
624        let hasUpdatedByKey, i, point, lastIndex, succeeded = true;
625        this.xIncrement = null;
626        // Iterate the new data
627        data.forEach(function (pointOptions, i) {
628            const optionsObject = (defined(pointOptions) &&
629                this.pointClass.prototype.optionsToObject.call({ series: this }, pointOptions)) || {};
630            let pointIndex;
631            // Get the x of the new data point
632            const x = optionsObject.x, id = optionsObject.id;
633            if (id || isNumber(x)) {
634                pointIndex = this.findPointIndex(optionsObject, lastIndex);
635                // Matching X not found
636                // or used already due to ununique x values (#8995),
637                // add point (but later)
638                if (pointIndex === -1 ||
639                    typeof pointIndex === 'undefined') {
640                    pointsToAdd.push(pointOptions);
641                    // Matching X found, update
642                }
643                else if (oldData[pointIndex] &&
644                    pointOptions !== options.data[pointIndex]) {
645                    oldData[pointIndex].update(pointOptions, false, null, false);
646                    // Mark it touched, below we will remove all points that
647                    // are not touched.
648                    oldData[pointIndex].touched = true;
649                    // Speed optimize by only searching after last known
650                    // index. Performs ~20% bettor on large data sets.
651                    if (requireSorting) {
652                        lastIndex = pointIndex + 1;
653                    }
654                    // Point exists, no changes, don't remove it
655                }
656                else if (oldData[pointIndex]) {
657                    oldData[pointIndex].touched = true;
658                }
659                // If the length is equal and some of the nodes had a
660                // match in the same position, we don't want to remove
661                // non-matches.
662                if (!equalLength ||
663                    i !== pointIndex ||
664                    (dataSorting && dataSorting.enabled) ||
665                    this.hasDerivedData) {
666                    hasUpdatedByKey = true;
667                }
668            }
669            else {
670                // Gather all points that are not matched
671                pointsToAdd.push(pointOptions);
672            }
673        }, this);
674        // Remove points that don't exist in the updated data set
675        if (hasUpdatedByKey) {
676            i = oldData.length;
677            while (i--) {
678                point = oldData[i];
679                if (point && !point.touched && point.remove) {
680                    point.remove(false, animation);
681                }
682            }
683            // If we did not find keys (ids or x-values), and the length is the
684            // same, update one-to-one
685        }
686        else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
687            data.forEach(function (point, i) {
688                // .update doesn't exist on a linked, hidden series (#3709)
689                // (#10187)
690                if (point !== oldData[i].y && !oldData[i].destroyed) {
691                    oldData[i].update(point, false, null, false);
692                }
693            });
694            // Don't add new points since those configs are used above
695            pointsToAdd.length = 0;
696            // Did not succeed in updating data
697        }
698        else {
699            succeeded = false;
700        }
701        oldData.forEach(function (point) {
702            if (point) {
703                point.touched = false;
704            }
705        });
706        if (!succeeded) {
707            return false;
708        }
709        // Add new points
710        pointsToAdd.forEach(function (point) {
711            this.addPoint(point, false, null, null, false);
712        }, this);
713        const xData = this.getColumn('x');
714        if (this.xIncrement === null &&
715            xData.length) {
716            this.xIncrement = arrayMax(xData);
717            this.autoIncrement();
718        }
719        return true;
720    }
721    dataColumnKeys() {
722        return ['x', ...(this.pointArrayMap || ['y'])];
723    }
724    /**
725     * Apply a new set of data to the series and optionally redraw it. The
726     * new data array is passed by reference (except in case of
727     * `updatePoints`), and may later be mutated when updating the chart
728     * data.
729     *
730     * Note the difference in behaviour when setting the same amount of
731     * points, or a different amount of points, as handled by the
732     * `updatePoints` parameter.
733     *
734     * @sample highcharts/members/series-setdata/
735     *         Set new data from a button
736     * @sample highcharts/members/series-setdata-pie/
737     *         Set data in a pie
738     * @sample stock/members/series-setdata/
739     *         Set new data in Highcharts Stock
740     * @sample maps/members/series-setdata/
741     *         Set new data in Highmaps
742     *
743     * @function Highcharts.Series#setData
744     *
745     * @param {Array<Highcharts.PointOptionsType>} data
746     *        Takes an array of data in the same format as described under
747     *        `series.{type}.data` for the given series type, for example a
748     *        line series would take data in the form described under
749     *        [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
750     *
751     * @param {boolean} [redraw=true]
752     *        Whether to redraw the chart after the series is altered. If
753     *        doing more operations on the chart, it is a good idea to set
754     *        redraw to false and call {@link Chart#redraw} after.
755     *
756     * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
757     *        When the updated data is the same length as the existing data,
758     *        points will be updated by default, and animation visualizes
759     *        how the points are changed. Set false to disable animation, or
760     *        a configuration object to set duration or easing.
761     *
762     * @param {boolean} [updatePoints=true]
763     *        When this is true, points will be updated instead of replaced
764     *        whenever possible. This occurs a) when the updated data is the
765     *        same length as the existing data, b) when points are matched
766     *        by their id's, or c) when points can be matched by X values.
767     *        This allows updating with animation and performs better. In
768     *        this case, the original array is not passed by reference. Set
769     *        `false` to prevent.
770     */
771    setData(data, redraw = true, animation, updatePoints) {
772        const series = this, oldData = series.points, oldDataLength = (oldData && oldData.length) || 0, options = series.options, chart = series.chart, dataSorting = options.dataSorting, xAxis = series.xAxis, turboThreshold = options.turboThreshold, table = this.dataTable, dataColumnKeys = this.dataColumnKeys(), pointValKey = series.pointValKey || 'y', pointArrayMap = series.pointArrayMap || [], valueCount = pointArrayMap.length, keys = options.keys;
773        let i, updatedData, indexOfX = 0, indexOfY = 1, copiedData;
774        if (!chart.options.chart.allowMutatingData) { // #4259
775            // Remove old reference
776            if (options.data) {
777                delete series.options.data;
778            }
779            if (series.userOptions.data) {
780                delete series.userOptions.data;
781            }
782            copiedData = merge(true, data);
783        }
784        data = copiedData || data || [];
785        const dataLength = data.length;
786        if (dataSorting && dataSorting.enabled) {
787            data = this.sortData(data);
788        }
789        // First try to run Point.update which is cheaper, allows animation, and
790        // keeps references to points.
791        if (chart.options.chart.allowMutatingData &&
792            updatePoints !== false &&
793            dataLength &&
794            oldDataLength &&
795            !series.cropped &&
796            !series.hasGroupedData &&
797            series.visible &&
798            // Soft updating has no benefit in boost, and causes JS error
799            // (#8355)
800            !series.boosted) {
801            updatedData = this.updateData(data, animation);
802        }
803        if (!updatedData) {
804            // Reset properties
805            series.xIncrement = null;
806            series.colorCounter = 0; // For series with colorByPoint (#1547)
807            // In turbo mode, look for one- or twodimensional arrays of numbers.
808            // The first and the last valid value are tested, and we assume that
809            // all the rest are defined the same way. Although the 'for' loops
810            // are similar, they are repeated inside each if-else conditional
811            // for max performance.
812            let runTurbo = turboThreshold && dataLength > turboThreshold;
813            if (runTurbo) {
814                const firstPoint = series.getFirstValidPoint(data), lastPoint = series.getFirstValidPoint(data, dataLength - 1, -1), isShortArray = (a) => Boolean(isArray(a) && (keys || isNumber(a[0])));
815                // Assume all points are numbers
816                if (isNumber(firstPoint) && isNumber(lastPoint)) {
817                    const x = [], valueData = [];
818                    for (const value of data) {
819                        x.push(this.autoIncrement());
820                        valueData.push(value);
821                    }
822                    table.setColumns({
823                        x,
824                        [pointValKey]: valueData
825                    });
826                    // Assume all points are arrays when first point is
827                }
828                else if (isShortArray(firstPoint) &&
829                    isShortArray(lastPoint)) {
830                    if (valueCount) { // [x, low, high] or [x, o, h, l, c]
831                        // When autoX is 1, the x is skipped: [low, high]. When
832                        // autoX is 0, the x is included: [x, low, high]
833                        const autoX = firstPoint.length === valueCount ?
834                            1 : 0, colArray = new Array(dataColumnKeys.length)
835                            .fill(0).map(() => []);
836                        for (const pt of data) {
837                            if (autoX) {
838                                colArray[0].push(this.autoIncrement());
839                            }
840                            for (let j = autoX; j <= valueCount; j++) {
841                                colArray[j]?.push(pt[j - autoX]);
842                            }
843                        }
844                        table.setColumns(dataColumnKeys.reduce((columns, columnName, i) => {
845                            columns[columnName] = colArray[i];
846                            return columns;
847                        }, {}));
848                    }
849                    else { // [x, y]
850                        if (keys) {
851                            indexOfX = keys.indexOf('x');
852                            indexOfY = keys.indexOf('y');
853                            indexOfX = indexOfX >= 0 ? indexOfX : 0;
854                            indexOfY = indexOfY >= 0 ? indexOfY : 1;
855                        }
856                        if (firstPoint.length === 1) {
857                            indexOfY = 0;
858                        }
859                        const xData = [], valueData = [];
860                        if (indexOfX === indexOfY) {
861                            for (const pt of data) {
862                                xData.push(this.autoIncrement());
863                                valueData.push(pt[indexOfY]);
864                            }
865                        }
866                        else {
867                            for (const pt of data) {
868                                xData.push(pt[indexOfX]);
869                                valueData.push(pt[indexOfY]);
870                            }
871                        }
872                        table.setColumns({
873                            x: xData,
874                            [pointValKey]: valueData
875                        });
876                    }
877                }
878                else {
879                    // Highcharts expects configs to be numbers or arrays in
880                    // turbo mode
881                    runTurbo = false;
882                }
883            }
884            if (!runTurbo) {
885                const columns = dataColumnKeys.reduce((columns, columnName) => {
886                    columns[columnName] = [];
887                    return columns;
888                }, {});
889                for (i = 0; i < dataLength; i++) {
890                    const pt = series.pointClass.prototype.applyOptions.apply({ series }, [data[i]]);
891                    for (const key of dataColumnKeys) {
892                        columns[key][i] = pt[key];
893                    }
894                }
895                table.setColumns(columns);
896            }
897            // Forgetting to cast strings to numbers is a common caveat when
898            // handling CSV or JSON
899            if (isString(this.getColumn('y')[0])) {
900                error(14, true, chart);
901            }
902            series.data = [];
903            series.options.data = series.userOptions.data = data;
904            // Destroy old points
905            i = oldDataLength;
906            while (i--) {
907                oldData[i]?.destroy();
908            }
909            // Reset minRange (#878)
910            if (xAxis) {
911                xAxis.minRange = xAxis.userMinRange;
912            }
913            // Redraw
914            series.isDirty = chart.isDirtyBox = true;
915            series.isDirtyData = !!oldData;
916            animation = false;
917        }
918        // Typically for pie series, points need to be processed and
919        // generated prior to rendering the legend
920        if (options.legendType === 'point') {
921            this.processData();
922            this.generatePoints();
923        }
924        if (redraw) {
925            chart.redraw(animation);
926        }
927    }
928    /**
929     * Internal function to sort series data
930     *
931     * @private
932     * @function Highcharts.Series#sortData
933     * @param {Array<Highcharts.PointOptionsType>} data
934     * Force data grouping.
935     */
936    sortData(data) {
937        const series = this, options = series.options, dataSorting = options.dataSorting, sortKey = dataSorting.sortKey || 'y', getPointOptionsObject = function (series, pointOptions) {
938            return (defined(pointOptions) &&
939                series.pointClass.prototype.optionsToObject.call({
940                    series: series
941                }, pointOptions)) || {};
942        };
943        data.forEach(function (pointOptions, i) {
944            data[i] = getPointOptionsObject(series, pointOptions);
945            data[i].index = i;
946        }, this);
947        // Sorting
948        const sortedData = data.concat().sort((a, b) => {
949            const aValue = getNestedProperty(sortKey, a);
950            const bValue = getNestedProperty(sortKey, b);
951            return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
952        });
953        // Set x value depending on the position in the array
954        sortedData.forEach(function (point, i) {
955            point.x = i;
956        }, this);
957        // Set the same x for linked series points if they don't have their
958        // own sorting
959        if (series.linkedSeries) {
960            series.linkedSeries.forEach(function (linkedSeries) {
961                const options = linkedSeries.options, seriesData = options.data;
962                if ((!options.dataSorting ||
963                    !options.dataSorting.enabled) &&
964                    seriesData) {
965                    seriesData.forEach(function (pointOptions, i) {
966                        seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
967                        if (data[i]) {
968                            seriesData[i].x = data[i].x;
969                            seriesData[i].index = i;
970                        }
971                    });
972                    linkedSeries.setData(seriesData, false);
973                }
974            });
975        }
976        return data;
977    }
978    /**
979     * Internal function to process the data by cropping away unused data
980     * points if the series is longer than the crop threshold. This saves
981     * computing time for large series.
982     *
983     * @private
984     * @function Highcharts.Series#getProcessedData
985     * @param {boolean} [forceExtremesFromAll]
986     * Force getting extremes of a total series data range.
987     */
988    getProcessedData(forceExtremesFromAll) {
989        const series = this, { dataTable: table, isCartesian, options, xAxis } = series, cropThreshold = options.cropThreshold, getExtremesFromAll = forceExtremesFromAll ||
990            // X-range series etc, #21003
991            series.getExtremesFromAll, logarithmic = xAxis?.logarithmic, dataLength = table.rowCount;
992        let croppedData, cropped, cropStart = 0, xExtremes, min, max, xData = series.getColumn('x'), modified = table, updatingNames = false;
993        if (xAxis) {
994            // Corrected for log axis (#3053)
995            xExtremes = xAxis.getExtremes();
996            min = xExtremes.min;
997            max = xExtremes.max;
998            updatingNames = !!(xAxis.categories && !xAxis.names.length);
999            // Optionally filter out points outside the plot area
1000            if (isCartesian &&
1001                series.sorted &&
1002                !getExtremesFromAll &&
1003                (!cropThreshold ||
1004                    dataLength > cropThreshold ||
1005                    series.forceCrop)) {
1006                // It's outside current extremes
1007                if (xData[dataLength - 1] < min ||
1008                    xData[0] > max) {
1009                    modified = new DataTableCore();
1010                    // Only crop if it's actually spilling out
1011                }
1012                else if (
1013                // Don't understand why this condition is needed
1014                series.getColumn(series.pointValKey || 'y').length && (xData[0] < min ||
1015                    xData[dataLength - 1] > max)) {
1016                    croppedData = this.cropData(table, min, max);
1017                    modified = croppedData.modified;
1018                    cropStart = croppedData.start;
1019                    cropped = true;
1020                }
1021            }
1022        }
1023        // Find the closest distance between processed points
1024        xData = modified.getColumn('x') || [];
1025        const closestPointRange = getClosestDistance([
1026            logarithmic ?
1027                xData.map(logarithmic.log2lin) :
1028                xData
1029        ], 
1030        // Unsorted data is not supported by the line tooltip, as well as
1031        // data grouping and navigation in Stock charts (#725) and width
1032        // calculation of columns (#1900). Avoid warning during the
1033        // premature processing pass in updateNames (#16104).
1034        () => (series.requireSorting &&
1035            !updatingNames &&
1036            error(15, false, series.chart)));
1037        return {
1038            modified,
1039            cropped,
1040            cropStart,
1041            closestPointRange
1042        };
1043    }
1044    /**
1045     * Internal function to apply processed data.
1046     * In Highcharts Stock, this function is extended to provide data grouping.
1047     *
1048     * @private
1049     * @function Highcharts.Series#processData
1050     * @param {boolean} [force]
1051     * Force data grouping.
1052     */
1053    processData(force) {
1054        const series = this, xAxis = series.xAxis, table = series.dataTable;
1055        // If the series data or axes haven't changed, don't go through
1056        // this. Return false to pass the message on to override methods
1057        // like in data grouping.
1058        if (series.isCartesian &&
1059            !series.isDirty &&
1060            !xAxis.isDirty &&
1061            !series.yAxis.isDirty &&
1062            !force) {
1063            return false;
1064        }
1065        const processedData = series.getProcessedData();
1066        // Record the properties
1067        table.modified = processedData.modified;
1068        series.cropped = processedData.cropped; // Undefined or true
1069        series.cropStart = processedData.cropStart;
1070        series.closestPointRange = (series.basePointRange = processedData.closestPointRange);
1071        fireEvent(series, 'afterProcessData');
1072    }
1073    /**
1074     * Iterate over xData and crop values between min and max. Returns
1075     * object containing crop start/end cropped xData with corresponding
1076     * part of yData, dataMin and dataMax within the cropped range.
1077     *
1078     * @private
1079     * @function Highcharts.Series#cropData
1080     */
1081    cropData(table, min, max) {
1082        const xData = table.getColumn('x', true) || [], dataLength = xData.length, columns = {};
1083        let i, j, start = 0, end = dataLength;
1084        // Iterate up to find slice start
1085        for (i = 0; i < dataLength; i++) {
1086            if (xData[i] >= min) {
1087                start = Math.max(0, i - 1);
1088                break;
1089            }
1090        }
1091        // Proceed to find slice end
1092        for (j = i; j < dataLength; j++) {
1093            if (xData[j] > max) {
1094                end = j + 1;
1095                break;
1096            }
1097        }
1098        for (const key of this.dataColumnKeys()) {
1099            const column = table.getColumn(key, true);
1100            if (column) {
1101                columns[key] = column.slice(start, end);
1102            }
1103        }
1104        return {
1105            modified: new DataTableCore({ columns }),
1106            start,
1107            end
1108        };
1109    }
1110    /**
1111     * Generate the data point after the data has been processed by cropping
1112     * away unused points and optionally grouped in Highcharts Stock.
1113     *
1114     * @private
1115     * @function Highcharts.Series#generatePoints
1116     */
1117    generatePoints() {
1118        const series = this, options = series.options, dataOptions = series.processedData || options.data, table = series.dataTable.modified, xData = series.getColumn('x', true), PointClass = series.pointClass, processedDataLength = table.rowCount, cropStart = series.cropStart || 0, hasGroupedData = series.hasGroupedData, keys = options.keys, points = [], groupCropStartIndex = (options.dataGrouping &&
1119            options.dataGrouping.groupAll ?
1120            cropStart :
1121            0), categories = series.xAxis?.categories, pointArrayMap = series.pointArrayMap || ['y'], 
1122        // Create a configuration object out of a data row
1123        dataColumnKeys = this.dataColumnKeys();
1124        let dataLength, cursor, point, i, data = series.data, pOptions;
1125        if (!data && !hasGroupedData) {
1126            const arr = [];
1127            arr.length = dataOptions?.length || 0;
1128            data = series.data = arr;
1129        }
1130        if (keys && hasGroupedData) {
1131            // Grouped data has already applied keys (#6590)
1132            series.options.keys = false;
1133        }
1134        for (i = 0; i < processedDataLength; i++) {
1135            cursor = cropStart + i;
1136            if (!hasGroupedData) {
1137                point = data[cursor];
1138                pOptions = dataOptions ?
1139                    dataOptions[cursor] :
1140                    table.getRow(i, pointArrayMap);
1141                // #970:
1142                if (!point &&
1143                    pOptions !== void 0) {
1144                    data[cursor] = point = new PointClass(series, pOptions, xData[i]);
1145                }
1146            }
1147            else {
1148                // Splat the y data in case of ohlc data array
1149                point = new PointClass(series, table.getRow(i, dataColumnKeys) || []);
1150                point.dataGroup = series.groupMap[groupCropStartIndex + i];
1151                if (point.dataGroup?.options) {
1152                    point.options = point.dataGroup.options;
1153                    extend(point, point.dataGroup.options);
1154                    // Collision of props and options (#9770)
1155                    delete point.dataLabels;
1156                }
1157            }
1158            if (point) { // #6279
1159                /**
1160                 * Contains the point's index in the `Series.points` array.
1161                 *
1162                 * @name Highcharts.Point#index
1163                 * @type {number}
1164                 * @readonly
1165                 */
1166                // For faster access in Point.update
1167                point.index = hasGroupedData ?
1168                    (groupCropStartIndex + i) : cursor;
1169                points[i] = point;
1170                // Set point properties for convenient access in tooltip and
1171                // data labels
1172                point.category = categories?.[point.x] ?? point.x;
1173                point.key = point.name ?? point.category;
1174            }
1175        }
1176        // Restore keys options (#6590)
1177        series.options.keys = keys;
1178        // Hide cropped-away points - this only runs when the number of
1179        // points is above cropThreshold, or when switching view from
1180        // non-grouped data to grouped data (#637)
1181        if (data &&
1182            (processedDataLength !== (dataLength = data.length) ||
1183                hasGroupedData)) {
1184            for (i = 0; i < dataLength; i++) {
1185                // When has grouped data, clear all points
1186                if (i === cropStart && !hasGroupedData) {
1187                    i += processedDataLength;
1188                }
1189                if (data[i]) {
1190                    data[i].destroyElements();
1191                    data[i].plotX = void 0; // #1003
1192                }
1193            }
1194        }
1195        /**
1196         * Read only. An array containing those values converted to points.
1197         * In case the series data length exceeds the `cropThreshold`, or if
1198         * the data is grouped, `series.data` doesn't contain all the
1199         * points. Also, in case a series is hidden, the `data` array may be
1200         * empty. In case of cropping, the `data` array may contain `undefined`
1201         * values, instead of points. To access raw values,
1202         * `series.options.data` will always be up to date. `Series.data` only
1203         * contains the points that have been created on demand. To modify the
1204         * data, use
1205         * {@link Highcharts.Series#setData} or
1206         * {@link Highcharts.Point#update}.
1207         *
1208         * @see Series.points
1209         *
1210         * @name Highcharts.Series#data
1211         * @type {Array<Highcharts.Point>}
1212         */
1213        series.data = data;
1214        /**
1215         * An array containing all currently visible point objects. In case
1216         * of cropping, the cropped-away points are not part of this array.
1217         * The `series.points` array starts at `series.cropStart` compared
1218         * to `series.data` and `series.options.data`. If however the series
1219         * data is grouped, these can't be correlated one to one. To modify
1220         * the data, use {@link Highcharts.Series#setData} or
1221         * {@link Highcharts.Point#update}.
1222         *
1223         * @name Highcharts.Series#points
1224         * @type {Array<Highcharts.Point>}
1225         */
1226        series.points = points;
1227        fireEvent(this, 'afterGeneratePoints');
1228    }
1229    /**
1230     * Get current X extremes for the visible data.
1231     *
1232     * @private
1233     * @function Highcharts.Series#getXExtremes
1234     * @param {Array<number>} xData
1235     * The data to inspect. Defaults to the current data within the visible
1236     * range.
1237     */
1238    getXExtremes(xData) {
1239        return {
1240            min: arrayMin(xData),
1241            max: arrayMax(xData)
1242        };
1243    }
1244    /**
1245     * Calculate Y extremes for the visible data. The result is returned
1246     * as an object with `dataMin` and `dataMax` properties.
1247     *
1248     * @private
1249     * @function Highcharts.Series#getExtremes
1250     * @param {Array<number>} [yData]
1251     * The data to inspect. Defaults to the current data within the visible
1252     * range.
1253     * @param {boolean} [forceExtremesFromAll]
1254     * Force getting extremes of a total series data range.
1255     */
1256    getExtremes(yData, forceExtremesFromAll) {
1257        const { xAxis, yAxis } = this, getExtremesFromAll = forceExtremesFromAll ||
1258            this.getExtremesFromAll ||
1259            this.options.getExtremesFromAll, // #4599, #21003
1260        table = getExtremesFromAll && this.cropped ?
1261            this.dataTable :
1262            this.dataTable.modified, rowCount = table.rowCount, customData = yData || this.stackedYData, yAxisData = customData ?
1263            [customData] :
1264            (this.keysAffectYAxis || this.pointArrayMap || ['y'])?.map((key) => table.getColumn(key, true) || []) || [], xData = this.getColumn('x', true), activeYData = [], 
1265        // Handle X outside the viewed area. This does not work with
1266        // non-sorted data like scatter (#7639).
1267        shoulder = this.requireSorting && !this.is('column') ?
1268            1 : 0, 
1269        // #2117, need to compensate for log X axis
1270        positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false, doAll = getExtremesFromAll ||
1271            this.cropped ||
1272            !xAxis;
vendor: 4,662 bytes, lines 1272-1387
1272 // For colorAxis support
1273        let xExtremes, x, i, xMin = 0, xMax = 0;
1274        if (xAxis) {
1275            xExtremes = xAxis.getExtremes();
1276            xMin = xExtremes.min;
1277            xMax = xExtremes.max;
1278        }
1279        for (i = 0; i < rowCount; i++) {
1280            x = xData[i];
1281            // Check if it is within the selected x axis range
1282            if (doAll ||
1283                ((xData[i + shoulder] || x) >= xMin &&
1284                    (xData[i - shoulder] || x) <= xMax)) {
1285                for (const values of yAxisData) {
1286                    const val = values[i];
1287                    // For points within the visible range, including the
1288                    // first point outside the visible range (#7061),
1289                    // consider y extremes.
1290                    if (isNumber(val) &&
1291                        (val > 0 || !positiveValuesOnly)) {
1292                        activeYData.push(val);
1293                    }
1294                }
1295            }
1296        }
1297        const dataExtremes = {
1298            activeYData, // Needed for Stock Cumulative Sum
1299            dataMin: arrayMin(activeYData),
1300            dataMax: arrayMax(activeYData)
1301        };
1302        fireEvent(this, 'afterGetExtremes', { dataExtremes });
1303        return dataExtremes;
1304    }
1305    /**
1306     * Set the current data extremes as `dataMin` and `dataMax` on the
1307     * Series item. Use this only when the series properties should be
1308     * updated.
1309     *
1310     * @private
1311     * @function Highcharts.Series#applyExtremes
1312     */
1313    applyExtremes() {
1314        const dataExtremes = this.getExtremes();
1315        /**
1316         * Contains the minimum value of the series' data point. Some series
1317         * types like `networkgraph` do not support this property as they
1318         * lack a `y`-value.
1319         * @name Highcharts.Series#dataMin
1320         * @type {number|undefined}
1321         * @readonly
1322         */
1323        this.dataMin = dataExtremes.dataMin;
1324        /**
1325         * Contains the maximum value of the series' data point. Some series
1326         * types like `networkgraph` do not support this property as they
1327         * lack a `y`-value.
1328         * @name Highcharts.Series#dataMax
1329         * @type {number|undefined}
1330         * @readonly
1331         */
1332        this.dataMax = dataExtremes.dataMax;
1333        return dataExtremes;
1334    }
1335    /**
1336     * Find and return the first non nullish point in the data
1337     *
1338     * @private
1339     * @function Highcharts.Series.getFirstValidPoint
1340     * @param {Array<Highcharts.PointOptionsType>} data
1341     *        Array of options for points
1342     * @param {number} [start=0]
1343     *        Index to start searching from
1344     * @param {number} [increment=1]
1345     *        Index increment, set -1 to search backwards
1346     */
1347    getFirstValidPoint(data, start = 0, increment = 1) {
1348        const dataLength = data.length;
1349        let i = start;
1350        while (i >= 0 && i < dataLength) {
1351            if (defined(data[i])) {
1352                return data[i];
1353            }
1354            i += increment;
1355        }
1356    }
1357    /**
1358     * Translate data points from raw data values to chart specific
1359     * positioning data needed later in the `drawPoints` and `drawGraph`
1360     * functions. This function can be overridden in plugins and custom
1361     * series type implementations.
1362     *
1363     * @function Highcharts.Series#translate
1364     *
1365     * @emits Highcharts.Series#events:translate
1366     */
1367    translate() {
1368        this.generatePoints();
1369        const series = this, options = series.options, stacking = options.stacking, xAxis = series.xAxis, enabledDataSorting = series.enabledDataSorting, yAxis = series.yAxis, points = series.points, dataLength = points.length, pointPlacement = series.pointPlacementToXValue(), // #7860
1370        dynamicallyPlaced = Boolean(pointPlacement), threshold = options.threshold, stackThreshold = options.startFromThreshold ? threshold : 0;
1371        let i, plotX, lastPlotX, stackIndicator, closestPointRangePx = Number.MAX_VALUE;
1372        /**
1373         * Plotted coordinates need to be within a limited range. Drawing
1374         * too far outside the viewport causes various rendering issues
1375         * (#3201, #3923, #7555).
1376         * @private
1377         */
1378        function limitedRange(val) {
1379            return clamp(val, -1e9, 1e9);
1380        }
1381        // Translate each point
1382        for (i = 0; i < dataLength; i++) {
1383            const point = points[i], xValue = point.x;
1384            let stackItem, stackValues, yValue = point.y, lowValue = point.low;
1385            const stacks = stacking && yAxis.stacking?.stacks[(series.negStacks &&
1386                yValue <
1387                    (stackThreshold ? 0 : threshold) ?
1388                '-' :
1389                '') + series.stackKey];
1390            plotX = xAxis.translate(// #3923
1391            xValue, false, false, false, true, pointPlacement);
1392            /**
1393             * The translated X value for the point in terms of pixels. Relative
1394             * to the X axis position if the series has one, otherwise relative
1395             * to the plot area. Depending on the series type this value might
1396             * not be defined.
1397             *
1398             * In an inverted chart the x-axis is going from the bottom to the
1399             * top so the `plotX` value is the number of pixels from the bottom
1400             * of the axis.
1401             *
1402             * @see Highcharts.Point#pos
1403             * @name Highcharts.Point#plotX
1404             * @type {number|undefined}
1405             */
1406            point.plotX = isNumber(plotX) ? correctFloat(// #5236
1407            limitedRange(plotX) // #3923
1408            ) : void 0;
1409            // Calculate the bottom y value for stacked series
1410            if (stacking &&
1411                series.visible &&
1412                stacks &&
1413                stacks[xValue]) {
1414                stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
1415                if (!point.isNull && stackIndicator.key) {
1416                    stackItem = stacks[xValue];
1417                    stackValues = stackItem.points[stackIndicator.key];
1418                }
1419                if (stackItem && isArray(stackValues)) {
1420                    lowValue = stackValues[0];
1421                    yValue = stackValues[1];
1422                    if (lowValue === stackThreshold &&
1423                        stackIndicator.key === stacks[xValue].base) {
1424                        lowValue = pick(isNumber(threshold) ? threshold : yAxis.min);
1425                    }
1426                    // #1200, #1232
1427                    if (yAxis.positiveValuesOnly &&
1428                        defined(lowValue) &&
1429                        lowValue <= 0) {
1430                        lowValue = void 0;
1431                    }
1432                    point.total = point.stackTotal = pick(stackItem.total);
1433                    point.percentage = defined(point.y) && stackItem.total ?
1434                        (point.y / stackItem.total * 100) : void 0;
1435                    point.stackY = yValue;
1436                    // In case of variwide series (where widths of points are
1437                    // different in most cases), stack labels are positioned
1438                    // wrongly, so the call of the setOffset is omitted here and
1439                    // labels are correctly positioned later, at the end of the
1440                    // variwide's translate function (#10962)
1441                    if (!series.irregularWidths) {
1442                        stackItem.setOffset(series.pointXOffset || 0, series.barW || 0, void 0, void 0, void 0, series.xAxis);
1443                    }
1444                }
1445            }
1446            // Set translated yBottom or remove it
1447            point.yBottom = defined(lowValue) ?
1448                limitedRange(yAxis.translate(lowValue, false, true, false, true)) :
1449                void 0;
1450            // General hook, used for Highcharts Stock compare and cumulative
1451            if (series.dataModify) {
1452                yValue = series.dataModify.modifyValue(yValue, i);
1453            }
1454            // Set the plotY value, reset it for redraws #3201, #18422
1455            let plotY;
1456            if (isNumber(yValue) && point.plotX !== void 0) {
1457                plotY = yAxis.translate(yValue, false, true, false, true);
1458                plotY = isNumber(plotY) ? limitedRange(plotY) : void 0;
1459            }
1460            /**
1461             * The translated Y value for the point in terms of pixels. Relative
1462             * to the Y axis position if the series has one, otherwise relative
1463             * to the plot area. Depending on the series type this value might
1464             * not be defined.
1465             *
1466             * In an inverted chart the y-axis is going from right to left
1467             * so the `plotY` value is the number of pixels from the right
1468             * of the `yAxis`.
1469             *
1470             * @see Highcharts.Point#pos
1471             * @name Highcharts.Point#plotY
1472             * @type {number|undefined}
1473             */
1474            point.plotY = plotY;
1475            point.isInside = this.isPointInside(point);
1476            // Set client related positions for mouse tracking
1477            point.clientX = dynamicallyPlaced ?
1478                correctFloat(xAxis.translate(xValue, false, false, false, true, pointPlacement)) :
1479                plotX; // #1514, #5383, #5518
1480            // Negative points #19028
1481            point.negative = (point.y || 0) < (threshold || 0);
1482            // Determine auto enabling of markers (#3635, #5099)
1483            if (!point.isNull && point.visible !== false) {
1484                if (typeof lastPlotX !== 'undefined') {
1485                    closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
1486                }
1487                lastPlotX = plotX;
1488            }
1489            // Find point zone
1490            point.zone = this.zones.length ? point.getZone() : void 0;
1491            // Animate new points with data sorting
1492            if (!point.graphic && series.group && enabledDataSorting) {
1493                point.isNew = true;
1494            }
1495        }
1496        series.closestPointRangePx = closestPointRangePx;
1497        fireEvent(this, 'afterTranslate');
1498    }
1499    /**
1500     * Return the series points with null points filtered out.
1501     *
1502     * @function Highcharts.Series#getValidPoints
1503     *
1504     * @param {Array<Highcharts.Point>} [points]
1505     * The points to inspect, defaults to {@link Series.points}.
1506     *
1507     * @param {boolean} [insideOnly=false]
1508     * Whether to inspect only the points that are inside the visible view.
1509     *
1510     * @param {boolean} [allowNull=false]
1511     * Whether to allow null points to pass as valid points.
1512     *
1513     * @return {Array<Highcharts.Point>}
1514     * The valid points.
1515     */
1516    getValidPoints(points, insideOnly, allowNull) {
1517        const chart = this.chart;
1518        // #3916, #5029, #5085
1519        return (points || this.points || []).filter(function (point) {
1520            const { plotX, plotY } = point, 
1521            // Undefined plotY is treated as null when negative values
1522            // in log axis (#18422)
1523            asNull = !allowNull && (point.isNull || !isNumber(plotY));
1524            if (asNull || (insideOnly && !chart.isInsidePlot(plotX, plotY, { inverted: chart.inverted }))) {
1525                return false;
1526            }
1527            return point.visible !== false;
1528        });
1529    }
1530    /**
1531     * Get the clipping for the series. Could be called for a series to
1532     * initiate animating the clip or to set the final clip (only width
1533     * and x).
1534     *
1535     * @private
1536     * @function Highcharts.Series#getClip
1537     */
1538    getClipBox() {
1539        const { chart, xAxis, yAxis } = this;
1540        // If no axes on the series, use global clipBox
1541        let { x, y, width, height } = merge(chart.clipBox);
1542        // Otherwise, use clipBox.width which is corrected for plotBorderWidth
1543        // and clipOffset
1544        if (xAxis && xAxis.len !== chart.plotSizeX) {
1545            width = xAxis.len;
1546        }
1547        if (yAxis && yAxis.len !== chart.plotSizeY) {
1548            height = yAxis.len;
1549        }
1550        // If the chart is inverted and the series is not invertible, the chart
1551        // clip box should be inverted, but not the series clip box (#20264)
1552        if (chart.inverted && !this.invertible) {
1553            [width, height] = [height, width];
1554        }
1555        return { x, y, width, height };
1556    }
1557    /**
1558     * Get the shared clip key, creating it if it doesn't exist.
1559     *
1560     * @private
1561     * @function Highcharts.Series#getSharedClipKey
1562     */
1563    getSharedClipKey() {
1564        this.sharedClipKey = (this.options.xAxis || 0) + ',' +
1565            (this.options.yAxis || 0);
1566        return this.sharedClipKey;
1567    }
1568    /**
1569     * Set the clipping for the series. For animated series the clip is later
1570     * modified.
1571     *
1572     * @private
1573     * @function Highcharts.Series#setClip
1574     */
1575    setClip() {
1576        const { chart, group, markerGroup } = this, sharedClips = chart.sharedClips, renderer = chart.renderer, clipBox = this.getClipBox(), sharedClipKey = this.getSharedClipKey(); // #4526
1577        let clipRect = sharedClips[sharedClipKey];
1578        // If a clipping rectangle for the same set of axes does not exist,
1579        // create it
1580        if (!clipRect) {
1581            sharedClips[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
1582            // When setting chart size, or when the series is rendered again before
1583            // starting animating, in compliance to a responsive rule
1584        }
1585        else {
1586            clipRect.animate(clipBox);
1587        }
1588        if (group) {
1589            // When clip is false, reset to no clip after animation
1590            group.clip(this.options.clip === false ? void 0 : clipRect);
1591        }
1592        // Unclip temporary animation clip
1593        if (markerGroup) {
1594            markerGroup.clip();
1595        }
1596    }
1597    /**
1598     * Animate in the series. Called internally twice. First with the `init`
1599     * parameter set to true, which sets up the initial state of the
1600     * animation. Then when ready, it is called with the `init` parameter
1601     * undefined, in order to perform the actual animation.
1602     *
1603     * @function Highcharts.Series#animate
1604     *
1605     * @param {boolean} [init]
1606     * Initialize the animation.
1607     */
1608    animate(init) {
1609        const { chart, group, markerGroup } = this, inverted = chart.inverted, animation = animObject(this.options.animation), 
1610        // The key for temporary animation clips
1611        animationClipKey = [
1612            this.getSharedClipKey(),
1613            animation.duration,
1614            animation.easing,
1615            animation.defer
1616        ].join(',');
1617        let animationClipRect = chart.sharedClips[animationClipKey], markerAnimationClipRect = chart.sharedClips[animationClipKey + 'm'];
1618        // Initialize the animation. Set up the clipping rectangle.
1619        if (init && group) {
1620            const clipBox = this.getClipBox();
1621            // Create temporary animation clips
1622            if (!animationClipRect) {
1623                clipBox.width = 0;
1624                if (inverted) {
1625                    clipBox.x = chart.plotHeight;
1626                }
1627                animationClipRect = chart.renderer.clipRect(clipBox);
1628                chart.sharedClips[animationClipKey] = animationClipRect;
1629                // The marker clip box. The number 99 is a safe margin to avoid
1630                // markers being clipped during animation.
1631                const markerClipBox = {
1632                    x: inverted ? -99 : -99,
1633                    y: inverted ? -99 : -99,
1634                    width: inverted ? chart.plotWidth + 199 : 99,
1635                    height: inverted ? 99 : chart.plotHeight + 199
1636                };
1637                markerAnimationClipRect = chart.renderer.clipRect(markerClipBox);
1638                chart.sharedClips[animationClipKey + 'm'] = markerAnimationClipRect;
1639            }
1640            else {
1641                // When height changes during animation, typically due to
1642                // responsive settings
1643                animationClipRect.attr('height', clipBox.height);
1644            }
1645            group.clip(animationClipRect);
1646            markerGroup?.clip(markerAnimationClipRect);
1647            // Run the animation
1648        }
1649        else if (animationClipRect &&
1650            // Only first series in this pane
1651            !animationClipRect.hasClass('highcharts-animating')) {
1652            const finalBox = this.getClipBox(), step = animation.step;
1653            // Only do this when there are actually markers, or we have multiple
1654            // series (#20473)
1655            if (markerGroup?.element.childNodes.length ||
1656                chart.series.length > 1) {
1657                // To provide as smooth animation as possible, update the marker
1658                // group clipping in steps of the main group animation
1659                animation.step = function (val, fx) {
1660                    if (step) {
1661                        step.apply(fx, arguments);
1662                    }
1663                    if (fx.prop === 'width' &&
1664                        markerAnimationClipRect?.element) {
1665                        markerAnimationClipRect.attr(inverted ? 'height' : 'width', val + 99);
1666                    }
1667                };
1668            }
1669            animationClipRect
1670                .addClass('highcharts-animating')
1671                .animate(finalBox, animation);
1672        }
1673    }
1674    /**
1675     * This runs after animation to land on the final plot clipping.
1676     *
1677     * @private
1678     * @function Highcharts.Series#afterAnimate
1679     *
1680     * @emits Highcharts.Series#event:afterAnimate
1681     */
1682    afterAnimate() {
1683        this.setClip();
1684        // Destroy temporary clip rectangles that are no longer in use
1685        objectEach(this.chart.sharedClips, (clip, key, sharedClips) => {
1686            if (clip && !this.chart.container.querySelector(`[clip-path="url(#${clip.id})"]`)) {
1687                clip.destroy();
1688                delete sharedClips[key];
1689            }
1690        });
1691        this.finishedAnimating = true;
1692        fireEvent(this, 'afterAnimate');
1693    }
1694    /**
1695     * Draw the markers for line-like series types, and columns or other
1696     * graphical representation for {@link Point} objects for other series
1697     * types. The resulting element is typically stored as
1698     * {@link Point.graphic}, and is created on the first call and updated
1699     * and moved on subsequent calls.
1700     *
1701     * @function Highcharts.Series#drawPoints
1702     */
1703    drawPoints(points = this.points) {
1704        const series = this, chart = series.chart, styledMode = chart.styledMode, { colorAxis, options } = series, seriesMarkerOptions = options.marker, markerGroup = series[series.specialGroup || 'markerGroup'], xAxis = series.xAxis, globallyEnabled = pick(seriesMarkerOptions.enabled, !xAxis || xAxis.isRadial ? true : null, 
1705        // Use larger or equal as radius is null in bubbles (#6321)
1706        series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
1707            seriesMarkerOptions.radius));
1708        let i, point, graphic, verb, pointMarkerOptions, hasPointMarker, markerAttribs;
1709        if (seriesMarkerOptions.enabled !== false ||
1710            series._hasPointMarkers) {
1711            for (i = 0; i < points.length; i++) {
1712                point = points[i];
1713                graphic = point.graphic;
1714                verb = graphic ? 'animate' : 'attr';
1715                pointMarkerOptions = point.marker || {};
1716                hasPointMarker = !!point.marker;
1717                const shouldDrawMarker = ((globallyEnabled &&
1718                    typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
1719                // Only draw the point if y is defined
1720                if (shouldDrawMarker) {
1721                    // Shortcuts
1722                    const symbol = pick(pointMarkerOptions.symbol, series.symbol, 'rect');
1723                    markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
1724                    // Set starting position for point sliding animation.
1725                    if (series.enabledDataSorting) {
1726                        point.startXPos = xAxis.reversed ?
1727                            -(markerAttribs.width || 0) :
1728                            xAxis.width;
1729                    }
1730                    const isInside = point.isInside !== false;
1731                    if (!graphic &&
1732                        isInside &&
1733                        ((markerAttribs.width || 0) > 0 || point.hasImage)) {
1734                        /**
1735                         * SVG graphic representing the point in the chart. In
1736                         * some cases it may be a hidden graphic to improve
1737                         * accessibility.
1738                         *
1739                         * Typically this is a simple shape, like a `rect`
1740                         * for column charts or `path` for line markers, but
1741                         * for some complex series types like boxplot or 3D
1742                         * charts, the graphic may be a `g` element
1743                         * containing other shapes. The graphic is generated
1744                         * the first time {@link Series#drawPoints} runs,
1745                         * and updated and moved on subsequent runs.
1746                         *
1747                         * @see Highcharts.Point#graphics
1748                         *
1749                         * @name Highcharts.Point#graphic
1750                         * @type {Highcharts.SVGElement|undefined}
1751                         */
1752                        point.graphic = graphic = chart.renderer
1753                            .symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
1754                            pointMarkerOptions :
1755                            seriesMarkerOptions)
1756                            .add(markerGroup);
1757                        // Sliding animation for new points
1758                        if (series.enabledDataSorting &&
1759                            chart.hasRendered) {
1760                            graphic.attr({
1761                                x: point.startXPos
1762                            });
1763                            verb = 'animate';
1764                        }
1765                    }
1766                    if (graphic && verb === 'animate') { // Update
1767                        // Since the marker group isn't clipped, each
1768                        // individual marker must be toggled
1769                        graphic[isInside ? 'show' : 'hide'](isInside)
1770                            .animate(markerAttribs);
1771                    }
1772                    // Presentational attributes
1773                    if (graphic) {
1774                        const pointAttr = series.pointAttribs(point, ((styledMode || !point.selected) ?
1775                            void 0 :
1776                            'select'));
1777                        if (!styledMode) {
1778                            graphic[verb](pointAttr);
1779                        }
1780                        else if (colorAxis) { // #14114
1781                            graphic['css']({
1782                                fill: pointAttr.fill
1783                            });
1784                        }
1785                    }
1786                    if (graphic) {
1787                        graphic.addClass(point.getClassName(), true);
1788                    }
1789                }
1790                else if (graphic) {
1791                    point.graphic = graphic.destroy(); // #1269
1792                }
1793            }
1794        }
1795    }
1796    /**
1797     * Get non-presentational attributes for a point. Used internally for
1798     * both styled mode and classic. Can be overridden for different series
1799     * types.
1800     *
1801     * @see Series#pointAttribs
1802     *
1803     * @function Highcharts.Series#markerAttribs
1804     *
1805     * @param {Highcharts.Point} point
1806     * The Point to inspect.
1807     *
1808     * @param {string} [state]
1809     * The state, can be either `hover`, `select` or undefined.
1810     *
1811     * @return {Highcharts.SVGAttributes}
1812     * A hash containing those attributes that are not settable from CSS.
1813     */
1814    markerAttribs(point, state) {
1815        const seriesOptions = this.options, seriesMarkerOptions = seriesOptions.marker, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
1816            seriesMarkerOptions.symbol), attribs = {};
1817        let seriesStateOptions, pointStateOptions, radius = pick(pointMarkerOptions.radius, seriesMarkerOptions && seriesMarkerOptions.radius);
1818        // Handle hover and select states
1819        if (state) {
1820            seriesStateOptions = seriesMarkerOptions.states[state];
1821            pointStateOptions = pointMarkerOptions.states &&
1822                pointMarkerOptions.states[state];
1823            radius = pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius && radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
1824                0));
1825        }
1826        point.hasImage = symbol && symbol.indexOf('url') === 0;
1827        if (point.hasImage) {
1828            radius = 0; // And subsequently width and height is not set
1829        }
1830        const pos = point.pos();
1831        if (isNumber(radius) && pos) {
1832            if (seriesOptions.crisp) {
1833                pos[0] = crisp(pos[0], point.hasImage ?
1834                    0 :
1835                    symbol === 'rect' ?
1836                        // Rectangle symbols need crisp edges, others don't
1837                        seriesMarkerOptions?.lineWidth || 0 :
1838                        1);
1839            }
1840            attribs.x = pos[0] - radius;
1841            attribs.y = pos[1] - radius;
1842        }
1843        if (radius) {
1844            attribs.width = attribs.height = 2 * radius;
1845        }
1846        return attribs;
1847    }
1848    /**
1849     * Internal function to get presentational attributes for each point.
1850     * Unlike {@link Series#markerAttribs}, this function should return
1851     * those attributes that can also be set in CSS. In styled mode,
1852     * `pointAttribs` won't be called.
1853     *
1854     * @private
1855     * @function Highcharts.Series#pointAttribs
1856     *
1857     * @param {Highcharts.Point} [point]
1858     * The point instance to inspect.
1859     *
1860     * @param {string} [state]
1861     * The point state, can be either `hover`, `select` or 'normal'. If
1862     * undefined, normal state is assumed.
1863     *
1864     * @return {Highcharts.SVGAttributes}
1865     * The presentational attributes to be set on the point.
1866     */
1867    pointAttribs(point, state) {
1868        const seriesMarkerOptions = this.options.marker, pointOptions = point && point.options, pointMarkerOptions = ((pointOptions && pointOptions.marker) || {}), pointColorOption = pointOptions && pointOptions.color, pointColor = point && point.color, zoneColor = point && point.zone && point.zone.color;
1869        let seriesStateOptions, pointStateOptions, color = this.color, fill, stroke, strokeWidth = pick(pointMarkerOptions.lineWidth, seriesMarkerOptions.lineWidth), opacity = 1;
1870        color = (pointColorOption ||
1871            zoneColor ||
1872            pointColor ||
1873            color);
1874        fill = (pointMarkerOptions.fillColor ||
1875            seriesMarkerOptions.fillColor ||
1876            color);
1877        stroke = (pointMarkerOptions.lineColor ||
1878            seriesMarkerOptions.lineColor ||
1879            color);
1880        // Handle hover and select states
1881        state = state || 'normal';
1882        if (state) {
1883            seriesStateOptions = (seriesMarkerOptions.states[state] || {});
1884            pointStateOptions = (pointMarkerOptions.states &&
1885                pointMarkerOptions.states[state]) || {};
1886            strokeWidth = pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
1887            fill = (pointStateOptions.fillColor ||
1888                seriesStateOptions.fillColor ||
1889                fill);
1890            stroke = (pointStateOptions.lineColor ||
1891                seriesStateOptions.lineColor ||
1892                stroke);
1893            opacity = pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
1894        }
1895        return {
1896            'stroke': stroke,
1897            'stroke-width': strokeWidth,
1898            'fill': fill,
1899            'opacity': opacity
1900        };
1901    }
1902    /**
1903     * Clear DOM objects and free up memory.
1904     *
1905     * @private
1906     * @function Highcharts.Series#destroy
1907     *
1908     * @emits Highcharts.Series#event:destroy
1909     */
1910    destroy(keepEventsForUpdate) {
1911        const series = this, chart = series.chart, issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent), data = series.data || [];
1912        let destroy, i, point, axis;
1913        // Add event hook
1914        fireEvent(series, 'destroy', { keepEventsForUpdate });
1915        // Remove events
1916        this.removeEvents(keepEventsForUpdate);
1917        // Erase from axes
1918        (series.axisTypes || []).forEach(function (AXIS) {
1919            axis = series[AXIS];
1920            if (axis && axis.series) {
1921                erase(axis.series, series);
1922                axis.isDirty = axis.forceRedraw = true;
1923            }
1924        });
1925        // Remove legend items
1926        if (series.legendItem) {
1927            series.chart.legend.destroyItem(series);
1928        }
1929        // Destroy all points with their elements
1930        i = data.length;
1931        while (i--) {
1932            point = data[i];
1933            if (point && point.destroy) {
1934                point.destroy();
1935            }
1936        }
1937        for (const zone of series.zones) {
1938            // Destroy SVGElement's but preserve primitive props (#20426)
1939            destroyObjectProperties(zone, void 0, true);
1940        }
1941        // Clear the animation timeout if we are destroying the series
1942        // during initial animation
1943        U.clearTimeout(series.animationTimeout);
1944        // Destroy all SVGElements associated to the series
1945        objectEach(series, function (val, prop) {
1946            // Survive provides a hook for not destroying
1947            if (val instanceof SVGElement && !val.survive) {
1948                // Issue 134 workaround
1949                destroy = issue134 && prop === 'group' ?
1950                    'hide' :
1951                    'destroy';
1952                val[destroy]();
1953            }
1954        });
1955        // Remove from hoverSeries
1956        if (chart.hoverSeries === series) {
1957            chart.hoverSeries = void 0;
1958        }
1959        erase(chart.series, series);
1960        chart.orderItems('series');
1961        // Clear all members
1962        objectEach(series, function (val, prop) {
1963            if (!keepEventsForUpdate || prop !== 'hcEvents') {
1964                delete series[prop];
1965            }
1966        });
1967    }
1968    /**
1969     * Clip the graphs into zones for colors and styling.
1970     *
1971     * @private
1972     * @function Highcharts.Series#applyZones
1973     */
1974    applyZones() {
1975        const series = this, { area, chart, graph, zones, points, xAxis, yAxis, zoneAxis } = series, { inverted, renderer } = chart, axis = this[`${zoneAxis}Axis`], { isXAxis, len = 0, minPointOffset = 0 } = axis || {}, halfWidth = (graph?.strokeWidth() || 0) / 2 + 1, 
1976        // Avoid points that are so close to the threshold that the graph
1977        // line would be split
1978        avoidClose = (zone, plotX = 0, plotY = 0) => {
1979            if (inverted) {
1980                plotY = len - plotY;
1981            }
1982            const { translated = 0, lineClip } = zone, distance = plotY - translated;
1983            lineClip?.push([
1984                'L',
1985                plotX,
1986                Math.abs(distance) < halfWidth ?
1987                    plotY - halfWidth * (distance <= 0 ? -1 : 1) :
1988                    translated
1989            ]);
1990        };
1991        if (zones.length &&
1992            (graph || area) &&
1993            axis &&
1994            isNumber(axis.min)) {
1995            const axisMax = axis.getExtremes().max + minPointOffset, 
1996            // Invert the x and y coordinates of inverted charts
1997            invertPath = (path) => {
1998                path.forEach((segment, i) => {
1999                    if (segment[0] === 'M' || segment[0] === 'L') {
2000                        path[i] = [
2001                            segment[0],
2002                            isXAxis ? len - segment[1] : segment[1],
2003                            isXAxis ? segment[2] : len - segment[2]
2004                        ];
2005                    }
2006                });
2007            };
2008            // Reset
vendor: 10,385 bytes, lines 2009-2280
2009            zones.forEach((zone) => {
2010                zone.lineClip = [];
2011                zone.translated = clamp(axis.toPixels(pick(zone.value, axisMax), true) || 0, 0, len);
2012            });
2013            // The use of the Color Threshold assumes there are no gaps so it is
2014            // safe to hide the original graph and area unless it is not
2015            // waterfall series, then use showLine property to set lines between
2016            // columns to be visible (#7862)
2017            if (graph && !this.showLine) {
2018                graph.hide();
2019            }
2020            if (area) {
2021                area.hide();
2022            }
2023            // Prepare for adaptive clips, avoiding segments close to the
2024            // threshold (#19709)
2025            if (zoneAxis === 'y' &&
2026                // Overheat protection
2027                points.length < xAxis.len) {
2028                for (const point of points) {
2029                    const { plotX, plotY, zone } = point, zoneBelow = zone && zones[zones.indexOf(zone) - 1];
2030                    // Close to upper boundary
2031                    if (zone) {
2032                        avoidClose(zone, plotX, plotY);
2033                    }
2034                    // Close to lower boundary
2035                    if (zoneBelow) {
2036                        avoidClose(zoneBelow, plotX, plotY);
2037                    }
2038                }
2039            }
2040            // Compute and apply the clips
2041            let lastLineClip = [], 
2042            // Starting point of the first zone. Offset for category axis
2043            // (#22188).
2044            lastTranslated = axis.toPixels(axis.getExtremes().min - minPointOffset, true);
2045            zones.forEach((zone) => {
2046                const lineClip = zone.lineClip || [], translated = Math.round(zone.translated || 0);
2047                if (xAxis.reversed) {
2048                    lineClip.reverse();
2049                }
2050                let { clip, simpleClip } = zone, x1 = 0, y1 = 0, x2 = xAxis.len, y2 = yAxis.len;
2051                if (isXAxis) {
2052                    x1 = translated;
2053                    x2 = lastTranslated;
2054                }
2055                else {
2056                    y1 = translated;
2057                    y2 = lastTranslated;
2058                }
2059                // Adaptive clips
2060                const simplePath = [
2061                    ['M', x1, y1],
2062                    ['L', x2, y1],
2063                    ['L', x2, y2],
2064                    ['L', x1, y2],
2065                    ['Z']
2066                ], adaptivePath = [
2067                    simplePath[0],
2068                    ...lineClip,
2069                    simplePath[1],
2070                    simplePath[2],
2071                    ...lastLineClip,
2072                    simplePath[3],
2073                    simplePath[4]
2074                ];
2075                lastLineClip = lineClip.reverse();
2076                lastTranslated = translated;
2077                if (inverted) {
2078                    invertPath(adaptivePath);
2079                    if (area) {
2080                        invertPath(simplePath);
2081                    }
2082                }
2083                /* Debug clip paths
2084                zone.path?.destroy();
2085                zone.path = chart.renderer.path(adaptivePath)
2086                    .attr({
2087                        stroke: zone.color || this.color || 'gray',
2088                        'stroke-width': 1,
2089                        'dashstyle': 'Dash'
2090                    })
2091                    .add(series.group);
2092                // */
2093                if (clip) {
2094                    clip.animate({ d: adaptivePath });
2095                    simpleClip?.animate({ d: simplePath });
2096                }
2097                else {
2098                    clip = zone.clip = renderer.path(adaptivePath);
2099                    if (area) {
2100                        simpleClip = zone.simpleClip = renderer.path(simplePath);
2101                    }
2102                }
2103                // When no data, graph zone is not applied and after setData
2104                // clip was ignored. As a result, it should be applied each
2105                // time.
2106                if (graph) {
2107                    zone.graph?.clip(clip);
2108                }
2109                if (area) {
2110                    zone.area?.clip(simpleClip);
2111                }
2112            });
2113        }
2114        else if (series.visible) {
2115            // If zones were removed, restore graph and area
2116            if (graph) {
2117                graph.show();
2118            }
2119            if (area) {
2120                area.show();
2121            }
2122        }
2123    }
2124    /**
2125     * General abstraction for creating plot groups like series.group,
2126     * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
2127     * the group will only be adjusted to the updated plot size.
2128     *
2129     * @private
2130     * @function Highcharts.Series#plotGroup
2131     */
2132    plotGroup(prop, name, visibility, zIndex, parent) {
2133        let group = this[prop];
2134        const isNew = !group, attrs = {
2135            visibility,
2136            zIndex: zIndex || 0.1 // Pointer logic uses this
2137        };
2138        // Avoid setting undefined opacity, or in styled mode
2139        if (defined(this.opacity) &&
2140            !this.chart.styledMode && this.state !== 'inactive' // #13719
2141        ) {
2142            attrs.opacity = this.opacity;
2143        }
2144        // Generate it on first call
2145        if (!group) {
2146            this[prop] = group = this.chart.renderer
2147                .g()
2148                .add(parent);
2149        }
2150        // Add the class names, and replace existing ones as response to
2151        // Series.update (#6660)
2152        group.addClass(('highcharts-' + name +
2153            ' highcharts-series-' + this.index +
2154            ' highcharts-' + this.type + '-series ' +
2155            (defined(this.colorIndex) ?
2156                'highcharts-color-' + this.colorIndex + ' ' :
2157                '') +
2158            (this.options.className || '') +
2159            (group.hasClass('highcharts-tracker') ?
2160                ' highcharts-tracker' :
2161                '')), true);
2162        // Place it on first and subsequent (redraw) calls
2163        group.attr(attrs)[isNew ? 'attr' : 'animate'](this.getPlotBox(name));
2164        return group;
2165    }
2166    /**
2167     * Get the translation and scale for the plot area of this series.
2168     *
2169     * @function Highcharts.Series#getPlotBox
2170     */
2171    getPlotBox(name) {
2172        let horAxis = this.xAxis, vertAxis = this.yAxis;
2173        const chart = this.chart, inverted = (chart.inverted &&
2174            !chart.polar &&
2175            horAxis &&
2176            this.invertible &&
2177            name === 'series');
2178        // Swap axes for inverted (#2339)
2179        if (chart.inverted) {
2180            horAxis = vertAxis;
2181            vertAxis = this.xAxis;
2182        }
2183        return {
2184            translateX: horAxis ? horAxis.left : chart.plotLeft,
2185            translateY: vertAxis ? vertAxis.top : chart.plotTop,
2186            rotation: inverted ? 90 : 0,
2187            rotationOriginX: inverted ?
2188                (horAxis.len - vertAxis.len) / 2 :
2189                0,
2190            rotationOriginY: inverted ?
2191                (horAxis.len + vertAxis.len) / 2 :
2192                0,
2193            scaleX: inverted ? -1 : 1, // #1623
2194            scaleY: 1
2195        };
2196    }
2197    /**
2198     * Removes the event handlers attached previously with addEvents.
2199     * @private
2200     * @function Highcharts.Series#removeEvents
2201     */
2202    removeEvents(keepEventsForUpdate) {
2203        const { eventsToUnbind } = this;
2204        if (!keepEventsForUpdate) {
2205            // Remove all events
2206            removeEvent(this);
2207        }
2208        if (eventsToUnbind.length) {
2209            // Remove only internal events for proper update. #12355 solves
2210            // problem with multiple destroy events
2211            eventsToUnbind.forEach((unbind) => {
2212                unbind();
2213            });
2214            eventsToUnbind.length = 0;
2215        }
2216    }
2217    /**
2218     * Render the graph and markers. Called internally when first rendering
2219     * and later when redrawing the chart. This function can be extended in
2220     * plugins, but normally shouldn't be called directly.
2221     *
2222     * @function Highcharts.Series#render
2223     *
2224     * @emits Highcharts.Series#event:afterRender
2225     */
2226    render() {
2227        const series = this, { chart, options, hasRendered } = series, animOptions = animObject(options.animation), visibility = series.visible ?
2228            'inherit' : 'hidden', // #2597
2229        zIndex = options.zIndex, chartSeriesGroup = chart.seriesGroup;
2230        let animDuration = series.finishedAnimating ?
2231            0 : animOptions.duration;
2232        fireEvent(this, 'render');
2233        // The group
2234        series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
2235        series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
2236        // Initial clipping, applies to columns etc. (#3839).
2237        if (options.clip !== false) {
2238            series.setClip();
2239        }
2240        // Initialize the animation
2241        if (animDuration) {
2242            series.animate?.(true);
2243        }
2244        // Draw the graph if any
2245        if (series.drawGraph) {
2246            series.drawGraph();
2247            series.applyZones();
2248        }
2249        // Draw the points
2250        if (series.visible) {
2251            series.drawPoints();
2252        }
2253        // Draw the data labels
2254        series.drawDataLabels?.();
2255        // In pie charts, slices are added to the DOM, but actual rendering
2256        // is postponed until labels reserved their space
2257        series.redrawPoints?.();
2258        // Draw the mouse tracking area
2259        if (options.enableMouseTracking) {
2260            series.drawTracker?.();
2261        }
2262        // Run the animation
2263        if (animDuration) {
2264            series.animate?.();
2265        }
2266        // Call the afterAnimate function on animation complete (but don't
2267        // overwrite the animation.complete option which should be available
2268        // to the user).
2269        if (!hasRendered) {
2270            // Additional time if defer is defined before afterAnimate
2271            // will be triggered
2272            if (animDuration && animOptions.defer) {
2273                animDuration += animOptions.defer;
2274            }
2275            series.animationTimeout = syncTimeout(() => {
2276                series.afterAnimate();
2277            }, animDuration || 0);
2278        }
2279        // Means data is in accordance with what you see
2280        series.isDirty = false;
vendor: 5,303 bytes, lines 2281-2410
2281        // (See #322) series.isDirty = series.isDirtyData = false; // means
2282        // data is in accordance with what you see
2283        series.hasRendered = true;
2284        fireEvent(series, 'afterRender');
2285    }
2286    /**
2287     * Redraw the series. This function is called internally from
2288     * `chart.redraw` and normally shouldn't be called directly.
2289     * @private
2290     * @function Highcharts.Series#redraw
2291     */
2292    redraw() {
2293        // Cache it here as it is set to false in render, but used after
2294        const wasDirty = this.isDirty || this.isDirtyData;
2295        this.translate();
2296        this.render();
2297        if (wasDirty) { // #3868, #3945
2298            delete this.kdTree;
2299        }
2300    }
2301    /**
2302     * Whether to reserve space for the series, either because it is visible or
2303     * because the `chart.ignoreHiddenSeries` option is false.
2304     *
2305     * @private
2306     */
2307    reserveSpace() {
2308        return this.visible || !this.chart.options.chart.ignoreHiddenSeries;
2309    }
2310    /**
2311     * Find the nearest point from a pointer event. This applies to series that
2312     * use k-d-trees to get the nearest point. Native pointer events must be
2313     * normalized using `Pointer.normalize`, that adds `chartX` and `chartY`
2314     * properties.
2315     *
2316     * @sample highcharts/demo/synchronized-charts
2317     *         Synchronized charts with tooltips
2318     *
2319     * @function Highcharts.Series#searchPoint
2320     *
2321     * @param {Highcharts.PointerEvent} e
2322     *        The normalized pointer event
2323     * @param {boolean} [compareX=false]
2324     *        Search only by the X value, not Y
2325     *
2326     * @return {Point|undefined}
2327     *        The closest point to the pointer event
2328     */
2329    searchPoint(e, compareX) {
2330        const { xAxis, yAxis } = this, inverted = this.chart.inverted;
2331        return this.searchKDTree({
2332            clientX: inverted ?
2333                xAxis.len - e.chartY + xAxis.pos :
2334                e.chartX - xAxis.pos,
2335            plotY: inverted ?
2336                yAxis.len - e.chartX + yAxis.pos :
2337                e.chartY - yAxis.pos
2338        }, compareX, e);
2339    }
2340    /**
2341     * Build the k-d-tree that is used by mouse and touch interaction to get
2342     * the closest point. Line-like series typically have a one-dimensional
2343     * tree where points are searched along the X axis, while scatter-like
2344     * series typically search in two dimensions, X and Y.
2345     *
2346     * @private
2347     * @function Highcharts.Series#buildKDTree
2348     */
2349    buildKDTree(e) {
2350        // Prevent multiple k-d-trees from being built simultaneously
2351        // (#6235)
2352        this.buildingKdTree = true;
2353        const series = this, dimensions = series.options.findNearestPointBy
2354            .indexOf('y') > -1 ? 2 : 1;
2355        /**
2356         * Internal function
2357         * @private
2358         */
2359        function kdtree(points, depth, dimensions) {
2360            const length = points?.length;
2361            let axis, median;
2362            if (length) {
2363                // Alternate between the axis
2364                axis = series.kdAxisArray[depth % dimensions];
2365                // Sort point array
2366                points.sort((a, b) => (a[axis] || 0) - (b[axis] || 0));
2367                median = Math.floor(length / 2);
2368                // Build and return node
2369                return {
2370                    point: points[median],
2371                    left: kdtree(points.slice(0, median), depth + 1, dimensions),
2372                    right: kdtree(points.slice(median + 1), depth + 1, dimensions)
2373                };
2374            }
2375        }
2376        /**
2377         * Start the recursive build process with a clone of the points
2378         * array and null points filtered out. (#3873)
2379         * @private
2380         */
2381        function startRecursive() {
2382            series.kdTree = kdtree(series.getValidPoints(void 0, 
2383            // For line-type series restrict to plot area, but
2384            // column-type series not (#3916, #4511)
2385            !series.directTouch), dimensions, dimensions);
2386            series.buildingKdTree = false;
2387        }
2388        delete series.kdTree;
2389        // For testing tooltips, don't build async. Also if touchstart, we may
2390        // be dealing with click events on mobile, so don't delay (#6817).
2391        syncTimeout(startRecursive, series.options.kdNow || e?.type === 'touchstart' ? 0 : 1);
2392    }
2393    /**
2394     * @private
2395     * @function Highcharts.Series#searchKDTree
2396     */
2397    searchKDTree(point, compareX, e, suppliedPointEvaluator, suppliedBSideCheckEvaluator) {
2398        const series = this, [kdX, kdY] = this.kdAxisArray, kdComparer = compareX ? 'distX' : 'dist', kdDimensions = (series.options.findNearestPointBy || '')
2399            .indexOf('y') > -1 ? 2 : 1, useRadius = !!series.isBubble, pointEvaluator = suppliedPointEvaluator || ((p1, p2, comparisonProp) => [
2400            (p1[comparisonProp] || 0) < (p2[comparisonProp] || 0) ?
2401                p1 :
2402                p2,
2403            false
2404        ]), bSideCheckEvaluator = suppliedBSideCheckEvaluator || ((a, b) => a < b);
2405        /**
2406         * Set the one and two dimensional distance on the point object.
2407         * @private
2408         */
2409        function setDistance(p1, p2) {
2410            const p1kdX = p1[kdX], p2kdX = p2[kdX], x = (defined(p1kdX) && defined(p2kdX)) ? p1kdX - p2kdX : null, p1kdY = p1[kdY], p2k
vendor: 1,218 bytes, lines 2410-2430
2410dY = p2[kdY], y = (defined(p1kdY) && defined(p2kdY)) ? p1kdY - p2kdY : 0, radius = useRadius ? (p2.marker?.radius || 0) : 0;
2411            p2.dist = Math.sqrt(((x && x * x) || 0) + y * y) - radius;
2412            p2.distX = defined(x) ? (Math.abs(x) - radius) : Number.MAX_VALUE;
2413        }
2414        /**
2415         * @private
2416         */
2417        function doSearch(search, tree, depth, dimensions) {
2418            const point = tree.point, axis = series.kdAxisArray[depth % dimensions];
2419            let ret = point, flip = false;
2420            setDistance(search, point);
2421            // Pick side based on distance to splitting point
2422            const tdist = (search[axis] || 0) - (point[axis] || 0) +
2423                (useRadius ? (point.marker?.radius || 0) : 0), sideA = tdist < 0 ? 'left' : 'right', sideB = tdist < 0 ? 'right' : 'left';
2424            // End of tree
2425            if (tree[sideA]) {
2426                [ret, flip] = pointEvaluator(point, doSearch(search, tree[sideA], depth + 1, dimensions), kdComparer);
2427            }
2428            if (tree[sideB]) {
2429                const sqrtTDist = Math.sqrt(tdist * tdist), retDist = ret[kdComparer];
2430                // Compare distance to current best to splitting point to decide
vendor: 4,975 bytes, lines 2431-2553
2431                // whether to check side B or no
2432                if (bSideCheckEvaluator(sqrtTDist, retDist, flip)) {
2433                    ret = pointEvaluator(ret, doSearch(search, tree[sideB], depth + 1, dimensions), kdComparer)[0];
2434                }
2435            }
2436            return ret;
2437        }
2438        if (!this.kdTree && !this.buildingKdTree) {
2439            this.buildKDTree(e);
2440        }
2441        if (this.kdTree) {
2442            return doSearch(point, this.kdTree, kdDimensions, kdDimensions);
2443        }
2444    }
2445    /**
2446     * @private
2447     * @function Highcharts.Series#pointPlacementToXValue
2448     */
2449    pointPlacementToXValue() {
2450        const { options, xAxis } = this;
2451        let factor = options.pointPlacement;
2452        // Point placement is relative to each series pointRange (#5889)
2453        if (factor === 'between') {
2454            factor = xAxis.reversed ? -0.5 : 0.5; // #11955
2455        }
2456        return isNumber(factor) ?
2457            factor * (options.pointRange || xAxis.pointRange) :
2458            0;
2459    }
2460    /**
2461     * @private
2462     * @function Highcharts.Series#isPointInside
2463     */
2464    isPointInside(point) {
2465        const { chart, xAxis, yAxis } = this, { plotX = -1, plotY = -1 } = point, isInside = (plotY >= 0 &&
2466            plotY <= (yAxis ? yAxis.len : chart.plotHeight) &&
2467            plotX >= 0 &&
2468            plotX <= (xAxis ? xAxis.len : chart.plotWidth));
2469        return isInside;
2470    }
2471    /**
2472     * Draw the tracker object that sits above all data labels and markers to
2473     * track mouse events on the graph or points. For the line type charts
2474     * the tracker uses the same graphPath, but with a greater stroke width
2475     * for better control.
2476     * @private
2477     */
2478    drawTracker() {
2479        const series = this, options = series.options, trackByArea = options.trackByArea, trackerPath = [].concat((trackByArea ? series.areaPath : series.graphPath) || []), chart = series.chart, pointer = chart.pointer, renderer = chart.renderer, snap = chart.options.tooltip?.snap || 0, onMouseOver = () => {
2480            if (options.enableMouseTracking &&
2481                chart.hoverSeries !== series) {
2482                series.onMouseOver();
2483            }
2484        }, 
2485        /*
2486         * Empirical lowest possible opacities for TRACKER_FILL for an
2487         * element to stay invisible but clickable
2488         * IE9: 0.00000000001 (unlimited)
2489         * IE10: 0.0001 (exporting only)
2490         * FF: 0.00000000001 (unlimited)
2491         * Chrome: 0.000001
2492         * Safari: 0.000001
2493         * Opera: 0.00000000001 (unlimited)
2494         */
2495        TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
2496        let tracker = series.tracker;
2497        // Draw the tracker
2498        if (tracker) {
2499            tracker.attr({ d: trackerPath });
2500        }
2501        else if (series.graph) { // Create
2502            series.tracker = tracker = renderer.path(trackerPath)
2503                .attr({
2504                visibility: series.visible ? 'inherit' : 'hidden',
2505                zIndex: 2
2506            })
2507                .addClass(trackByArea ?
2508                'highcharts-tracker-area' :
2509                'highcharts-tracker-line')
2510                .add(series.group);
2511            if (!chart.styledMode) {
2512                tracker.attr({
2513                    'stroke-linecap': 'round',
2514                    'stroke-linejoin': 'round', // #1225
2515                    stroke: TRACKER_FILL,
2516                    fill: trackByArea ? TRACKER_FILL : 'none',
2517                    'stroke-width': series.graph.strokeWidth() +
2518                        (trackByArea ? 0 : 2 * snap)
2519                });
2520            }
2521            // The tracker is added to the series group, which is clipped, but
2522            // is covered by the marker group. So the marker group also needs to
2523            // capture events.
2524            [
2525                series.tracker,
2526                series.markerGroup,
2527                series.dataLabelsGroup
2528            ].forEach((tracker) => {
2529                if (tracker) {
2530                    tracker.addClass('highcharts-tracker')
2531                        .on('mouseover', onMouseOver)
2532                        .on('mouseout', (e) => {
2533                        pointer?.onTrackerMouseOut(e);
2534                    });
2535                    if (options.cursor && !chart.styledMode) {
2536                        tracker.css({ cursor: options.cursor });
2537                    }
2538                    tracker.on('touchstart', onMouseOver);
2539                }
2540            });
2541        }
2542        fireEvent(this, 'afterDrawTracker');
2543    }
2544    /**
2545     * Add a point to the series after render time. The point can be added at
2546     * the end, or by giving it an X value, to the start or in the middle of the
2547     * series.
2548     *
2549     * @sample highcharts/members/series-addpoint-append/
2550     *         Append point
2551     * @sample highcharts/members/series-addpoint-append-and-shift/
2552     *         Append and shift
2553     * @sample highcharts/members/series-addpoint-x-and-y/
2554     *         Both X and Y values given
2555     * @sample highcharts/members/series-addpoint-pie/
2556     *         Append pie slice
2557     * @sample stock/members/series-addpoint/
2558     *         Append 100 points in Highcharts Stock
2559     * @sample stock/members/series-addpoint-shift/
2560     *         Append and shift in Highcharts Stock
2561     * @sample maps/members/series-addpoint/
2562     *         Add a point in Highmaps
2563     *
2564     * @function Highcharts.Series#addPoint
2565     *
2566     * @param {Highcharts.PointOptionsType} options
2567     *        The point options. If options is a single number, a point with
2568     *        that y value is appended to the series. If it is an array, it will
2569     *        be interpreted as x and y values respectively. If it is an
2570     *        object, advanced options as outlined under `series.data` are
2571     *        applied.
2572     *
2573     * @param {boolean} [redraw=true]
2574     *        Whether to redraw the chart after the point is added. When adding
2575     *        more than one point, it is highly recommended that the redraw
2576     *        option be set to false, and instead {@link Chart#redraw} is
2577     *        explicitly called after the adding of points is finished.
2578     *        Otherwise, the chart will redraw after adding each point.
2579     *
2580     * @param {boolean} [shift=false]
2581     *        If true, a point is shifted off the start of the series as one is
2582     *        appended to the end.
2583     *
2584     * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
2585     *        Whether to apply animation, and optionally animation
2586     *        configuration.
2587     *
2588     * @param {boolean} [withEvent=true]
2589     *        Used internally, whether to fire the series `addPoint` event.
2590     *
2591     * @emits Highcharts.Series#event:addPoint
2592     */
2593    addPoint(options, redraw, shift, animation, withEvent) {
2594        const series = this, seriesOptions = series.options, { chart, data, dataTable: table, xAxis } = series, names = xAxis && xAxis.hasNames && xAxis.names, dataOptions = seriesOptions.data, xData = series.getColumn('x');
2595        let isInTheMiddle, i;
2596        // Optional redraw, defaults to true
2597        redraw = pick(redraw, true);
2598        // Get options and push the point to xData, yData and series.options. In
2599        // series.generatePoints the Point instance will be created on demand
2600        // and pushed to the series.data array.
2601        const point = { series: series };
2602        series.pointClass.prototype.applyOptions.apply(point, [options]);
2603        const x = point.x;
2604        // Get the insertion point
2605        i = xData.length;
2606        if (series.requireSorting && x < xData[i - 1]) {
2607            isInTheMiddle = true;
2608            while (i && xData[i - 1] > x) {
2609                i--;
2610            }
2611        }
2612        // Insert the row at the given index
2613        table.setRow(point, i, true, { addColumns: false });
2614        if (names && point.name) {
2615            names[x] = point.name;
2616        }
2617        dataOptions?.splice(i, 0, options);
2618        if (isInTheMiddle ||
2619            // When processedData is present we need to splice an empty slot
2620            // into series.data, otherwise generatePoints won't pick it up.
2621            series.processedData) {
2622            series.data.splice(i, 0, null);
2623            series.processData();
2624        }
2625        // Generate points to be added to the legend (#1329)
2626        if (seriesOptions.legendType === 'point') {
2627            series.generatePoints();
2628        }
2629        // Shift the first point off the parallel arrays
2630        if (shift) {
2631            if (data[0] && !!data[0].remove) {
2632                data[0].remove(false);
2633            }
2634            else {
2635                [
2636                    data,
2637                    dataOptions,
2638                    ...Object.values(table.getColumns())
2639                ].filter(defined).forEach((coll) => {
2640                    coll.shift();
2641                });
2642                table.rowCount -= 1;
2643                fireEvent(table, 'afterDeleteRows');
2644            }
2645        }
2646        // Fire event
2647        if (withEvent !== false) {
2648            fireEvent(series, 'addPoint', { point: point });
2649        }
2650        // Redraw
2651        series.isDirty = true;
2652        series.isDirtyData = true;
2653        if (redraw) {
2654            chart.redraw(animation); // Animation is set anyway on redraw, #5665
2655        }
2656    }
2657    /**
2658     * Remove a point from the series. Unlike the
2659     * {@link Highcharts.Point#remove} method, this can also be done on a point
2660     * that is not instantiated because it is outside the view or subject to
2661     * Highcharts Stock data grouping.
2662     *
2663     * @sample highcharts/members/series-removepoint/
2664     *         Remove cropped point
2665     *
2666     * @function Highcharts.Series#removePoint
2667     *
2668     * @param {number} i
2669     *        The index of the point in the {@link Highcharts.Series.data|data}
2670     *        array.
2671     *
2672     * @param {boolean} [redraw=true]
2673     *        Whether to redraw the chart after the point is added. When
2674     *        removing more than one point, it is highly recommended that the
2675     *        `redraw` option be set to `false`, and instead {@link
2676     *        Highcharts.Chart#redraw} is explicitly called after the adding of
2677     *        points is finished.
2678     *
2679     * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
2680     *        Whether and optionally how the series should be animated.
2681     *
2682     * @emits Highcharts.Point#event:remove
2683     */
2684    removePoint(i, redraw, animation) {
2685        const series = this, { chart, data, points, dataTable: table } = series, point = data[i], remove = function () {
2686            // Splice out the point's data from all parallel arrays
2687            [
2688                // #4935
2689                points?.length === data.length ? points : void 0,
2690                data,
2691                series.options.data,
2692                ...Object.values(table.getColumns())
2693            ].filter(defined).forEach((coll) => {
2694                coll.splice(i, 1);
2695            });
2696            // Shorthand row deletion in order to avoid including the whole
2697            // `deleteRows` function in the DataTableCore module.
2698            table.rowCount -= 1;
2699            fireEvent(table, 'afterDeleteRows');
2700            point?.destroy();
2701            // Redraw
2702            series.isDirty = true;
2703            series.isDirtyData = true;
2704            if (redraw) {
2705                chart.redraw();
2706            }
2707        };
2708        setAnimation(animation, chart);
2709        redraw = pick(redraw, true);
2710        // Fire the event with a default handler of removing the point
2711        if (point) {
2712            point.firePointEvent('remove', null, remove);
2713        }
2714        else {
2715            remove();
2716        }
2717    }
2718    /**
2719     * Remove a series and optionally redraw the chart.
2720     *
2721     * @sample highcharts/members/series-remove/
2722     *         Remove first series from a button
2723     *
2724     * @function Highcharts.Series#remove
2725     *
2726     * @param {boolean} [redraw=true]
2727     *        Whether to redraw the chart or wait for an explicit call to
2728     *        {@link Highcharts.Chart#redraw}.
2729     *
2730     * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
2731     *        Whether to apply animation, and optionally animation
2732     *        configuration.
2733     *
2734     * @param {boolean} [withEvent=true]
2735     *        Used internally, whether to fire the series `remove` event.
2736     *
2737     * @emits Highcharts.Series#event:remove
2738     */
2739    remove(redraw, animation, withEvent, keepEvents) {
2740        const series = this, chart = series.chart;
2741        /**
2742         * @private
2743         */
2744        function remove() {
2745            // Destroy elements
2746            series.destroy(keepEvents);
2747            // Redraw
2748            chart.isDirtyLegend = chart.isDirtyBox = true;
2749            chart.linkSeries(keepEvents);
2750            if (pick(redraw, true)) {
2751                chart.redraw(animation);
2752            }
2753        }
2754        // Fire the event with a default handler of removing the point
2755        if (withEvent !== false) {
2756            fireEvent(series, 'remove', null, remove);
2757        }
2758        else {
2759            remove();
2760        }
2761    }
2762    /**
2763     * Update the series with a new set of options. For a clean and precise
2764     * handling of new options, all methods and elements from the series are
2765     * removed, and it is initialized from scratch. Therefore, this method is
2766     * more performance expensive than some other utility methods like {@link
2767     * Series#setData} or {@link Series#setVisible}.
2768     *
2769     * Note that `Series.update` may mutate the passed `data` options.
2770     *
2771     * @sample highcharts/members/series-update/
2772     *         Updating series options
2773     * @sample maps/members/series-update/
2774     *         Update series options in Highmaps
2775     *
2776     * @function Highcharts.Series#update
2777     *
2778     * @param {Highcharts.SeriesOptionsType} options
2779     *        New options that will be merged with the series' existing options.
2780     *
2781     * @param {boolean} [redraw=true]
2782     *        Whether to redraw the chart after the series is altered. If doing
2783     *        more operations on the chart, it is a good idea to set redraw to
2784     *        false and call {@link Chart#redraw} after.
2785     *
2786     * @emits Highcharts.Series#event:update
2787     * @emits Highcharts.Series#event:afterUpdate
2788     */
2789    update(options, redraw) {
2790        options = diffObjects(options, this.userOptions);
2791        fireEvent(this, 'update', { options: options });
2792        const series = this, chart = series.chart, 
2793        // Must use user options when changing type because series.options
2794        // is merged in with type specific plotOptions
2795        oldOptions = series.userOptions, initialType = series.initialType || series.type, plotOptions = chart.options.plotOptions, initialSeriesProto = seriesTypes[initialType].prototype, groups = [
2796            'group',
2797            'markerGroup',
2798            'dataLabelsGroup',
2799            'transformGroup'
2800        ], optionsToCheck = [
2801            'dataGrouping',
2802            'pointStart',
2803            'pointInterval',
2804            'pointIntervalUnit',
2805            'keys'
2806        ], 
2807        // Animation must be enabled when calling update before the initial
2808        // animation has first run. This happens when calling update
2809        // directly after chart initialization, or when applying responsive
2810        // rules (#6912).
2811        animation = series.finishedAnimating && { animation: false }, kinds = {};
2812        let seriesOptions, n, preserve = [
2813            'colorIndex',
2814            'eventOptions',
2815            'navigatorSeries',
2816            'symbolIndex',
2817            'baseSeries'
2818        ], newType = (options.type ||
2819            oldOptions.type ||
2820            chart.options.chart.type);
2821        const keepPoints = !(
2822        // Indicators, histograms etc recalculate the data. It should be
2823        // possible to omit this.
2824        this.hasDerivedData ||
2825            // New type requires new point classes
2826            (newType && newType !== this.type) ||
2827            // New options affecting how the data points are built
2828            typeof options.keys !== 'undefined' ||
2829            typeof options.pointStart !== 'undefined' ||
2830            typeof options.pointInterval !== 'undefined' ||
2831            typeof options.relativeXValue !== 'undefined' ||
2832            options.joinBy ||
2833            options.mapData || // #11636
2834            // Changes to data grouping requires new points in new group
2835            optionsToCheck.some((option) => series.hasOptionChanged(option)));
2836        newType = newType || initialType;
2837        if (keepPoints) {
2838            preserve.push('data', 'isDirtyData', 
2839            // GeoHeatMap interpolation
2840            'isDirtyCanvas', 'points', 'dataTable', 'processedData', // #17057
2841            'xIncrement', 'cropped', '_hasPointMarkers', 'hasDataLabels', 
2842            // Networkgraph (#14397)
2843            'nodes', 'layout', 
2844            // Treemap
2845            'level', 
2846            // Map specific, consider moving it to series-specific preserve-
2847            // properties (#10617)
2848            'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX', 'transformGroups' // #18857
2849            );
2850            if (options.visible !== false) {
2851                preserve.push('area', 'graph');
2852            }
2853            series.parallelArrays.forEach(function (key) {
2854                preserve.push(key + 'Data');
2855            });
2856            if (options.data) {
2857                // `setData` uses `dataSorting` options so we need to update
2858                // them earlier
2859                if (options.dataSorting) {
2860                    extend(series.options.dataSorting, options.dataSorting);
2861                }
2862                this.setData(options.data, false);
2863            }
2864        }
2865        else {
2866            this.dataTable.modified = this.dataTable;
2867        }
2868        // Do the merge, with some forced options
2869        options = merge(oldOptions, {
2870            // When oldOptions.index is null it should't be cleared.
2871            // Otherwise navigator series will have wrong indexes (#10193).
2872            index: oldOptions.index === void 0 ?
2873                series.index : oldOptions.index,
2874            pointStart: 
2875            // When updating from blank (#7933)
2876            plotOptions?.series?.pointStart ??
2877                oldOptions.pointStart ??
2878                // When updating after addPoint
2879                series.getColumn('x')[0]
2880        }, !keepPoints && { data: series.options.data }, options, animation);
2881        // Merge does not merge arrays, but replaces them. Since points were
2882        // updated, `series.options.data` has correct merged options, use it:
2883        if (keepPoints && options.data) {
2884            options.data = series.options.data;
2885        }
2886        // Make sure preserved properties are not destroyed (#3094)
2887        preserve = groups.concat(preserve);
2888        preserve.forEach(function (prop) {
2889            preserve[prop] = series[prop];
2890            delete series[prop];
2891        });
2892        let casting = false;
2893        if (seriesTypes[newType]) {
2894            casting = newType !== series.type;
2895            // Destroy the series and delete all properties, it will be
2896            // reinserted within the `init` call below
2897            series.remove(false, false, false, true);
2898            if (casting) {
2899                // #20264: Re-detect a certain chart properties from new series
2900                chart.propFromSeries();
2901                // Modern browsers including IE11
2902                if (Object.setPrototypeOf) {
2903                    Object.setPrototypeOf(series, seriesTypes[newType].prototype);
2904                    // Legacy (IE < 11)
2905                }
2906                else {
2907                    const ownEvents = Object.hasOwnProperty.call(series, 'hcEvents') && series.hcEvents;
2908                    for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
2909                        series[n] = void 0;
2910                    }
2911                    // Reinsert all methods and properties from the new type
2912                    // prototype (#2270, #3719).
2913                    extend(series, seriesTypes[newType].prototype);
2914                    // The events are tied to the prototype chain, don't copy if
2915                    // they're not the series' own
2916                    if (ownEvents) {
2917                        series.hcEvents = ownEvents;
2918                    }
2919                    else {
2920                        delete series.hcEvents;
2921                    }
2922                }
2923            }
2924        }
2925        else {
2926            error(17, true, chart, { missingModuleFor: newType });
2927        }
2928        // Re-register groups (#3094) and other preserved properties
2929        preserve.forEach(function (prop) {
2930            series[prop] = preserve[prop];
2931        });
2932        series.init(chart, options);
2933        // Remove particular elements of the points. Check `series.options`
2934        // because we need to consider the options being set on plotOptions as
2935        // well.
2936        if (keepPoints && this.points) {
2937            seriesOptions = series.options;
2938            // What kind of elements to destroy
2939            if (seriesOptions.visible === false) {
2940                kinds.graphic = 1;
2941                kinds.dataLabel = 1;
2942            }
2943            else {
2944                // If the marker got disabled or changed its symbol, width or
2945                // height - destroy
2946                if (this.hasMarkerChanged(seriesOptions, oldOptions)) {
2947                    kinds.graphic = 1;
2948                }
2949                if (!series.hasDataLabels?.()) {
2950                    kinds.dataLabel = 1;
2951                }
2952            }
2953            for (const point of this.points) {
2954                if (point && point.series) {
2955                    point.resolveColor();
2956                    // Destroy elements in order to recreate based on updated
2957                    // series options.
2958                    if (Object.keys(kinds).length) {
2959                        point.destroyElements(kinds);
2960                    }
2961                    if (seriesOptions.showInLegend === false &&
2962                        point.legendItem) {
2963                        chart.legend.destroyItem(point);
2964                    }
2965                }
2966            }
2967        }
2968        series.initialType = initialType;
2969        chart.linkSeries(); // Links are lost in series.remove (#3028)
2970        // Set data for series with sorting enabled if it isn't set yet (#19715)
2971        chart.setSortedData();
2972        // #15383: Fire updatedData if the type has changed to keep linked
2973        // series such as indicators updated
2974        if (casting && series.linkedSeries.length) {
2975            series.isDirtyData = true;
2976        }
2977        fireEvent(this, 'afterUpdate');
2978        if (pick(redraw, true)) {
2979            chart.redraw(keepPoints ? void 0 : false);
2980        }
2981    }
2982    /**
2983     * Used from within series.update
2984     * @private
2985     */
2986    setName(name) {
2987        this.name = this.options.name = this.userOptions.name = name;
2988        this.chart.isDirtyLegend = true;
2989    }
2990    /**
2991     * Check if the option has changed.
2992     * @private
2993     */
2994    hasOptionChanged(optionName) {
2995        const chart = this.chart, option = this.options[optionName], plotOptions = chart.options.plotOptions, oldOption = this.userOptions[optionName], plotOptionsOption = pick(plotOptions?.[this.type]?.[optionName], plotOptions?.series?.[optionName]);
2996        // Check if `plotOptions` are defined already, #19203
2997        if (oldOption && !defined(plotOptionsOption)) {
2998            return option !== oldOption;
2999        }
3000        return option !== pick(plotOptionsOption, option);
3001    }
3002    /**
3003     * Runs on mouse over the series graphical items.
3004     *
3005     * @function Highcharts.Series#onMouseOver
3006     * @emits Highcharts.Series#event:mouseOver
3007     */
3008    onMouseOver() {
3009        const series = this, chart = series.chart, hoverSeries = chart.hoverSeries, pointer = chart.pointer;
3010        pointer?.setHoverChartIndex();
3011        // Set normal state to previous series
3012        if (hoverSeries && hoverSeries !== series) {
3013            hoverSeries.onMouseOut();
3014        }
3015        // Trigger the event, but to save processing time,
3016        // only if defined
3017        if (series.options.events.mouseOver) {
3018            fireEvent(series, 'mouseOver');
3019        }
3020        // Hover this
3021        series.setState('hover');
3022        /**
3023         * Contains the original hovered series.
3024         *
3025         * @name Highcharts.Chart#hoverSeries
3026         * @type {Highcharts.Series|null}
3027         */
3028        chart.hoverSeries = series;
3029    }
3030    /**
3031     * Runs on mouse out of the series graphical items.
3032     *
3033     * @function Highcharts.Series#onMouseOut
3034     *
3035     * @emits Highcharts.Series#event:mouseOut
3036     */
3037    onMouseOut() {
3038        // Trigger the event only if listeners exist
3039        const series = this, options = series.options, chart = series.chart, tooltip = chart.tooltip, hoverPoint = chart.hoverPoint;
3040        // #182, set to null before the mouseOut event fires
3041        chart.hoverSeries = null;
3042        // Trigger mouse out on the point, which must be in this series
3043        if (hoverPoint) {
3044            hoverPoint.onMouseOut();
3045        }
3046        // Fire the mouse out event
3047        if (series && options.events.mouseOut) {
3048            fireEvent(series, 'mouseOut');
3049        }
3050        // Hide the tooltip
3051        if (tooltip &&
3052            !series.stickyTracking &&
3053            (!tooltip.shared || series.noSharedTooltip)) {
3054            tooltip.hide();
3055        }
3056        // Reset all inactive states
3057        chart.series.forEach(function (s) {
3058            s.setState('', true);
3059        });
3060    }
3061    /**
3062     * Set the state of the series. Called internally on mouse interaction
3063     * operations, but it can also be called directly to visually
3064     * highlight a series.
3065     *
3066     * @function Highcharts.Series#setState
3067     *
3068     * @param {Highcharts.SeriesStateValue|""} [state]
3069     *        The new state, can be either `'hover'`, `'inactive'`, `'select'`,
3070     *        or `''` (an empty string), `'normal'` or `undefined` to set to
3071     *        normal state.
3072     * @param {boolean} [inherit]
3073     *        Determines if state should be inherited by points too.
3074     */
3075    setState(state, inherit) {
3076        const series = this, options = series.options, graph = series.graph, inactiveOtherPoints = options.inactiveOtherPoints, stateOptions = options.states, 
3077        // By default a quick animation to hover/inactive,
3078        // slower to un-hover
3079        stateAnimation = pick((stateOptions[state || 'normal'] &&
3080            stateOptions[state || 'normal'].animation), series.chart.options.chart.animation);
3081        let lineWidth = options.lineWidth, opacity = options.opacity;
3082        state = state || '';
3083        if (series.state !== state) {
3084            // Toggle class names
3085            [
3086                series.group,
3087                series.markerGroup,
3088                series.dataLabelsGroup
3089            ].forEach(function (group) {
3090                if (group) {
3091                    // Old state
3092                    if (series.state) {
3093                        group.removeClass('highcharts-series-' + series.state);
3094                    }
3095                    // New state
3096                    if (state) {
3097                        group.addClass('highcharts-series-' + state);
3098                    }
3099                }
3100            });
3101            series.state = state;
3102            if (!series.chart.styledMode) {
3103                if (stateOptions[state] &&
3104                    stateOptions[state].enabled === false) {
3105                    return;
3106                }
3107                if (state) {
3108                    lineWidth = (stateOptions[state].lineWidth ||
3109                        lineWidth + (stateOptions[state].lineWidthPlus || 0)); // #4035
3110                    opacity = pick(stateOptions[state].opacity, opacity);
3111                }
3112                if (graph && !graph.dashstyle && isNumber(lineWidth)) {
3113                    // Animate the graph stroke-width
3114                    for (const graphElement of [
3115                        graph,
3116                        ...this.zones.map((zone) => zone.graph)
3117                    ]) {
3118                        graphElement?.animate({
3119                            'stroke-width': lineWidth
3120                        }, stateAnimation);
3121                    }
3122                }
3123                // For some types (pie, networkgraph, sankey) opacity is
3124                // resolved on a point level
3125                if (!inactiveOtherPoints) {
3126                    [
3127                        series.group,
3128                        series.markerGroup,
3129                        series.dataLabelsGroup,
3130                        series.labelBySeries
3131                    ].forEach(function (group) {
3132                        if (group) {
3133                            group.animate({
3134                                opacity: opacity
3135                            }, stateAnimation);
3136                        }
3137                    });
3138                }
3139            }
3140        }
3141        // Don't loop over points on a series that doesn't apply inactive state
3142        // to siblings markers (e.g. line, column)
3143        if (inherit && inactiveOtherPoints && series.points) {
3144            series.setAllPointsToState(state || void 0);
3145        }
3146    }
3147    /**
3148     * Set the state for all points in the series.
3149     *
3150     * @function Highcharts.Series#setAllPointsToState
3151     *
3152     * @private
3153     *
3154     * @param {string} [state]
3155     *        Can be either `hover` or undefined to set to normal state.
3156     */
3157    setAllPointsToState(state) {
vendor: 6,275 bytes, lines 3158-3367
3158        this.points.forEach(function (point) {
3159            if (point.setState) {
3160                point.setState(state);
3161            }
3162        });
3163    }
3164    /**
3165     * Show or hide the series.
3166     *
3167     * @function Highcharts.Series#setVisible
3168     *
3169     * @param {boolean} [visible]
3170     * True to show the series, false to hide. If undefined, the visibility is
3171     * toggled.
3172     *
3173     * @param {boolean} [redraw=true]
3174     * Whether to redraw the chart after the series is altered. If doing more
3175     * operations on the chart, it is a good idea to set redraw to false and
3176     * call {@link Chart#redraw|chart.redraw()} after.
3177     *
3178     * @emits Highcharts.Series#event:hide
3179     * @emits Highcharts.Series#event:show
3180     */
3181    setVisible(vis, redraw) {
3182        const series = this, chart = series.chart, ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries, oldVisibility = series.visible;
3183        // If called without an argument, toggle visibility
3184        series.visible =
3185            vis =
3186                series.options.visible =
3187                    series.userOptions.visible =
3188                        typeof vis === 'undefined' ? !oldVisibility : vis; // #5618
3189        const showOrHide = vis ? 'show' : 'hide';
3190        // Show or hide elements
3191        [
3192            'group',
3193            'dataLabelsGroup',
3194            'markerGroup',
3195            'tracker',
3196            'tt'
3197        ].forEach((key) => {
3198            series[key]?.[showOrHide]();
3199        });
3200        // Hide tooltip (#1361)
3201        if (chart.hoverSeries === series ||
3202            chart.hoverPoint?.series === series) {
3203            series.onMouseOut();
3204        }
3205        if (series.legendItem) {
3206            chart.legend.colorizeItem(series, vis);
3207        }
3208        // Rescale or adapt to resized chart
3209        series.isDirty = true;
3210        // In a stack, all other series are affected
3211        if (series.options.stacking) {
3212            chart.series.forEach((otherSeries) => {
3213                if (otherSeries.options.stacking && otherSeries.visible) {
3214                    otherSeries.isDirty = true;
3215                }
3216            });
3217        }
3218        // Show or hide linked series
3219        series.linkedSeries.forEach((otherSeries) => {
3220            otherSeries.setVisible(vis, false);
3221        });
3222        if (ignoreHiddenSeries) {
3223            chart.isDirtyBox = true;
3224        }
3225        fireEvent(series, showOrHide);
3226        if (redraw !== false) {
3227            chart.redraw();
3228        }
3229    }
3230    /**
3231     * Show the series if hidden.
3232     *
3233     * @sample highcharts/members/series-hide/
3234     *         Toggle visibility from a button
3235     *
3236     * @function Highcharts.Series#show
3237     * @emits Highcharts.Series#event:show
3238     */
3239    show() {
3240        this.setVisible(true);
3241    }
3242    /**
3243     * Hide the series if visible. If the
3244     * [chart.ignoreHiddenSeries](https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries)
3245     * option is true, the chart is redrawn without this series.
3246     *
3247     * @sample highcharts/members/series-hide/
3248     *         Toggle visibility from a button
3249     *
3250     * @function Highcharts.Series#hide
3251     * @emits Highcharts.Series#event:hide
3252     */
3253    hide() {
3254        this.setVisible(false);
3255    }
3256    /**
3257     * Select or unselect the series. This means its
3258     * {@link Highcharts.Series.selected|selected}
3259     * property is set, the checkbox in the legend is toggled and when selected,
3260     * the series is returned by the {@link Highcharts.Chart#getSelectedSeries}
3261     * function.
3262     *
3263     * @sample highcharts/members/series-select/
3264     *         Select a series from a button
3265     *
3266     * @function Highcharts.Series#select
3267     *
3268     * @param {boolean} [selected]
3269     * True to select the series, false to unselect. If undefined, the selection
3270     * state is toggled.
3271     *
3272     * @emits Highcharts.Series#event:select
3273     * @emits Highcharts.Series#event:unselect
3274     */
3275    select(selected) {
3276        const series = this;
3277        series.selected =
3278            selected =
3279                this.options.selected = (typeof selected === 'undefined' ?
3280                    !series.selected :
3281                    selected);
3282        if (series.checkbox) {
3283            series.checkbox.checked = selected;
3284        }
3285        fireEvent(series, selected ? 'select' : 'unselect');
3286    }
3287    /**
3288     * Checks if a tooltip should be shown for a given point.
3289     *
3290     * @private
3291     */
3292    shouldShowTooltip(plotX, plotY, options = {}) {
3293        options.series = this;
3294        options.visiblePlotOnly = true;
3295        return this.chart.isInsidePlot(plotX, plotY, options);
3296    }
3297    /**
3298     * Draws the legend symbol based on the legendSymbol user option.
3299     *
3300     * @private
3301     */
3302    drawLegendSymbol(legend, item) {
3303        LegendSymbol[this.options.legendSymbol || 'rectangle']
3304            ?.call(this, legend, item);
3305    }
3306}
3307Series.defaultOptions = SeriesDefaults;
3308/**
3309 * Registry of all available series types.
3310 *
3311 * @name Highcharts.Series.types
3312 * @type {Highcharts.Dictionary<typeof_Highcharts.Series>}
3313 */
3314Series.types = SeriesRegistry.seriesTypes;
3315/* *
3316 *
3317 *  Static Functions
3318 *
3319 * */
3320/**
3321 * Registers a series class to be accessible via `Series.types`.
3322 *
3323 * @function Highcharts.Series.registerType
3324 *
3325 * @param {string} seriesType
3326 * The series type as an identifier string in lower case.
3327 *
3328 * @param {Function} SeriesClass
3329 * The series class as a class pattern or a constructor function with
3330 * prototype.
3331 */
3332Series.registerType = SeriesRegistry.registerSeriesType;
3333extend(Series.prototype, {
3334    axisTypes: ['xAxis', 'yAxis'],
3335    coll: 'series',
3336    colorCounter: 0,
3337    directTouch: false,
3338    invertible: true,
3339    isCartesian: true,
3340    kdAxisArray: ['clientX', 'plotY'],
3341    // Each point's x and y values are stored in this.xData and this.yData:
3342    parallelArrays: ['x', 'y'],
3343    pointClass: Point,
3344    requireSorting: true,
3345    // Requires the data to be sorted:
3346    sorted: true
3347});
3348/* *
3349 *
3350 *  Registry
3351 *
3352 * */
3353SeriesRegistry.series = Series;
3354/* *
3355 *
3356 *  Default Export
3357 *
3358 * */
3359export default Series;
3360/* *
3361 *
3362 *  API Declarations
3363 *
3364 * */
3365/**
3366 * This is a placeholder type of the possible series options for
3367 * [Highcharts](../highcharts/series), [Highcharts Stock](../highstock/series),
3368 * [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
3369 *
3370 * In TypeScript is this dynamically generated to reference all possible types
3371 * of series options.
3372 *
3373 * @ignore-declaration
3374 * @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
3375 */
3376/**
3377 * Options for `dataSorting`.
3378 *
3379 * @interface Highcharts.DataSortingOptionsObject
3380 * @since 8.0.0
3381 */ /**
3382* Enable or disable data sorting for the series.
3383* @name Highcharts.DataSortingOptionsObject#enabled
3384* @type {boolean|undefined}
3385*/ /**
3386* Whether to allow matching points by name in an update.
3387* @name Highcharts.DataSortingOptionsObject#matchByName
3388* @type {boolean|undefined}
3389*/ /**
3390* Determines what data value should be used to sort by.
3391* @name Highcharts.DataSortingOptionsObject#sortKey
3392* @type {string|undefined}
3393*/
3394/**
3395 * Function callback when a series has been animated.
3396 *
3397 * @callback Highcharts.SeriesAfterAnimateCallbackFunction
3398 *
3399 * @param {Highcharts.Series} this
3400 *        The series where the event occurred.
3401 *
3402 * @param {Highcharts.SeriesAfterAnimateEventObject} event
3403 *        Event arguments.
3404 */
3405/**
3406 * Event information regarding completed animation of a series.
3407 *
3408 * @interface Highcharts.SeriesAfterAnimateEventObject
3409 */ /**
3410* Animated series.
3411* @name Highcharts.SeriesAfterAnimateEventObject#target
3412* @type {Highcharts.Series}
3413*/ /**
3414* Event type.
3415* @name Highcharts.SeriesAfterAnimateEventObject#type
3416* @type {"afterAnimate"}
3417*/
3418/**
3419 * Function callback when the checkbox next to the series' name in the legend is
3420 * clicked.
3421 *
3422 * @callback Highcharts.SeriesCheckboxClickCallbackFunction
3423 *
3424 * @param {Highcharts.Series} this
3425 *        The series where the event occurred.
3426 *
3427 * @param {Highcharts.SeriesCheckboxClickEventObject} event
3428 *        Event arguments.
3429 */
3430/**
3431 * Event information regarding check of a series box.
3432 *
3433 * @interface Highcharts.SeriesCheckboxClickEventObject
3434 */ /**
3435* Whether the box has been checked.
3436* @name Highcharts.SeriesCheckboxClickEventObject#checked
3437* @type {boolean}
3438*/ /**
3439* Related series.
3440* @name Highcharts.SeriesCheckboxClickEventObject#item
3441* @type {Highcharts.Series}
3442*/ /**
3443* Related series.
3444* @name Highcharts.SeriesCheckboxClickEventObject#target
3445* @type {Highcharts.Series}
3446*/ /**
3447* Event type.
3448* @name Highcharts.SeriesCheckboxClickEventObject#type
3449* @type {"checkboxClick"}
3450*/
3451/**
3452 * Function callback when a series is clicked. Return false to cancel toogle
3453 * actions.
3454 *
3455 * @callback Highcharts.SeriesClickCallbackFunction
3456 *
3457 * @param {Highcharts.Series} this
3458 *        The series where the event occurred.
3459 *
3460 * @param {Highcharts.SeriesClickEventObject} event
3461 *        Event arguments.
3462 */
3463/**
3464 * Common information for a click event on a series.
3465 *
3466 * @interface Highcharts.SeriesClickEventObject
3467 * @extends global.Event
3468 */ /**
3469* Nearest point on the graph.
3470* @name Highcharts.SeriesClickEventObject#point
3471* @type {Highcharts.Point}
3472*/
3473/**
3474 * Gets fired when the series is hidden after chart generation time, either by
3475 * clicking the legend item or by calling `.hide()`.
3476 *
3477 * @callback Highcharts.SeriesHideCallbackFunction
3478 *
3479 * @param {Highcharts.Series} this
3480 *        The series where the event occurred.
3481 *
3482 * @param {global.Event} event
3483 *        The event that occurred.
3484 */
3485/**
3486 * The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
3487 * graph.
3488 *
3489 * @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
3490 */
3491/**
3492 * Gets fired when the legend item belonging to the series is clicked. The
3493 * default action is to toggle the visibility of the series. This can be
3494 * prevented by returning `false` or calling `event.preventDefault()`.
3495 *
3496 * **Note:** This option is deprecated in favor of
3497 * Highcharts.LegendItemClickCallbackFunction.
3498 *
3499 * @deprecated 11.4.4
3500 * @callback Highcharts.SeriesLegendItemClickCallbackFunction
3501 *
3502 * @param {Highcharts.Series} this
3503 *        The series where the event occurred.
3504 *
3505 * @param {Highcharts.SeriesLegendItemClickEventObject} event
3506 *        The event that occurred.
3507 */
3508/**
3509 * Information about the event.
3510 *
3511 * **Note:** This option is deprecated in favor of
3512 * Highcharts.LegendItemClickEventObject.
3513 *
3514 * @deprecated 11.4.4
3515 * @interface Highcharts.SeriesLegendItemClickEventObject
3516 */ /**
3517* Related browser event.
3518* @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
3519* @type {global.PointerEvent}
3520*/ /**
3521* Prevent the default action of toggle the visibility of the series.
3522* @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
3523* @type {Function}
3524*/ /**
3525* Related series.
3526* @name Highcharts.SeriesCheckboxClickEventObject#target
3527* @type {Highcharts.Series}
3528*/ /**
3529* Event type.
3530* @name Highcharts.SeriesCheckboxClickEventObject#type
3531* @type {"checkboxClick"}
3532*/
3533/**
3534 * Gets fired when the mouse leaves the graph.
3535 *
3536 * @callback Highcharts.SeriesMouseOutCallbackFunction
3537 *
3538 * @param {Highcharts.Series} this
3539 *        Series where the event occurred.
3540 *
3541 * @param {global.PointerEvent} event
3542 *        Event that occurred.
3543 */
3544/**
3545 * Gets fired when the mouse enters the graph.
3546 *
3547 * @callback Highcharts.SeriesMouseOverCallbackFunction
3548 *
3549 * @param {Highcharts.Series} this
3550 *        Series where the event occurred.
3551 *
3552 * @param {global.PointerEvent} event
3553 *        Event that occurred.
3554 */
3555/**
3556 * Translation and scale for the plot area of a series.
3557 *
3558 * @interface Highcharts.SeriesPlotBoxObject
3559 */ /**
3560* @name Highcharts.SeriesPlotBoxObject#scaleX
3561* @type {number}
3562*/ /**
3563* @name Highcharts.SeriesPlotBoxObject#scaleY
3564* @type {number}
3565*/ /**
3566* @name Highcharts.SeriesPlotBoxObject#translateX
3567* @type {number}
3568*/ /**
3569* @name Highcharts.SeriesPlotBoxObject#translateY
3570* @type {number}
3571*/
3572/**
3573 * Gets fired when the series is shown after chart generation time, either by
3574 * clicking the legend item or by calling `.show()`.
3575 *
3576 * @callback Highcharts.SeriesShowCallbackFunction
3577 *
3578 * @param {Highcharts.Series} this
3579 *        Series where the event occurred.
3580 *
3581 * @param {global.Event} event
3582 *        Event that occurred.
3583 */
3584/**
3585 * Possible key values for the series state options.
3586 *
3587 * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
3588 */
3589''; // Detach doclets above
3590/* *
3591 *
3592 *  API Options
3593 *
3594 * */
3595/**
3596 * Series options for specific data and the data itself. In TypeScript you
3597 * have to cast the series options to specific series types, to get all
3598 * possible options for a series.
3599 *
3600 * @example
3601 * // TypeScript example
3602 * Highcharts.chart('container', {
3603 *     series: [{
3604 *         color: '#06C',
3605 *         data: [[0, 1], [2, 3]]
3606 *     } as Highcharts.SeriesLineOptions ]
3607 * });
3608 *
3609 * @type      {Array<*>}
3610 * @apioption series
3611 */
3612/**
3613 * An id for the series. This can be used after render time to get a pointer
3614 * to the series object through `chart.get()`.
3615 *
3616 * @sample {highcharts} highcharts/plotoptions/series-id/
3617 *         Get series by id
3618 *
3619 * @type      {string}
3620 * @since     1.2.0
3621 * @apioption series.id
3622 */
3623/**
3624 * The index of the series in the chart, affecting the internal index in the
3625 * `chart.series` array, the visible Z index as well as the order in the
3626 * legend.
3627 *
3628 * @type      {number}
3629 * @since     2.3.0
3630 * @apioption series.index
3631 */
3632/**
3633 * The sequential index of the series in the legend.
3634 *
3635 * @see [legend.reversed](#legend.reversed),
3636 *      [yAxis.reversedStacks](#yAxis.reversedStacks)
3637 *
3638 * @sample {highcharts|highstock} highcharts/series/legendindex/
3639 *         Legend in opposite order
3640 *
3641 * @type      {number}
3642 * @apioption series.legendIndex
3643 */
3644/**
3645 * The name of the series as shown in the legend, tooltip etc.
3646 *
3647 * @sample {highcharts} highcharts/series/name/
3648 *         Series name
3649 * @sample {highmaps} maps/demo/category-map/
3650 *         Series name
3651 *
3652 * @type      {string}
3653 * @apioption series.name
3654 */
3655/**
3656 * This option allows grouping series in a stacked chart. The stack option
3657 * can be a string or anything else, as long as the grouped series' stack
3658 * options match each other after conversion into a string.
3659 *
3660 * @sample {highcharts} highcharts/series/stack/
3661 *         Stacked and grouped columns
3662 * @sample {highcharts} highcharts/series/stack-centerincategory/
3663 *         Stacked and grouped, centered in category
3664 *
3665 * @type      {number|string}
3666 * @since     2.1
3667 * @product   highcharts highstock
3668 * @apioption series.stack
3669 */
3670/**
3671 * The type of series, for example `line` or `column`. By default, the
3672 * series type is inherited from [chart.type](#chart.type), so unless the
3673 * chart is a combination of series types, there is no need to set it on the
3674 * series level.
3675 *
3676 * @sample {highcharts} highcharts/series/type/
3677 *         Line and column in the same chart
3678 * @sample highcharts/series/type-dynamic/
3679 *         Dynamic types with button selector
3680 * @sample {highmaps} maps/demo/mapline-mappoint/
3681 *         Multiple types in the same map
3682 *
3683 * @type      {string}
3684 * @apioption series.type
3685 */
3686/**
3687 * When using dual or multiple x axes, this number defines which xAxis the
3688 * particular series is connected to. It refers to either the
3689 * {@link #xAxis.id|axis id}
3690 * or the index of the axis in the xAxis array, with 0 being the first.
3691 *
3692 * @type      {number|string}
3693 * @default   0
3694 * @product   highcharts highstock
3695 * @apioption series.xAxis
3696 */
3697/**
3698 * When using dual or multiple y axes, this number defines which yAxis the
3699 * particular series is connected to. It refers to either the
3700 * {@link #yAxis.id|axis id}
3701 * or the index of the axis in the yAxis array, with 0 being the first.
3702 *
3703 * @sample {highcharts} highcharts/series/yaxis/
3704 *         Apply the column series to the secondary Y axis
3705 *
3706 * @type      {number|string}
3707 * @default   0
3708 * @product   highcharts highstock
3709 * @apioption series.yAxis
3710 */
3711/**
3712 * Define the visual z index of the series.
3713 *
3714 * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
3715 *         With no z index, the series defined last are on top
3716 * @sample {highcharts} highcharts/plotoptions/series-zindex/
3717 *         With a z index, the series with the highest z index is on top
3718 * @sample {highstock} highcharts/plotoptions/series-zindex-default/
3719 *         With no z index, the series defined last are on top
3720 * @sample {highstock} highcharts/plotoptions/series-zindex/
3721 *         With a z index, the series with the highest z index is on top
3722 *
3723 * @type      {number}
3724 * @product   highcharts highstock
3725 * @apioption series.zIndex
3726 */
3727''; // Include precedent doclets in transpiled

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.