PageSourceSearch

https://www.ain-tourisme.com/app/dist/adt-ain/addons/woody-library…/code/es-modules/Core/Legend/Legend.js

js ain-tourisme.com collected 2026-10-02 05:49:40 UTC 51,568 bytes, 1,364 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 F from '../Foundation.js';
14const { registerEventOptions } = F;
15import H from '../Globals.js';
16const { composed, marginNames } = H;
17import Series from '../Series/Series.js';
18import Point from '../Series/Point.js';
19import R from '../Renderer/RendererUtilities.js';
20const { distribute } = R;
21import T from '../Templating.js';
22const { format } = T;
23import U from '../Utilities.js';
24const { addEvent, createElement, css, defined, discardElement, find, fireEvent, isNumber, merge, pick, pushUnique, relativeLength, stableSort, syncTimeout } = U;
25/* *
26 *
27 *  Class
28 *
29 * */
30/**
31 * The overview of the chart's series. The legend object is instantiated
32 * internally in the chart constructor, and is available from the `chart.legend`
33 * property. Each chart has only one legend.
34 *
35 * @class
36 * @name Highcharts.Legend
37 *
38 * @param {Highcharts.Chart} chart
39 * The chart instance.
40 *
41 * @param {Highcharts.LegendOptions} options
42 * Legend options.
43 */
44class Legend {
45    /* *
46     *
47     *  Functions
48     *
49     * */
50    /**
51     * Initialize the legend.
52     *
53     * @private
54     * @function Highcharts.Legend#init
55     *
56     * @param {Highcharts.Chart} chart
57     * The chart instance.
58     *
59     * @param {Highcharts.LegendOptions} options
60     * Legend options.
61     */
62    constructor(chart, options) {
63        /* *
64         *
65         *  Properties
66         *
67         * */
68        this.allItems = [];
69        this.initialItemY = 0;
70        this.itemHeight = 0;
71        this.itemMarginBottom = 0;
72        this.itemMarginTop = 0;
73        this.itemX = 0;
74        this.itemY = 0;
75        this.lastItemY = 0;
76        this.lastLineHeight = 0;
77        this.legendHeight = 0;
78        this.legendWidth = 0;
79        this.maxItemWidth = 0;
80        this.maxLegendWidth = 0;
81        this.offsetWidth = 0;
82        this.padding = 0;
83        this.pages = [];
84        this.symbolHeight = 0;
85        this.symbolWidth = 0;
86        this.titleHeight = 0;
87        this.totalItemWidth = 0;
88        this.widthOption = 0;
89        /**
90         * Chart of this legend.
91         *
92         * @readonly
93         * @name Highcharts.Legend#chart
94         * @type {Highcharts.Chart}
95         */
96        this.chart = chart;
97        this.setOptions(options);
98        if (options.enabled) {
99            // Render it
100            this.render();
101            registerEventOptions(this, options);
102            // Move checkboxes
103            addEvent(this.chart, 'endResize', function () {
104                this.legend.positionCheckboxes();
105            });
106        }
107        // On Legend.init and Legend.update, make sure that proximate layout
108        // events are either added or removed (#18362).
109        addEvent(this.chart, 'render', () => {
110            if (this.options.enabled && this.proximate) {
111                this.proximatePositions();
112                this.positionItems();
113            }
114        });
115    }
116    /**
117     * @private
118     * @function Highcharts.Legend#setOptions
119     * @param {Highcharts.LegendOptions} options
120     */
121    setOptions(options) {
122        const padding = pick(options.padding, 8);
123        /**
124         * Legend options.
125         *
126         * @readonly
127         * @name Highcharts.Legend#options
128         * @type {Highcharts.LegendOptions}
129         */
130        this.options = options;
131        if (!this.chart.styledMode) {
132            this.itemStyle = options.itemStyle;
133            this.itemHiddenStyle = merge(this.itemStyle, options.itemHiddenStyle);
134        }
135        this.itemMarginTop = options.itemMarginTop;
136        this.itemMarginBottom = options.itemMarginBottom;
137        this.padding = padding;
138        this.initialItemY = padding - 5; // 5 is pixels above the text
139        this.symbolWidth = pick(options.symbolWidth, 16);
140        this.pages = [];
141        this.proximate = options.layout === 'proximate' && !this.chart.inverted;
142        // #12705: baseline has to be reset on every update
143        this.baseline = void 0;
144    }
145    /**
146     * Update the legend with new options. Equivalent to running `chart.update`
147     * with a legend configuration option.
148     *
149     * @sample highcharts/legend/legend-update/
150     *         Legend update
151     *
152     * @function Highcharts.Legend#update
153     *
154     * @param {Highcharts.LegendOptions} options
155     * Legend options.
156     *
157     * @param {boolean} [redraw=true]
158     * Whether to redraw the chart after the axis is altered. If doing more
159     * operations on the chart, it is a good idea to set redraw to fal
159se and
160     * call {@link Chart#redraw} after. Whether to redraw the chart.
161     *
162     * @emits Highcharts.Legends#event:afterUpdate
163     */
164    update(options, redraw) {
165        const chart = this.chart;
166        this.setOptions(merge(true, this.options, options));
167        if ('events' in this.options) {
168            // Legend event handlers
169            registerEventOptions(this, this.options);
170        }
171        this.destroy();
172        chart.isDirtyLegend = chart.isDirtyBox = true;
173        if (pick(redraw, true)) {
174            chart.redraw();
175        }
176        fireEvent(this, 'afterUpdate', { redraw });
177    }
178    /**
179     * Set the colors for the legend item.
180     *
181     * @private
182     * @function Highcharts.Legend#colorizeItem
183     * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
184     *        A Series or Point instance
185     * @param {boolean} [visible=false]
186     *        Dimmed or colored
187     *
188     * @todo
189     * Make events official: Fires the event `afterColorizeItem`.
190     */
191    colorizeItem(item, visible) {
192        const originalColor = item.color, { area, group, label, line, symbol } = item.legendItem || {};
193        if (item instanceof Series || item instanceof Point) {
194            item.color = item.options?.legendSymbolColor || originalColor;
195        }
196        group?.[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden');
197        if (!this.chart.styledMode) {
198            const { itemHiddenStyle = {} } = this, hiddenColor = itemHiddenStyle.color, { fillColor, fillOpacity, lineColor, marker } = item.options, colorizeHidden = (attr) => {
199                if (!visible) {
200                    if (attr.fill) {
201                        attr.fill = hiddenColor;
202                    }
203                    if (attr.stroke) {
204                        attr.stroke = hiddenColor;
205                    }
206                }
207                return attr;
208            };
209            label?.css(merge(visible ? this.itemStyle : itemHiddenStyle));
210            line?.attr(colorizeHidden({ stroke: lineColor || item.color }));
211            if (symbol) {
212                // Apply marker options
213                symbol.attr(colorizeHidden(marker && symbol.isMarker ? // #585
214                    item.pointAttribs() :
215                    { fill: item.color }));
216            }
217            area?.attr(colorizeHidden({
218                fill: fillColor || item.color,
219                'fill-opacity': fillColor ? 1 : (fillOpacity ?? 0.75)
220            }));
221        }
222        item.color = originalColor;
223        fireEvent(this, 'afterColorizeItem', { item, visible });
224    }
225    /**
226     * @private
227     * @function Highcharts.Legend#positionItems
228     */
229    positionItems() {
230        // Now that the legend width and height are established, put the items
231        // in the final position
232        this.allItems.forEach(this.positionItem, this);
233        if (!this.chart.isResizing) {
234            this.positionCheckboxes();
235        }
236    }
237    /**
238     * Position the legend item.
239     *
240     * @private
241     * @function Highcharts.Legend#positionItem
242     * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
243     * The item to position
244     */
245    positionItem(item) {
246        const legend = this, { group, x = 0, y = 0 } = item.legendItem || {}, options = legend.options, symbolPadding = options.symbolPadding, ltr = !options.rtl, checkbox = item.checkbox;
247        if (group && group.element) {
248            const attribs = {
249                translateX: ltr ?
250                    x :
251                    legend.legendWidth - x - 2 * symbolPadding - 4,
252                translateY: y
253            };
254            const complete = () => {
255                fireEvent(this, 'afterPositionItem', { item });
256            };
257            group[defined(group.translateY) ? 'animate' : 'attr'](attribs, void 0, complete);
258        }
259        if (checkbox) {
260            checkbox.x = x;
261            checkbox.y = y;
262        }
263    }
264    /**
265     * Destroy a single legend item, used internally on removing series items.
266     *
267     * @private
268     * @function Highcharts.Legend#destroyItem
269     * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
270     * The item to remove
271     */
272    destroyItem(item) {
273        const checkbox = item.checkbox, legendItem = item.legendItem || {};
274        // Destroy SVG elements
275        for (const key of ['group', 'label', 'line', 'symbol']) {
276            if (legendItem[key]) {
277                legendItem[key] = legendItem[key].destroy();
278            }
279        }
280        if (checkbox) {
281            discardElement(checkbox);
282        }
283        item.legendItem = void 0;
284    }
285    /**
286     * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
287     * must be called after destruction.
288     *
289     * @private
290     * @function Highcharts.Legend#destroy
291     */
292    destroy() {
293        const legend = this;
294        // Destroy items
295        for (const item of this.getAllItems()) {
296            this.destroyItem(item);
297        }
298        // Destroy legend elements
299        for (const key of [
300            'clipRect',
301            'up',
302            'down',
303            'pager',
304            'nav',
305            'box',
306            'title',
307            'group'
308        ]) {
309            if (legend[key]) {
310                legend[key] = legend[key].destroy();
311            }
312        }
313        this.display = null; // Reset in .render on update.
314    }
315    /**
316     * Position the checkboxes after the width is determined.
317     *
318     * @private
319     * @function Highcharts.Legend#positionCheckboxes
320     */
321    positionCheckboxes() {
322        const alignAttr = this.group && this.group.alignAttr, clipHeight = this.clipHeight || this.legendHeight, titleHeight = this.titleHeight;
323        let translateY;
324        if (alignAttr) {
325            translateY = alignAttr.translateY;
vendor: 4,451 bytes, lines 326-425
326            this.allItems.forEach(function (item) {
327                const checkbox = item.checkbox;
328                let top;
329                if (checkbox) {
330                    top = translateY + titleHeight + checkbox.y +
331                        (this.scrollOffset || 0) + 3;
332                    css(checkbox, {
333                        left: (alignAttr.translateX + item.checkboxOffset +
334                            checkbox.x - 20) + 'px',
335                        top: top + 'px',
336                        display: this.proximate || (top > translateY - 6 &&
337                            top < translateY + clipHeight - 6) ?
338                            '' :
339                            'none'
340                    });
341                }
342            }, this);
343        }
344    }
345    /**
346     * Render the legend title on top of the legend.
347     *
348     * @private
349     * @function Highcharts.Legend#renderTitle
350     */
351    renderTitle() {
352        const options = this.options, padding = this.padding, titleOptions = options.title;
353        let bBox, titleHeight = 0;
354        if (titleOptions.text) {
355            if (!this.title) {
356                /**
357                 * SVG element of the legend title.
358                 *
359                 * @readonly
360                 * @name Highcharts.Legend#title
361                 * @type {Highcharts.SVGElement}
362                 */
363                this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, void 0, void 0, void 0, options.useHTML, void 0, 'legend-title')
364                    .attr({ zIndex: 1 });
365                if (!this.chart.styledMode) {
366                    this.title.css(titleOptions.style);
367                }
368                this.title.add(this.group);
369            }
370            // Set the max title width (#7253)
371            if (!titleOptions.width) {
372                this.title.css({
373                    width: this.maxLegendWidth + 'px'
374                });
375            }
376            bBox = this.title.getBBox();
377            titleHeight = bBox.height;
378            this.offsetWidth = bBox.width; // #1717
379            this.contentGroup.attr({ translateY: titleHeight });
380        }
381        this.titleHeight = titleHeight;
382    }
383    /**
384     * Set the legend item text.
385     *
386     * @function Highcharts.Legend#setText
387     * @param {Highcharts.Point|Highcharts.Series} item
388     *        The item for which to update the text in the legend.
389     */
390    setText(item) {
391        const options = this.options;
392        item.legendItem.label.attr({
393            text: options.labelFormat ?
394                format(options.labelFormat, item, this.chart) :
395                options.labelFormatter.call(item)
396        });
397    }
398    /**
399     * Render a single specific legend item. Called internally from the `render`
400     * function.
401     *
402     * @private
403     * @function Highcharts.Legend#renderItem
404     * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
405     * The item to render.
406     */
407    renderItem(item) {
408        const legend = this, legendItem = item.legendItem = item.legendItem || {}, chart = legend.chart, renderer = chart.renderer, options = legend.options, horizontal = options.layout === 'horizontal', symbolWidth = legend.symbolWidth, symbolPadding = options.symbolPadding || 0, itemStyle = legend.itemStyle, itemHiddenStyle = legend.itemHiddenStyle, itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, ltr = !options.rtl, isSeries = !item.series, series = !isSeries && item.series.drawLegendSymbol ?
409            item.series :
410            item, seriesOptions = series.options, showCheckbox = (!!legend.createCheckboxForItem &&
411            seriesOptions &&
412            seriesOptions.showCheckbox), useHTML = options.useHTML, itemClassName = item.options.className;
413        let label = legendItem.label, 
414        // Full width minus text width
415        itemExtraWidth = symbolWidth + symbolPadding +
416            itemDistance + (showCheckbox ? 20 : 0);
417        if (!label) { // Generate it once, later move it
418            // Generate the group box, a group to hold the symbol and text. Text
419            // is to be appended in Legend class.
420            legendItem.group = renderer
421                .g('legend-item')
422                .addClass('highcharts-' + series.type + '-series ' +
423                'highcharts-color-' + item.colorIndex +
424                (itemClassName ? ' ' + itemClassName : '') +
425                (isSeries ?
vendor: 5,942 bytes, lines 426-557
426                    ' highcharts-series-' + item.index :
427                    ''))
428                .attr({ zIndex: 1 })
429                .add(legend.scrollGroup);
430            // Generate the list item text and add it to the group
431            legendItem.label = label = renderer.text('', ltr ?
432                symbolWidth + symbolPadding :
433                -symbolPadding, legend.baseline || 0, useHTML);
434            if (!chart.styledMode) {
435                // Merge to prevent modifying original (#1021)
436                label.css(merge(item.visible ?
437                    itemStyle :
438                    itemHiddenStyle));
439            }
440            label
441                .attr({
442                align: ltr ? 'left' : 'right',
443                zIndex: 2
444            })
445                .add(legendItem.group);
446            // Get the baseline for the first item - the font size is equal for
447            // all
448            if (!legend.baseline) {
449                legend.fontMetrics = renderer.fontMetrics(label);
450                legend.baseline =
451                    legend.fontMetrics.f + 3 + legend.itemMarginTop;
452                label.attr('y', legend.baseline);
453                legend.symbolHeight =
454                    pick(options.symbolHeight, legend.fontMetrics.f);
455                if (options.squareSymbol) {
456                    legend.symbolWidth = pick(options.symbolWidth, Math.max(legend.symbolHeight, 16));
457                    itemExtraWidth = legend.symbolWidth + symbolPadding +
458                        itemDistance + (showCheckbox ? 20 : 0);
459                    if (ltr) {
460                        label.attr('x', legend.symbolWidth + symbolPadding);
461                    }
462                }
463            }
464            // Draw the legend symbol inside the group box
465            series.drawLegendSymbol(legend, item);
466            if (legend.setItemEvents) {
467                legend.setItemEvents(item, label, useHTML);
468            }
469        }
470        // Add the HTML checkbox on top
471        if (showCheckbox && !item.checkbox && legend.createCheckboxForItem) {
472            legend.createCheckboxForItem(item);
473        }
474        // Colorize the items
475        legend.colorizeItem(item, item.visible);
476        // Take care of max width and text overflow (#6659)
477        if (chart.styledMode || !itemStyle.width) {
478            label.css({
479                width: ((options.itemWidth ||
480                    legend.widthOption ||
481                    chart.spacingBox.width) - itemExtraWidth) + 'px'
482            });
483        }
484        // Always update the text
485        legend.setText(item);
486        // Calculate the positions for the next line
487        const bBox = label.getBBox();
488        const fontMetricsH = (legend.fontMetrics && legend.fontMetrics.h) || 0;
489        item.itemWidth = item.checkboxOffset =
490            options.itemWidth ||
491                legendItem.labelWidth ||
492                bBox.width + itemExtraWidth;
493        legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
494        legend.totalItemWidth += item.itemWidth;
495        legend.itemHeight = item.itemHeight = Math.round(legendItem.labelHeight ||
496            // Use bBox for multiline (#16398)
497            (bBox.height > fontMetricsH * 1.5 ? bBox.height : fontMetricsH));
498    }
499    /**
500     * Get the position of the item in the layout. We now know the
501     * maxItemWidth from the previous loop.
502     *
503     * @private
504     * @function Highcharts.Legend#layoutItem
505     * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
506     */
507    layoutItem(item) {
508        const options = this.options, padding = this.padding, horizontal = options.layout === 'horizontal', itemHeight = item.itemHeight, itemMarginBottom = this.itemMarginBottom, itemMarginTop = this.itemMarginTop, itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, maxLegendWidth = this.maxLegendWidth, itemWidth = (options.alignColumns &&
509            this.totalItemWidth > maxLegendWidth) ?
510            this.maxItemWidth :
511            item.itemWidth, legendItem = item.legendItem || {};
512        // If the item exceeds the width, start a new line
513        if (horizontal &&
514            this.itemX - padding + itemWidth > maxLegendWidth) {
515            this.itemX = padding;
516            if (this.lastLineHeight) { // Not for the first line (#10167)
517                this.itemY += (itemMarginTop +
518                    this.lastLineHeight +
519                    itemMarginBottom);
520            }
521            this.lastLineHeight = 0; // Reset for next line (#915, #3976)
522        }
523        // Set the edge positions
524        this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
525        this.lastLineHeight = Math.max(// #915
526        itemHeight, this.lastLineHeight);
527        // Cache the position of the newly generated or reordered items
528        legendItem.x = this.itemX;
529        legendItem.y = this.itemY;
530        // Advance
531        if (horizontal) {
532            this.itemX += itemWidth;
533        }
534        else {
535            this.itemY +=
536                itemMarginTop + itemHeight + itemMarginBottom;
537            this.lastLineHeight = itemHeight;
538        }
539        // The width of the widest item
540        this.offsetWidth = this.widthOption || Math.max((horizontal ? this.itemX - padding - (item.checkbox ?
541            // Decrease by itemDistance only when no checkbox #4853
542            0 :
543            itemDistance) : itemWidth) + padding, this.offsetWidth);
544    }
545    /**
546     * Get all items, which is one item per series for most series and one
547     * item per point for pie series and its derivatives. Fires the event
548     * `afterGetAllItems`.
549     *
550     * @private
551     * @function Highcharts.Legend#getAllItems
552     * @return {Array<(Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series)>}
553     * The current items in the legend.
554     * @emits Highcharts.Legend#event:afterGetAllItems
555     */
556    getAllItems() {
557        let allItems = [];
558        this.chart.series.forEach(function (series) {
559            const seriesOptions = series && series.options;
560            // Handle showInLegend. If the series is linked to another series,
561            // defaults to false.
562            if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
563                // Use points or series for the legend item depending on
564                // legendType
565                allItems = allItems.concat((series.legendItem || {}).labels ||
566                    (seriesOptions.legendType === 'point' ?
567                        series.data :
568                        series));
569            }
570        });
571        fireEvent(this, 'afterGetAllItems', { allItems });
572        return allItems;
573    }
574    /**
575     * Get a short, three letter string reflecting the alignment and layout.
576     *
577     * @private
578     * @function Highcharts.Legend#getAlignment
579     * @return {string}
580     * The alignment, empty string if floating
581     */
582    getAlignment() {
583        const options = this.options;
584        // Use the first letter of each alignment option in order to detect
585        // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
586        if (this.proximate) {
587            return options.align.charAt(0) + 'tv';
588        }
589        return options.floating ? '' : (options.align.charAt(0) +
590            options.verticalAlign.charAt(0) +
591            options.layout.charAt(0));
592    }
593    /**
594     * Adjust the chart margins by reserving space for the legend on only one
595     * side of the chart. If the position is set to a corner, top or bottom is
596     * reserved for horizontal legends and left or right for vertical ones.
597     *
598     * @private
599     * @function Highcharts.Legend#adjustMargins
600     * @param {Array<number>} margin
601     * @param {Array<number>} spacing
602     */
603    adjustMargins(margin, spacing) {
604        const chart = this.chart, options = this.options, alignment = this.getAlignment();
605        if (alignment) {
606            ([
607                /(lth|ct|rth)/,
608                /(rtv|rm|rbv)/,
609                /(rbh|cb|lbh)/,
610                /(lbv|lm|ltv)/
611            ]).forEach(function (alignments, side) {
612                if (alignments.test(alignment) && !defined(margin[side])) {
613                    // Now we have detected on which side of the chart we should
614                    // reserve space for the legend
615                    chart[marginNames[side]] = Math.max(chart[marginNames[side]], (chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] +
616                        [1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] +
617                        pick(options.margin, 12) +
618                        spacing[side] +
619                        (chart.titleOffset[side] || 0)));
620                }
621            });
622        }
623    }
624    /**
625     * @private
626     * @function Highcharts.Legend#proximatePositions
627     */
628    proximatePositions() {
629        const chart = this.chart, boxes = [], alignLeft = this.options.align === 'left';
630        this.allItems.forEach(function (item) {
631            let lastPoint, height, useFirstPoint = alignLeft, target, top;
632            if (item.yAxis) {
633                if (item.xAxis.options.reversed) {
634                    useFirstPoint = !useFirstPoint;
635                }
636                if (item.points) {
637                    lastPoint = find(useFirstPoint ?
638                        item.points :
639                        item.points.slice(0).reverse(), function (item) {
640                        return isNumber(item.plotY);
641                    });
642                }
643                height = this.itemMarginTop +
644                    item.legendItem.label.getBBox().height +
645                    this.itemMarginBottom;
646                top = item.yAxis.top - chart.plotTop;
647                if (item.visible) {
648                    target = lastPoint ?
649                        lastPoint.plotY :
650                        item.yAxis.height;
651                    target += top - 0.3 * height;
652                }
653                else {
654                    target = top + item.yAxis.height;
655                }
656                boxes.push({
657                    target: target,
658                    size: height,
659                    item
660                });
661            }
662        }, this);
663        let legendItem;
664        for (const box of distribute(boxes, chart.plotHeight)) {
665            legendItem = box.item.legendItem || {};
666            if (isNumber(box.pos)) {
667                legendItem.y = chart.plotTop - chart.spacing[0] + box.pos;
668            }
669        }
670    }
671    /**
672     * Render the legend. This method can be called both before and after
673     * `chart.render`. If called after, it will only rearrange items instead
674     * of creating new ones. Called internally on initial render and after
675     * redraws.
676     *
677     * @private
678     * @function Highcharts.Legend#render
679     */
680    render() {
681        const legend = this, chart = legend.chart, renderer = chart.renderer, options = legend.options, padding = legend.padding, 
682        // Add each series or point
683        allItems = legend.getAllItems();
684        let display, legendWidth, legendHeight, legendGroup = legend.group, allowedWidth, box = legend.box;
685        legend.itemX = padding;
686        legend.itemY = legend.initialItemY;
687        legend.offsetWidth = 0;
688        legend.lastItemY = 0;
689        legend.widthOption = relativeLength(options.width, chart.spacingBox.width - padding);
690        // Compute how wide the legend is allowed to be
691        allowedWidth = chart.spacingBox.width - 2 * padding - options.x;
692        if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
693            allowedWidth /= 2;
694        }
695        legend.maxLegendWidth = legend.widthOption || allowedWidth;
696        if (!legendGroup) {
697            /**
698             * SVG group of the legend.
699             *
700             * @readonly
701             * @name Highcharts.Legend#group
702             * @type {Highcharts.SVGElement}
703             */
704            legend.group = legendGroup = renderer
705                .g('legend')
706                .addClass(options.className || '')
707                .attr({ zIndex: 7 })
708                .add();
709            legend.contentGroup = renderer
710                .g()
711                .attr({ zIndex: 1 }) // Above background
712                .add(legendGroup);
713            legend.scrollGroup = renderer
714                .g()
715                .add(legend.contentGroup);
716        }
717        legend.renderTitle();
718        // Sort by legendIndex
719        stableSort(allItems, (a, b) => ((a.options && a.options.legendIndex) || 0) -
720            ((b.options && b.options.legendIndex) || 0));
721        // Reversed legend
722        if (options.reversed) {
723            allItems.reverse();
724        }
725        /**
726         * All items for the legend, which is an array of series for most series
727         * and an array of points for pie series and its derivatives.
728         *
729         * @readonly
730         * @name Highcharts.Legend#allItems
731         * @type {Array<(Highcharts.Point|Highcharts.Series)>}
732         */
733        legend.allItems = allItems;
734        legend.display = display = !!allItems.length;
735        // Render the items. First we run a loop to set the text and properties
736        // and read all the bounding boxes. The next loop computes the item
737        // positions based on the bounding boxes.
738        legend.lastLineHeight = 0;
739        legend.maxItemWidth = 0;
740        legend.totalItemWidth = 0;
741        legend.itemHeight = 0;
742        allItems.forEach(legend.renderItem, legend);
743        allItems.forEach(legend.layoutItem, legend);
744        // Get the box
745        legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
746        legendHeight = legend.lastItemY + legend.lastLineHeight +
747            legend.titleHeight;
748        legendHeight = legend.handleOverflow(legendHeight);
749        legendHeight += padding;
750        // Draw the border and/or background
751        if (!box) {
752            /**
753             * SVG element of the legend box.
754             *
755             * @readonly
756             * @name Highcharts.Legend#box
757             * @type {Highcharts.SVGElement}
758             */
759            legend.box = box = renderer.rect()
760                .addClass('highcharts-legend-box')
761                .attr({
762                r: options.borderRadius
763            })
764                .add(legendGroup);
765        }
766        // Presentational
767        if (!chart.styledMode) {
768            box
769                .attr({
770                stroke: options.borderColor,
771                'stroke-width': options.borderWidth || 0,
772                fill: options.backgroundColor || 'none'
773            })
774                .shadow(options.shadow);
775        }
776        if (legendWidth > 0 && legendHeight > 0) {
777            box[box.placed ? 'animate' : 'attr'](box.crisp.call({}, {
778                x: 0,
779                y: 0,
780                width: legendWidth,
781                height: legendHeight
782            }, box.strokeWidth()));
783        }
784        // Hide the border if no items
785        legendGroup[display ? 'show' : 'hide']();
786        // Open for responsiveness
787        if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
788            legendWidth = legendHeight = 0;
789        }
790        legend.legendWidth = legendWidth;
791        legend.legendHeight = legendHeight;
792        if (display) {
793            legend.align();
794        }
795        if (!this.proximate) {
796            this.positionItems();
797        }
vendor: 10,536 bytes, lines 798-1059
798        fireEvent(this, 'afterRender');
799    }
800    /**
801     * Align the legend to chart's box.
802     *
803     * @private
804     * @function Highcharts.align
805     * @param {Highcharts.BBoxObject} alignTo
806     */
807    align(alignTo = this.chart.spacingBox) {
808        const chart = this.chart, options = this.options;
809        // If aligning to the top and the layout is horizontal, adjust for
810        // the title (#7428)
811        let y = alignTo.y;
812        if (/(lth|ct|rth)/.test(this.getAlignment()) &&
813            chart.titleOffset[0] > 0) {
814            y += chart.titleOffset[0];
815        }
816        else if (/(lbh|cb|rbh)/.test(this.getAlignment()) &&
817            chart.titleOffset[2] > 0) {
818            y -= chart.titleOffset[2];
819        }
820        if (y !== alignTo.y) {
821            alignTo = merge(alignTo, { y });
822        }
823        if (!chart.hasRendered) {
824            // Avoid animation when adjusting alignment for responsiveness and
825            // colorAxis label layout
826            this.group.placed = false;
827        }
828        this.group.align(merge(options, {
829            width: this.legendWidth,
830            height: this.legendHeight,
831            verticalAlign: this.proximate ? 'top' : options.verticalAlign
832        }), true, alignTo);
833    }
834    /**
835     * Set up the overflow handling by adding navigation with up and down arrows
836     * below the legend.
837     *
838     * @private
839     * @function Highcharts.Legend#handleOverflow
840     */
841    handleOverflow(legendHeight) {
842        const legend = this, chart = this.chart, renderer = chart.renderer, options = this.options, optionsY = options.y, alignTop = options.verticalAlign === 'top', padding = this.padding, maxHeight = options.maxHeight, navOptions = options.navigation, animation = pick(navOptions.animation, true), arrowSize = navOptions.arrowSize || 12, pages = this.pages, allItems = this.allItems, clipToHeight = function (height) {
843            if (typeof height === 'number') {
844                clipRect.attr({
845                    height: height
846                });
847            }
848            else if (clipRect) { // Reset (#5912)
849                legend.clipRect = clipRect.destroy();
850                legend.contentGroup.clip();
851            }
852            // Use HTML
853            if (legend.contentGroup.div) {
854                legend.contentGroup.div.style.clip = height ?
855                    'rect(' + padding + 'px,9999px,' +
856                        (padding + height) + 'px,0)' :
857                    'auto';
858            }
859        }, addTracker = function (key) {
860            legend[key] = renderer
861                .circle(0, 0, arrowSize * 1.3)
862                .translate(arrowSize / 2, arrowSize / 2)
863                .add(nav);
864            if (!chart.styledMode) {
865                legend[key].attr('fill', 'rgba(0,0,0,0.0001)');
866            }
867            return legend[key];
868        };
869        let clipHeight, lastY, legendItem, spaceHeight = (chart.spacingBox.height +
870            (alignTop ? -optionsY : optionsY) - padding), nav = this.nav, clipRect = this.clipRect;
871        // Adjust the height
872        if (options.layout === 'horizontal' &&
873            options.verticalAlign !== 'middle' &&
874            !options.floating) {
875            spaceHeight /= 2;
876        }
877        if (maxHeight) {
878            spaceHeight = Math.min(spaceHeight, maxHeight);
879        }
880        // Reset the legend height and adjust the clipping rectangle
881        pages.length = 0;
882        if (legendHeight &&
883            spaceHeight > 0 &&
884            legendHeight > spaceHeight &&
885            navOptions.enabled !== false) {
886            this.clipHeight = clipHeight =
887                Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
888            this.currentPage = pick(this.currentPage, 1);
889            this.fullHeight = legendHeight;
890            // Fill pages with Y positions so that the top of each a legend item
891            // defines the scroll top for each page (#2098)
892            allItems.forEach((item, i) => {
893                legendItem = item.legendItem || {};
894                const y = legendItem.y || 0, h = Math.round(legendItem.label.getBBox().height);
895                let len = pages.length;
896                if (!len || (y - pages[len - 1] > clipHeight &&
897                    (lastY || y) !== pages[len - 1])) {
898                    pages.push(lastY || y);
899                    len++;
900                }
901                // Keep track of which page each item is on
902                legendItem.pageIx = len - 1;
903                if (lastY) {
904                    (allItems[i - 1].legendItem || {}).pageIx = len - 1;
905                }
906                // Add the last page if needed (#2617, #13683)
907                if (
908                // Check the last item
909                i === allItems.length - 1 &&
910                    // If adding next page is needed (#18768)
911                    y + h - pages[len - 1] > clipHeight &&
912                    y > pages[len - 1]) {
913                    pages.push(y);
914                    legendItem.pageIx = len;
915                }
916                if (y !== lastY) {
917                    lastY = y;
918                }
919            });
920            // Only apply clipping if needed. Clipping causes blurred legend in
921            // PDF export (#1787)
922            if (!clipRect) {
923                clipRect = legend.clipRect =
924                    renderer.clipRect(0, padding - 2, 9999, 0);
925                legend.contentGroup.clip(clipRect);
926            }
927            clipToHeight(clipHeight);
928            // Add navigation elements
929            if (!nav) {
930                this.nav = nav = renderer.g()
931                    .attr({ zIndex: 1 })
932                    .add(this.group);
933                this.up = renderer
934                    .symbol('triangle', 0, 0, arrowSize, arrowSize)
935                    .add(nav);
936                addTracker('upTracker')
937                    .on('click', function () {
938                    legend.scroll(-1, animation);
939                });
940                this.pager = renderer.text('', 15, 10)
941                    .addClass('highcharts-legend-navigation');
942                if (!chart.styledMode && navOptions.style) {
943                    this.pager.css(navOptions.style);
944                }
945                this.pager.add(nav);
946                this.down = renderer
947                    .symbol('triangle-down', 0, 0, arrowSize, arrowSize)
948                    .add(nav);
949                addTracker('downTracker')
950                    .on('click', function () {
951                    legend.scroll(1, animation);
952                });
953            }
954            // Set initial position
955            legend.scroll(0);
956            legendHeight = spaceHeight;
957            // Reset
958        }
959        else if (nav) {
960            clipToHeight();
961            this.nav = nav.destroy(); // #6322
962            this.scrollGroup.attr({
963                translateY: 1
964            });
965            this.clipHeight = 0; // #1379
966        }
967        return legendHeight;
968    }
969    /**
970     * Scroll the legend by a number of pages.
971     *
972     * @private
973     * @function Highcharts.Legend#scroll
974     *
975     * @param {number} scrollBy
976     *        The number of pages to scroll.
977     *
978     * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
979     *        Whether and how to apply animation.
980     *
981     */
982    scroll(scrollBy, animation) {
983        const chart = this.chart, pages = this.pages, pageCount = pages.length, clipHeight = this.clipHeight, navOptions = this.options.navigation, pager = this.pager, padding = this.padding;
984        let currentPage = this.currentPage + scrollBy;
985        // When resizing while looking at the last page
986        if (currentPage > pageCount) {
987            currentPage = pageCount;
988        }
989        if (currentPage > 0) {
990            if (typeof animation !== 'undefined') {
991                setAnimation(animation, chart);
992            }
993            this.nav.attr({
994                translateX: padding,
995                translateY: clipHeight + this.padding + 7 + this.titleHeight,
996                visibility: 'inherit'
997            });
998            [this.up, this.upTracker].forEach(function (elem) {
999                elem.attr({
1000                    'class': currentPage === 1 ?
1001                        'highcharts-legend-nav-inactive' :
1002                        'highcharts-legend-nav-active'
1003                });
1004            });
1005            pager.attr({
1006                text: currentPage + '/' + pageCount
1007            });
1008            [this.down, this.downTracker].forEach(function (elem) {
1009                elem.attr({
1010                    // Adjust to text width
1011                    x: 18 + this.pager.getBBox().width,
1012                    'class': currentPage === pageCount ?
1013                        'highcharts-legend-nav-inactive' :
1014                        'highcharts-legend-nav-active'
1015                });
1016            }, this);
1017            if (!chart.styledMode) {
1018                this.up
1019                    .attr({
1020                    fill: currentPage === 1 ?
1021                        navOptions.inactiveColor :
1022                        navOptions.activeColor
1023                });
1024                this.upTracker
1025                    .css({
1026                    cursor: currentPage === 1 ? 'default' : 'pointer'
1027                });
1028                this.down
1029                    .attr({
1030                    fill: currentPage === pageCount ?
1031                        navOptions.inactiveColor :
1032                        navOptions.activeColor
1033                });
1034                this.downTracker
1035                    .css({
1036                    cursor: currentPage === pageCount ?
1037                        'default' :
1038                        'pointer'
1039                });
1040            }
1041            this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
1042            this.scrollGroup.animate({
1043                translateY: this.scrollOffset
1044            });
1045            this.currentPage = currentPage;
1046            this.positionCheckboxes();
1047            // Fire event after scroll animation is complete
1048            const animOptions = animObject(pick(animation, chart.renderer.globalAnimation, true));
1049            syncTimeout(() => {
1050                fireEvent(this, 'afterScroll', { currentPage });
1051            }, animOptions.duration);
1052        }
1053    }
1054    /**
1055     * @private
1056     * @function Highcharts.Legend#setItemEvents
1057     * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
1058     * @param {Highcharts.SVGElement} legendLabel
1059     * @param {boolean} [useHTML=false]
vendor: 7,687 bytes, lines 1060-1274
1060     * @emits Highcharts.Legend#event:itemClick
1061     */
1062    setItemEvents(item, legendLabel, useHTML) {
1063        const legend = this, legendItem = item.legendItem || {}, boxWrapper = legend.chart.renderer.boxWrapper, isPoint = item instanceof Point, isSeries = item instanceof Series, activeClass = 'highcharts-legend-' +
1064            (isPoint ? 'point' : 'series') + '-active', styledMode = legend.chart.styledMode, 
1065        // When `useHTML`, the symbol is rendered in other group, so
1066        // we need to apply events listeners to both places
1067        legendElements = useHTML ?
1068            [legendLabel, legendItem.symbol] :
1069            [legendItem.group];
1070        const setOtherItemsState = (state) => {
1071            legend.allItems.forEach((otherItem) => {
1072                if (item !== otherItem) {
1073                    [otherItem]
1074                        .concat(otherItem.linkedSeries || [])
1075                        .forEach((otherItem) => {
1076                        otherItem.setState(state, !isPoint);
1077                    });
1078                }
1079            });
1080        };
1081        // Set the events on the item group, or in case of useHTML, the item
1082        // itself (#1249)
1083        for (const element of legendElements) {
1084            if (element) {
1085                element
1086                    .on('mouseover', function () {
1087                    if (item.visible) {
1088                        setOtherItemsState('inactive');
1089                    }
1090                    item.setState('hover');
1091                    // A CSS class to dim or hide other than the hovered
1092                    // series.
1093                    // Works only if hovered series is visible (#10071).
1094                    if (item.visible) {
1095                        boxWrapper.addClass(activeClass);
1096                    }
1097                    if (!styledMode) {
1098                        legendLabel.css(legend.options.itemHoverStyle);
1099                    }
1100                })
1101                    .on('mouseout', function () {
1102                    if (!legend.chart.styledMode) {
1103                        legendLabel.css(merge(item.visible ?
1104                            legend.itemStyle :
1105                            legend.itemHiddenStyle));
1106                    }
1107                    setOtherItemsState('');
1108                    // A CSS class to dim or hide other than the hovered
1109                    // series.
1110                    boxWrapper.removeClass(activeClass);
1111                    item.setState();
1112                })
1113                    .on('click', function (event) {
1114                    const defaultItemClick = function () {
1115                        if (item.setVisible) {
1116                            item.setVisible();
1117                        }
1118                        // Reset inactive state
1119                        setOtherItemsState(item.visible ? 'inactive' : '');
1120                    };
1121                    // A CSS class to dim or hide other than the hovered
1122                    // series. Event handling in iOS causes the activeClass
1123                    // to be added prior to click in some cases (#7418).
1124                    boxWrapper.removeClass(activeClass);
1125                    fireEvent(legend, 'itemClick', {
1126                        // Pass over the click/touch event. #4.
1127                        browserEvent: event,
1128                        legendItem: item
1129                    }, defaultItemClick);
1130                    // Deprecated logic
1131                    // Click the name or symbol
1132                    if (isPoint) {
1133                        item.firePointEvent('legendItemClick', {
1134                            browserEvent: event
1135                        });
1136                    }
1137                    else if (isSeries) {
1138                        fireEvent(item, 'legendItemClick', {
1139                            browserEvent: event
1140                        });
1141                    }
1142                });
1143            }
1144        }
1145    }
1146    /**
1147     * @private
1148     * @function Highcharts.Legend#createCheckboxForItem
1149     * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
1150     * @emits Highcharts.Series#event:checkboxClick
1151     */
1152    createCheckboxForItem(item) {
1153        const legend = this;
1154        item.checkbox = createElement('input', {
1155            type: 'checkbox',
1156            className: 'highcharts-legend-checkbox',
1157            checked: item.selected,
1158            defaultChecked: item.selected // Required by IE7
1159        }, legend.options.itemCheckboxStyle, legend.chart.container);
1160        addEvent(item.checkbox, 'click', function (event) {
1161            const target = event.target;
1162            fireEvent(item.series || item, 'checkboxClick', {
1163                checked: target.checked,
1164                item: item
1165            }, function () {
1166                item.select();
1167            });
1168        });
1169    }
1170}
1171/* *
1172 *
1173 *  Class Namespace
1174 *
1175 * */
1176(function (Legend) {
1177    /* *
1178     *
1179     *  Declarations
1180     *
1181     * */
1182    /* *
1183     *
1184     *  Functions
1185     *
1186     * */
1187    /**
1188     * @private
1189     */
1190    function compose(ChartClass) {
1191        if (pushUnique(composed, 'Core.Legend')) {
1192            addEvent(ChartClass, 'beforeMargins', function () {
1193                /**
1194                 * The legend contains an interactive overview over chart items,
1195                 * usually individual series or points depending on the series
1196                 * type. The color axis and bubble legend are also rendered in
1197                 * the chart legend.
1198                 *
1199                 * @name Highcharts.Chart#legend
1200                 * @type {Highcharts.Legend}
1201                 */
1202                this.legend = new Legend(this, this.options.legend);
1203            });
1204        }
1205    }
1206    Legend.compose = compose;
1207})(Legend || (Legend = {}));
1208/* *
1209 *
1210 *  Default Export
1211 *
1212 * */
1213export default Legend;
1214/* *
1215 *
1216 *  API Declarations
1217 *
1218 * */
1219/**
1220 * @interface Highcharts.LegendItemObject
1221 */ /**
1222* @name Highcharts.LegendItemObject#item
1223* @type {Highcharts.SVGElement|undefined}
1224*/ /**
1225* @name Highcharts.LegendItemObject#line
1226* @type {Highcharts.SVGElement|undefined}
1227*/ /**
1228* @name Highcharts.LegendItemObject#symbol
1229* @type {Highcharts.SVGElement|undefined}
1230*/
1231/**
1232 * Gets fired when the legend item is clicked. The default
1233 * action is to toggle the visibility of the series or point. This can be
1234 * prevented by returning `false` or calling `event.preventDefault()`.
1235 *
1236 * @callback Highcharts.LegendItemClickCallbackFunction
1237 *
1238 * @param {Highcharts.Legend} this
1239 *        The legend on which the event occurred.
1240 *
1241 * @param {Highcharts.LegendItemClickEventObject} event
1242 *        The event that occurred.
1243 */
1244/**
1245 * Information about the legend click event.
1246 *
1247 * @interface Highcharts.LegendItemClickEventObject
1248 */ /**
1249* Related browser event.
1250* @name Highcharts.LegendItemClickEventObject#browserEvent
1251* @type {Highcharts.PointerEvent}
1252*/ /**
1253* Prevent the default action of toggle the visibility of the series or point.
1254* @name Highcharts.LegendItemClickEventObject#preventDefault
1255* @type {Function}
1256* */ /**
1257* Related legend item, it can be series, point, color axis or data class from
1258* color axis.
1259* @name Highcharts.LegendItemClickEventObject#legendItem
1260* @type {Highcharts.Series|Highcharts.Point|Highcharts.LegendItemObject}
1261* */ /**
1262* Related legend.
1263* @name Highcharts.LegendItemClickEventObject#target
1264* @type {Highcharts.Legend}
1265*/ /**
1266* Event type.
1267* @name Highcharts.LegendItemClickEventObject#type
1268* @type {"itemClick"}
1269*/
1270/**
1271 * Gets fired when the legend item belonging to a point is clicked. The default
1272 * action is to toggle the visibility of the point. This can be prevented by
1273 * returning `false` or calling `event.preventDefault()`.
1274 *
1275 * **Note:** This option is deprecated in favor of
1276 * Highcharts.LegendItemClickCallbackFunction.
1277 *
1278 * @deprecated 11.4.4
1279 * @callback Highcharts.PointLegendItemClickCallbackFunction
1280 *
1281 * @param {Highcharts.Point} this
1282 *        The point on which the event occurred.
1283 *
1284 * @param {Highcharts.PointLegendItemClickEventObject} event
1285 *        The event that occurred.
1286 */
1287/**
1288 * Information about the legend click event.
1289 *
1290 * **Note:** This option is deprecated in favor of
1291 * Highcharts.LegendItemClickEventObject.
1292 *
1293 * @deprecated 11.4.4
1294 * @interface Highcharts.PointLegendItemClickEventObject
1295 */ /**
1296* Related browser event.
1297* @name Highcharts.PointLegendItemClickEventObject#browserEvent
1298* @type {Highcharts.PointerEvent}
1299*/ /**
1300* Prevent the default action of toggle the visibility of the point.
1301* @name Highcharts.PointLegendItemClickEventObject#preventDefault
1302* @type {Function}
1303*/ /**
1304* Related point.
1305* @name Highcharts.PointLegendItemClickEventObject#target
1306* @type {Highcharts.Point}
1307*/ /**
1308* Event type.
1309* @name Highcharts.PointLegendItemClickEventObject#type
1310* @type {"legendItemClick"}
1311*/
1312/**
1313 * Series color as used by the legend and some series types.
1314 * @name Highcharts.Series#color
1315 * @type {Highcharts.ColorType|undefined}
1316 */ /**
1317* Legend data for the series.
1318* @name Highcharts.Series#legendItem
1319* @type {Highcharts.LegendItemObject|undefined}
1320* @since 10.3.0
1321*/
1322/**
1323 * Gets fired when the legend item belonging to a series is clicked. The default
1324 * action is to toggle the visibility of the series. This can be prevented by
1325 * returning `false` or calling `event.preventDefault()`.
1326 *
1327 * **Note:** This option is deprecated in favor of
1328 * Highcharts.LegendItemClickCallbackFunction.
1329 *
1330 * @deprecated 11.4.4
1331 * @callback Highcharts.SeriesLegendItemClickCallbackFunction
1332 *
1333 * @param {Highcharts.Series} this
1334 *        The series where the event occurred.
1335 *
1336 * @param {Highcharts.SeriesLegendItemClickEventObject} event
1337 *        The event that occurred.
1338 */
1339/**
1340 * Information about the legend click event.
1341 *
1342 * **Note:** This option is deprecated in favor of
1343 * Highcharts.LegendItemClickEventObject.
1344 *
1345 * @deprecated 11.4.4
1346 * @interface Highcharts.SeriesLegendItemClickEventObject
1347 */ /**
1348* Related browser event.
1349* @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
1350* @type {Highcharts.PointerEvent}
1351*/ /**
1352* Prevent the default action of toggle the visibility of the series.
1353* @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
1354* @type {Function}
1355*/ /**
1356* Related series.
1357* @name Highcharts.SeriesLegendItemClickEventObject#target
1358* @type {Highcharts.Series}
1359*/ /**
1360* Event type.
1361* @name Highcharts.SeriesLegendItemClickEventObject#type
1362* @type {"legendItemClick"}
1363*/
1364(''); // Keeps doclets above in JS file

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.