PageSourceSearch

https://www.ledevoluy.com/app/dist/ledevoluy-tourisme/addons/woody…/code/es-modules/Core/Tooltip.js

js ledevoluy.com collected 2026-09-24 23:13:39 UTC 51,989 bytes, 1,349 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 } = A;
13import F from './Templating.js';
14const { format } = F;
15import H from './Globals.js';
16const { composed, dateFormats, doc, isSafari } = H;
17import R from './Renderer/RendererUtilities.js';
18const { distribute } = R;
19import RendererRegistry from './Renderer/RendererRegistry.js';
20import U from './Utilities.js';
21const { addEvent, clamp, css, discardElement, extend, fireEvent, isArray, isNumber, isObject, isString, merge, pick, pushUnique, splat, syncTimeout } = U;
22/* *
23 *
24 *  Class
25 *
26 * */
27/* eslint-disable no-invalid-this, valid-jsdoc */
28/**
29 * Tooltip of a chart.
30 *
31 * @class
32 * @name Highcharts.Tooltip
33 *
34 * @param {Highcharts.Chart} chart
35 * The chart instance.
36 *
37 * @param {Highcharts.TooltipOptions} options
38 * Tooltip options.
39 *
40 * @param {Highcharts.Pointer} pointer
41 * The pointer instance.
42 */
43class Tooltip {
44    /* *
45     *
46     *  Constructors
47     *
48     * */
49    constructor(chart, options, pointer) {
50        /* *
51         *
52         *  Properties
53         *
54         * */
55        this.allowShared = true;
56        this.crosshairs = [];
57        this.distance = 0;
58        this.isHidden = true;
59        this.isSticky = false;
60        this.options = {};
61        this.outside = false;
62        this.chart = chart;
63        this.init(chart, options);
64        this.pointer = pointer;
65    }
66    /* *
67     *
68     *  Functions
69     *
70     * */
71    /**
72     * Build the body (lines) of the tooltip by iterating over the items and
73     * returning one entry for each item, abstracting this functionality allows
74     * to easily overwrite and extend it.
75     *
76     * @private
77     * @function Highcharts.Tooltip#bodyFormatter
78     */
79    bodyFormatter(points) {
80        return points.map((point) => {
81            const tooltipOptions = point.series.tooltipOptions, formatPrefix = point.formatPrefix || 'point';
82            return (tooltipOptions[formatPrefix + 'Formatter'] ||
83                point.tooltipFormatter).call(point, tooltipOptions[formatPrefix + 'Format'] || '');
84        });
85    }
86    /**
87     * Destroy the single tooltips in a split tooltip.
88     * If the tooltip is active then it is not destroyed, unless forced to.
89     *
90     * @private
91     * @function Highcharts.Tooltip#cleanSplit
92     *
93     * @param {boolean} [force]
94     * Force destroy all tooltips.
95     */
96    cleanSplit(force) {
97        this.chart.series.forEach(function (series) {
98            const tt = series && series.tt;
99            if (tt) {
100                if (!tt.isActive || force) {
101                    series.tt = tt.destroy();
102                }
103                else {
104                    tt.isActive = false;
105                }
106            }
107        });
108    }
109    /**
110     * In case no user defined formatter is given, this will be used. Note that
111     * the context here is an object holding point, series, x, y etc.
112     *
113     * @function Highcharts.Tooltip#defaultFormatter
114     *
115     * @param {Highcharts.Tooltip} tooltip
116     *
117     * @return {string|Array<string>}
118     * Returns a string (single tooltip and shared)
119     * or an array of strings (split tooltip)
120     */
121    defaultFormatter(tooltip) {
122        const hoverPoints = this.points || splat(this);
123        let s;
124        // Build the header
125        s = [tooltip.headerFooterFormatter(hoverPoints[0])];
126        // Build the values
127        s = s.concat(tooltip.bodyFormatter(hoverPoints));
128        // Footer
129        s.push(tooltip.headerFooterFormatter(hoverPoints[0], true));
130        return s;
131    }
132    /**
133     * Removes and destroys the tooltip and its elements.
134     *
135     * @function Highcharts.Tooltip#destroy
136     */
137    destroy() {
138        // Destroy and clear local variables
139        if (this.label) {
140            this.label = this.label.destroy();
141        }
142        if (this.split) {
143            this.cleanSplit(true);
144            if (this.tt) {
145                this.tt = this.tt.destroy();
146            }
147        }
148        if (this.renderer) {
149            this.renderer = this.renderer.destroy();
150            discardElement(this.container);
151        }
152        U.clearTimeout(this.hideTimer);
153    }
154    /**
155     * Extendable method to get the anchor position of the tooltip
156     * from a point or set of points
157     *
158     * @private
159     * @function Highcharts.Tooltip#getAnchor
160     */
161    getAnchor(points, mouseEvent) {
162        const { chart, pointer } = this, inverted = chart.inverted, plotTop = chart.plotTop, plotLeft = chart.plotLeft;
163        let ret;
164        points = splat(points);
165        // If reversedStacks are false the tooltip position should be taken from
166        // the last point (#17948)
167        if (points[0].series &&
168            points[0].series.yAxis &&
169            !points[0].series.yAxis.options.reversedStacks) {
170            points = points.slice().reverse();
171        }
172        // When tooltip follows mouse, relate the position to the mouse
173        if (this.followPointer && mouseEvent) {
174            if (typeof mouseEvent.chartX === 'undefined') {
175                mouseEvent = pointer.normalize(mouseEvent);
176            }
177            ret = [
178                mouseEvent.chartX - plotLeft,
179                mouseEvent.chartY - plotTop
180            ];
181            // Some series types use a specificly calculated tooltip position for
182            // each point
183        }
184        else if (points[0].tooltipPos) {
185            ret = points[0].tooltipPos;
186            // Calculate the average position and adjust for axis positions
187        }
188        else {
189            let chartX = 0, chartY = 0;
190            points.forEach(function (point) {
191                const pos = point.pos(true);
192                if (pos) {
193                    chartX += pos[0];
194                    chartY += pos[1];
195                }
196            });
197            chartX /= points.length;
198            chartY /= points.length;
199            // When shared, place the tooltip next to the mouse (#424)
200            if (this.shared && points.length > 1 && mouseEvent) {
201                if (inverted) {
202                    chartX = mouseEvent.chartX;
203                }
204                else {
205                    chartY = mouseEvent.chartY;
206                }
207            }
208            // Use the average position for multiple points
209            ret = [chartX - plotLeft, chartY - plotTop];
210        }
211        return ret.map(Math.round);
212    }
213    /**
214     * Get the CSS class names for the tooltip's label. Styles the label
215     * by `colorIndex` or user-defined CSS.
216     *
217     * @function Highcharts.Tooltip#getClassName
218     *
219     * @return {string}
220     *         The class names.
221     */
222    getClassName(point, isSplit, isHeader) {
223        const options = this.options, series = point.series, seriesOptions = series.options;
224        return [
225            options.className,
226            'highcharts-label',
227            isHeader && 'highcharts-tooltip-header',
228            isSplit ? 'highcharts-tooltip-box' : 'highcharts-tooltip',
229            !isHeader && 'highcharts-color-' + pick(point.colorIndex, series.colorIndex),
230            (seriesOptions && seriesOptions.className)
231        ].filter(isString).join(' ');
232    }
233    /**
234     * Creates the Tooltip label element if it does not exist, then returns it.
235     *
236     * @function Highcharts.Tooltip#getLabel
237     *
238     * @return {Highcharts.SVGElement}
239     * Tooltip label
240     */
241    getLabel({ anchorX, anchorY } = { anchorX: 0, anchorY: 0 }) {
242        const tooltip = this, styledMode = this.chart.styledMode, options = this.options, doSplit = this.split && this.allowShared;
243        let container = this.container, renderer = this.chart.renderer;
244        // If changing from a split tooltip to a non-split tooltip, we must
245        // destroy it in order to get the SVG right. #13868.
246        if (this.label) {
247            const wasSplit = !this.label.hasClass('highcharts-label');
248            if ((!doSplit && wasSplit) || (doSplit && !wasSplit)) {
249                this.destroy();
250            }
251        }
252        if (!this.label) {
253            if (this.outside) {
254                const chart = this.chart, chartStyle = chart.options.chart.style, Renderer = RendererRegistry.getRendererType();
255                /**
256                 * Reference to the tooltip's container, when
257                 * [Highcharts.Tooltip#outside] is set to true, otherwise
258                 * it's undefined.
259                 *
260                 * @name Highcharts.Tooltip#container
261                 * @type {Highcharts.HTMLDOMElement|undefined}
262                 */
263                this.container = container = H.doc.createElement('div');
264                container.className = ('highcharts-tooltip-container ' +
265                    (chart.renderTo.className.match(/(highcharts[a-zA-Z0-9-]+)\s?/gm) || [].join(' ')));
266                // We need to set pointerEvents = 'none' as otherwise it makes
267                // the area under the tooltip non-hoverable even after the
268                // tooltip disappears, #19035.
269                css(container, {
270                    position: 'absolute',
271                    top: '1px',
272                    pointerEvents: 'none',
273                    zIndex: Math.max(this.options.style.zIndex || 0, (chartStyle && chartStyle.zIndex || 0) + 3)
274                });
275                /**
276                 * Reference to the tooltip's renderer, when
277                 * [Highcharts.Tooltip#outside] is set to true, otherwise
278                 * it's undefined.
279                 *
280                 * @name Highcharts.Tooltip#renderer
281                 * @type {Highcharts.SVGRenderer|undefined}
282                 */
283                this.renderer = renderer = new Renderer(container, 0, 0, chartStyle, void 0, void 0, renderer.styledMode);
284            }
285            // Create the label
286            if (doSplit) {
287                this.label = renderer.g('tooltip');
288            }
289            else {
290                this.label = renderer
291                    .label('', anchorX, anchorY, options.shape, void 0, void 0, options.useHTML, void 0, 'tooltip')
292                    .attr({
293                    padding: options.padding,
294                    r: options.borderRadius
295                });
296                if (!styledMode) {
297                    this.label
298                        .attr({
299                        fill: options.backgroundColor,
300                        'stroke-width': options.borderWidth || 0
301                    })
302                        // #2301, #2657
303                        .css(options.style)
304                        .css({
305                        pointerEvents: (options.style.pointerEvents ||
306                            (this.shouldStickOnContact() ? 'auto' : 'none'))
307                    });
308                }
309            }
310            // Split tooltip use updateTooltipContainer to position the tooltip
311            // container.
312            if (tooltip.outside) {
313                const label = this.label;
314                [label.xSetter, label.ySetter].forEach((setter, i) => {
315                    label[i ? 'ySetter' : 'xSetter'] = (value) => {
316                        setter.call(label, tooltip.distance);
317                        label[i ? 'y' : 'x'] = value;
318                        if (container) {
319                            container.style[i ? 'top' : 'left'] = `${value}px`;
320                        }
321                    };
322                });
323            }
324            this.label
325                .attr({ zIndex: 8 })
326                .shadow(options.shadow)
327                .add();
328        }
329        if (container && !container.parentElement) {
330            H.doc.body.appendChild(container);
331        }
332        return this.label;
333    }
334    /**
335     * Get the total area available area to place the tooltip
336     *
337     * @private
338     */
339    getPlayingField() {
340        const { body, documentElement } = doc, { chart, distance, outside }
vendor: 4,653 bytes, lines 340-438
340 = this;
341        return {
342            width: outside ?
343                // Subtract distance to prevent scrollbars
344                Math.max(body.scrollWidth, documentElement.scrollWidth, body.offsetWidth, documentElement.offsetWidth, documentElement.clientWidth) - (2 * distance) - 2 :
345                chart.chartWidth,
346            height: outside ?
347                Math.max(body.scrollHeight, documentElement.scrollHeight, body.offsetHeight, documentElement.offsetHeight, documentElement.clientHeight) :
348                chart.chartHeight
349        };
350    }
351    /**
352     * Place the tooltip in a chart without spilling over and not covering the
353     * point itself.
354     *
355     * @function Highcharts.Tooltip#getPosition
356     *
357     * @param {number} boxWidth
358     *        Width of the tooltip box.
359     *
360     * @param {number} boxHeight
361     *        Height of the tooltip box.
362     *
363     * @param {Highcharts.Point} point
364     *        Tooltip related point.
365     *
366     * @return {Highcharts.PositionObject}
367     *         Recommended position of the tooltip.
368     */
369    getPosition(boxWidth, boxHeight, point) {
370        const { distance, chart, outside, pointer } = this, { inverted, plotLeft, plotTop, polar } = chart, { plotX = 0, plotY = 0 } = point, ret = {}, 
371        // Don't use h if chart isn't inverted (#7242) ???
372        h = (inverted && point.h) || 0, // #4117 ???
373        { height: outerHeight, width: outerWidth } = this.getPlayingField(), chartPosition = pointer.getChartPosition(), scaleX = (val) => (val * chartPosition.scaleX), scaleY = (val) => (val * chartPosition.scaleY), 
374        // Build parameter arrays for firstDimension()/secondDimension()
375        buildDimensionArray = (dim) => {
376            const isX = dim === 'x';
377            return [
378                dim, // Dimension - x or y
379                isX ? outerWidth : outerHeight,
380                isX ? boxWidth : boxHeight
381            ].concat(outside ? [
382                // If we are using tooltip.outside, we need to scale the
383                // position to match scaling of the container in case there
384                // is a transform/zoom on the container. #11329
385                isX ? scaleX(boxWidth) : scaleY(boxHeight),
386                isX ? chartPosition.left - distance +
387                    scaleX(plotX + plotLeft) :
388                    chartPosition.top - distance +
389                        scaleY(plotY + plotTop),
390                0,
391                isX ? outerWidth : outerHeight
392            ] : [
393                // Not outside, no scaling is needed
394                isX ? boxWidth : boxHeight,
395                isX ? plotX + plotLeft : plotY + plotTop,
396                isX ? plotLeft : plotTop,
397                isX ? plotLeft + chart.plotWidth :
398                    plotTop + chart.plotHeight
399            ]);
400        };
401        let first = buildDimensionArray('y'), second = buildDimensionArray('x'), swapped;
402        // Handle negative points or reversed axis (#13780)
403        let flipped = !!point.negative;
404        if (!polar &&
405            chart.hoverSeries?.yAxis?.reversed) {
406            flipped = !flipped;
407        }
408        // The far side is right or bottom
409        const preferFarSide = !this.followPointer &&
410            pick(point.ttBelow, polar ? false : !inverted === flipped), // #4984
411        /*
412         * Handle the preferred dimension. When the preferred dimension is
413         * tooltip on top or bottom of the point, it will look for space
414         * there.
415         *
416         * @private
417         */
418        firstDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
419        point, min, max) {
420            const scaledDist = outside ?
421                (dim === 'y' ? scaleY(distance) : scaleX(distance)) :
422                distance, scaleDiff = (innerSize - scaledInnerSize) / 2, roomLeft = scaledInnerSize < point - distance, roomRight = point + distance + scaledInnerSize < outerSize, alignedLeft = point - scaledDist - innerSize + scaleDiff, alignedRight = point + scaledDist - scaleDiff;
423            if (preferFarSide && roomRight) {
424                ret[dim] = alignedRight;
425            }
426            else if (!preferFarSide && roomLeft) {
427                ret[dim] = alignedLeft;
428            }
429            else if (roomLeft) {
430                ret[dim] = Math.min(max - scaledInnerSize, alignedLeft - h < 0 ? alignedLeft : alignedLeft - h);
431            }
432            else if (roomRight) {
433                ret[dim] = Math.max(min, alignedRight + h + innerSize > outerSize ?
434                    alignedRight :
435                    alignedRight + h);
436            }
437            else {
438                return false;
vendor: 5,244 bytes, lines 439-601
439            }
440        }, 
441        /*
442         * Handle the secondary dimension. If the preferred dimension is
443         * tooltip on top or bottom of the point, the second dimension is to
444         * align the tooltip above the point, trying to align center but
445         * allowing left or right align within the chart box.
446         *
447         * @private
448         */
449        secondDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
450        point) {
451            // Too close to the edge, return false and swap dimensions
452            if (point < distance || point > outerSize - distance) {
453                return false;
454            }
455            // Align left/top
456            if (point < innerSize / 2) {
457                ret[dim] = 1;
458                // Align right/bottom
459            }
460            else if (point > outerSize - scaledInnerSize / 2) {
461                ret[dim] = outerSize - scaledInnerSize - 2;
462                // Align center
463            }
464            else {
465                ret[dim] = point - innerSize / 2;
466            }
467        }, 
468        /*
469         * Swap the dimensions
470         */
471        swap = function (count) {
472            [first, second] = [second, first];
473            swapped = count;
474        }, run = () => {
475            if (firstDimension.apply(0, first) !== false) {
476                if (secondDimension.apply(0, second) === false &&
477                    !swapped) {
478                    swap(true);
479                    run();
480                }
481            }
482            else if (!swapped) {
483                swap(true);
484                run();
485            }
486            else {
487                ret.x = ret.y = 0;
488            }
489        };
490        // Under these conditions, prefer the tooltip on the side of the point
491        if ((inverted && !polar) || this.len > 1) {
492            swap();
493        }
494        run();
495        return ret;
496    }
497    /**
498     * Hides the tooltip with a fade out animation.
499     *
500     * @function Highcharts.Tooltip#hide
501     *
502     * @param {number} [delay]
503     *        The fade out in milliseconds. If no value is provided the value
504     *        of the tooltip.hideDelay option is used. A value of 0 disables
505     *        the fade out animation.
506     */
507    hide(delay) {
508        const tooltip = this;
509        // Disallow duplicate timers (#1728, #1766)
510        U.clearTimeout(this.hideTimer);
511        delay = pick(delay, this.options.hideDelay);
512        if (!this.isHidden) {
513            this.hideTimer = syncTimeout(function () {
514                const label = tooltip.getLabel();
515                // If there is a delay, fade out with the default duration. If
516                // the hideDelay is 0, we assume no animation is wanted, so we
517                // pass 0 duration. #12994.
518                tooltip.getLabel().animate({
519                    opacity: 0
520                }, {
521                    duration: delay ? 150 : delay,
522                    complete: () => {
523                        // #3088, assuming we're only using this for tooltips
524                        label.hide();
525                        // Clear the container for outside tooltip (#18490)
526                        if (tooltip.container) {
527                            tooltip.container.remove();
528                        }
529                    }
530                });
531                tooltip.isHidden = true;
532            }, delay);
533        }
534    }
535    /**
536     * Initialize tooltip.
537     *
538     * @private
539     * @function Highcharts.Tooltip#init
540     *
541     * @param {Highcharts.Chart} chart
542     *        The chart instance.
543     *
544     * @param {Highcharts.TooltipOptions} options
545     *        Tooltip options.
546     */
547    init(chart, options) {
548        /**
549         * Chart of the tooltip.
550         *
551         * @readonly
552         * @name Highcharts.Tooltip#chart
553         * @type {Highcharts.Chart}
554         */
555        this.chart = chart;
556        /**
557         * Used tooltip options.
558         *
559         * @readonly
560         * @name Highcharts.Tooltip#options
561         * @type {Highcharts.TooltipOptions}
562         */
563        this.options = options;
564        /**
565         * List of crosshairs.
566         *
567         * @private
568         * @readonly
569         * @name Highcharts.Tooltip#crosshairs
570         * @type {Array<null>}
571         */
572        this.crosshairs = [];
573        /**
574         * Tooltips are initially hidden.
575         *
576         * @private
577         * @readonly
578         * @name Highcharts.Tooltip#isHidden
579         * @type {boolean}
580         */
581        this.isHidden = true;
582        /**
583         * True, if the tooltip is split into one label per series, with the
584         * header close to the axis.
585         *
586         * @readonly
587         * @name Highcharts.Tooltip#split
588         * @type {boolean|undefined}
589         */
590        this.split = options.split && !chart.inverted && !chart.polar;
591        /**
592         * When the tooltip is shared, the entire plot area will capture mouse
593         * movement or touch events.
594         *
595         * @readonly
596         * @name Highcharts.Tooltip#shared
597         * @type {boolean|undefined}
598         */
599        this.shared = options.shared || this.split;
600        /**
601         * Whether to allow the tooltip to render outside the chart'
601s SVG
602         * element box. By default (false), the tooltip is rendered within the
603         * chart's SVG element, which results in the tooltip being aligned
604         * inside the chart area.
605         *
606         * @readonly
607         * @name Highcharts.Tooltip#outside
608         * @type {boolean}
609         *
610         * @todo
611         * Split tooltip does not support outside in the first iteration. Should
612         * not be too complicated to implement.
613         */
614        this.outside = pick(options.outside, Boolean(chart.scrollablePixelsX || chart.scrollablePixelsY));
615    }
616    shouldStickOnContact(pointerEvent) {
617        return !!(!this.followPointer &&
618            this.options.stickOnContact &&
619            (!pointerEvent || this.pointer.inClass(pointerEvent.target, 'highcharts-tooltip')));
620    }
621    /**
622     * Moves the tooltip with a soft animation to a new position.
623     *
624     * @private
625     * @function Highcharts.Tooltip#move
626     *
627     * @param {number} x
628     *
629     * @param {number} y
630     *
631     * @param {number} anchorX
632     *
633     * @param {number} anchorY
634     */
635    move(x, y, anchorX, anchorY) {
636        const tooltip = this, animation = animObject(!tooltip.isHidden && tooltip.options.animation), skipAnchor = tooltip.followPointer || (tooltip.len || 0) > 1, attr = { x, y };
637        if (!skipAnchor) {
638            attr.anchorX = anchorX;
639            attr.anchorY = anchorY;
640        }
641        animation.step = () => tooltip.drawTracker();
642        tooltip.getLabel().animate(attr, animation);
643    }
644    /**
645     * Refresh the tooltip's text and position.
646     *
647     * @function Highcharts.Tooltip#refresh
648     *
649     * @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
650     *        Either a point or an array of points.
651     *
652     * @param {Highcharts.PointerEventObject} [mouseEvent]
653     *        Mouse event, that is responsible for the refresh and should be
654     *        used for the tooltip update.
655     */
656    refresh(pointOrPoints, mouseEvent) {
657        const tooltip = this, { chart, options, pointer, shared } = this, points = splat(pointOrPoints), point = points[0], formatString = options.format, formatter = options.formatter || tooltip.defaultFormatter, styledMode = chart.styledMode;
658        let wasShared = tooltip.allowShared;
659        if (!options.enabled || !point.series) { // #16820
660            return;
661        }
662        U.clearTimeout(this.hideTimer);
663        // A switch saying if this specific tooltip configuration allows shared
664        // or split modes
665        tooltip.allowShared = !(!isArray(pointOrPoints) &&
666            pointOrPoints.series &&
667            pointOrPoints.series.noSharedTooltip);
668        wasShared = wasShared && !tooltip.allowShared;
669        // Get the reference point coordinates (pie charts use tooltipPos)
670        tooltip.followPointer = (!tooltip.split && point.series.tooltipOptions.followPointer);
671        const anchor = tooltip.getAnchor(pointOrPoints, mouseEvent), x = anchor[0], y = anchor[1];
672        // Shared tooltip, array is sent over
673        if (shared && tooltip.allowShared) {
674            pointer.applyInactiveState(points);
675            // Now set hover state for the chosen ones:
676            points.forEach((item) => item.setState('hover'));
677            point.points = points;
678        }
679        this.len = points.length; // #6128
680        const text = isString(formatString) ?
681            format(formatString, point, chart) :
682            formatter.call(point, tooltip);
683        // Reset the preliminary circular references
684        point.points = void 0;
685        // Register the current series
686        const currentSeries = point.series;
687        this.distance = pick(currentSeries.tooltipOptions.distance, 16);
688        // Update the inner HTML
689        if (text === false) {
690            this.hide();
691        }
692        else {
693            // Update text
694            if (tooltip.split && tooltip.allowShared) { // #13868
695                this.renderSplit(text, points);
696            }
697            else {
698                let checkX = x;
699                let checkY = y;
700                if (mouseEvent && pointer.isDirectTouch) {
701                    checkX = mouseEvent.chartX - chart.plotLeft;
702                    checkY = mouseEvent.chartY - chart.plotTop;
703                }
704                // #11493, #13095
705                if (chart.polar ||
706                    currentSeries.options.clip === false ||
707                    points.some((p) => // #16004
708                     pointer.isDirectTouch || // ##17929
709                        p.series.shouldShowTooltip(checkX, checkY))) {
710                    const label = tooltip.getLabel(wasShared && tooltip.tt || {});
711                    // Prevent the tooltip from flowing over the chart box
712                    // (#6659)
713                    if (!options.style.width || styledMode) {
714                        label.css({
715                            width: (this.outside ?
716                                this.getPlayingField() :
717                                chart.spacingBox).width + 'px'
718                        });
719                    }
720                    label.attr({
721                        // Add class before the label BBox calculation (#21035)
722                        'class': tooltip.getClassName(point),
723                        text: text && text.join ?
724                            text.join('') :
725                            text
726                    });
727                    // When the length of the label has increased, immediately
728                    // update the x position to prevent tooltip from flowing
729                    // outside the viewport during animation (#21371)
730                    if (this.outside) {
731                        label.attr({
732                            x: clamp(label.x || 0, 0, this.getPlayingField().width -
733                                (label.width || 0) -
734                                1)
735                        });
736                    }
737                    if (!styledMode) {
738                        label.attr({
739                            stroke: (options.borderColor ||
740                                point.color ||
741                                currentSeries.color ||
742                                "#666666" /* Palette.neutralColor60 */)
743                        });
744                    }
745                    tooltip.updatePosition({
746                        plotX: x,
747                        plotY: y,
748                        negative: point.negative,
749                        ttBelow: point.ttBelow,
750                        h: anchor[2] || 0
751                    });
752                }
753                else {
754                    tooltip.hide();
755                    return;
756                }
757            }
758            // Show it
759            if (tooltip.isHidden && tooltip.label) {
760                tooltip.label.attr({
761                    opacity: 1
762                }).show();
763            }
764            tooltip.isHidden = false;
vendor: 1,523 bytes, lines 765-797
765        }
766        fireEvent(this, 'refresh');
767    }
768    /**
769     * Render the split tooltip. Loops over each point's text and adds
770     * a label next to the point, then uses the distribute function to
771     * find best non-overlapping positions.
772     *
773     * @private
774     * @function Highcharts.Tooltip#renderSplit
775     *
776     * @param {string|Array<(boolean|string)>} labels
777     *
778     * @param {Array<Highcharts.Point>} points
779     */
780    renderSplit(labels, points) {
781        const tooltip = this;
782        const { chart, chart: { chartWidth, chartHeight, plotHeight, plotLeft, plotTop, scrollablePixelsY = 0, scrollablePixelsX, styledMode }, distance, options, options: { positioner }, pointer } = tooltip;
783        const { scrollLeft = 0, scrollTop = 0 } = chart.scrollablePlotArea?.scrollingContainer || {};
784        // The area which the tooltip should be limited to. Limit to scrollable
785        // plot area if enabled, otherwise limit to the chart container. If
786        // outside is true it should be the whole viewport
787        const bounds = (tooltip.outside &&
788            typeof scrollablePixelsX !== 'number') ?
789            doc.documentElement.getBoundingClientRect() : {
790            left: scrollLeft,
791            right: scrollLeft + chartWidth,
792            top: scrollTop,
793            bottom: scrollTop + chartHeight
794        };
795        const tooltipLabel = tooltip.getLabel();
796        const ren = this.renderer || chart.renderer;
797        const headerTop = Boolean(chart.xAxis[0] && chart.xAxis[0].opposite);
798        const { left: chartLeft, top: chartTop } = pointer.getChartPosition();
799        let distributionBoxTop = plotTop + scrollTop;
800        let headerHeight = 0;
801        let adjustedPlotHeight = plotHeight - scrollablePixelsY;
802        /**
803         * Calculates the anchor position for the partial tooltip
804         *
805         * @private
806         * @param {Highcharts.Point} point The point related to the tooltip
807         * @return {Object} Returns an object with anchorX and anchorY
808         */
809        function getAnchor(point) {
810            const { isHeader, plotX = 0, plotY = 0, series } = point;
811            let anchorX;
812            let anchorY;
813            if (isHeader) {
814                // Set anchorX to plotX
815                anchorX = Math.max(plotLeft + plotX, plotLeft);
816                // Set anchorY to center of visible plot area.
817                anchorY = plotTop + plotHeight / 2;
818            }
819            else {
820                const { xAxis, yAxis } = series;
821                // Set anchorX to plotX. Limit to within xAxis.
822                anchorX = xAxis.pos + clamp(plotX, -distance, xAxis.len + distance);
823                // Set anchorY, limit to the scrollable plot area
824                if (series.shouldShowTooltip(0, yAxis.pos - plotTop + plotY, {
825                    ignoreX: true
826                })) {
827                    anchorY = yAxis.pos + plotY;
828                }
829            }
830            // Limit values to plot area
831            anchorX = clamp(anchorX, bounds.left - distance, bounds.right + distance);
832            return { anchorX, anchorY };
833        }
834        /**
835         * Calculates the position of the partial tooltip
836         *
837         * @private
838         * @param {number} anchorX
839         * The partial tooltip anchor x position
840         *
841         * @param {number} anchorY
842         * The partial tooltip anchor y position
843         *
844         * @param {boolean|undefined} isHeader
845         * Whether the partial tooltip is a header
846         *
847         * @param {number} boxWidth
848         * Width of the partial tooltip
849         *
850         * @return {Highcharts.PositionObject}
851         * Returns the partial tooltip x and y position
852         */
853        function defaultPositioner(anchorX, anchorY, isHeader, boxWidth, alignedLeft = true) {
854            let y;
855            let x;
856            if (isHeader) {
857                y = headerTop ? 0 : adjustedPlotHeight;
858                x = clamp(anchorX - (boxWidth / 2), bounds.left, bounds.right - boxWidth - (tooltip.outside ? chartLeft : 0));
859            }
860            else {
861                y = anchorY - distributionBoxTop;
862                x = alignedLeft ?
863                    anchorX - boxWidth - distance :
864                    anchorX + distance;
865                x = clamp(x, alignedLeft ? x : bounds.left, bounds.right);
866            }
867            // NOTE: y is relative to distributionBoxTop
868            return { x, y };
869        }
870        /**
871         * Updates the attributes and styling of the partial tooltip. Creates a
872         * new partial tooltip if it does not exists.
873         *
874         * @private
875         * @param {Highcharts.SVGElement|undefined} partialTooltip
876         *  The partial tooltip to update
877         * @param {Highcharts.Point} point
878         *  The point related to the partial tooltip
879         * @param {boolean|string} str The text for the partial tooltip
880         * @return {Highcharts.SVGElement} Returns the updated partial tooltip
881         */
882        function updatePartialTooltip(partialTooltip, point, str) {
883            let tt = partialTooltip;
884            const { isHeader, series } = point;
885            if (!tt) {
886                const attribs = {
887                    padding: options.padding,
888                    r: options.borderRadius
889                };
890                if (!styledMode) {
891                    attribs.fill = options.backgroundColor;
892                    attribs['stroke-width'] = options.borderWidth ?? 1;
893                }
894                tt = ren
895                    .label('', 0, 0, (options[isHeader ? 'headerShape' : 'shape']), void 0, void 0, options.useHTML)
896                    .addClass(tooltip.getClassName(point, true, isHeader))
897                    .attr(attribs)
898                    .add(tooltipLabel);
899            }
900            tt.isActive = true;
901            tt.attr({
902                text: str
903            });
904            if (!styledMode) {
905                tt.css(options.style)
906                    .attr({
907                    stroke: (options.borderColor ||
908                        point.color ||
909                        series.color ||
910                        "#333333" /* Palette.neutralColor80 */)
911                });
912            }
913            return tt;
914        }
915        // Graceful degradation for legacy formatters
916        if (isString(labels)) {
917            labels = [false, labels];
918        }
919        // Create the individual labels for header and points, ignore footer
920        let boxes = labels.slice(0, points.length + 1).reduce(function (boxes, str, i) {
921            if (str !== false && str !== '') {
922                const point = (points[i - 1] ||
923                    {
924                        // Item 0 is the header. Instead of this, we could also
925                        // use the crosshair label
926                        isHeader: true,
927                        plotX: points[0].plotX,
928                        plotY: plotHeight,
929                        series: {}
930                    });
931                const isHeader = point.isHeader;
932                // Store the tooltip label reference on the series
933                const owner = isHeader ? tooltip : point.series;
934                const tt = owner.tt = updatePartialTooltip(owner.tt, point, str.toString());
935                // Get X position now, so we can move all to the other side in
936                // case of overflow
937                const bBox = tt.getBBox();
938                const boxWidth = bBox.width + tt.strokeWidth();
939                if (isHeader) {
940                    headerHeight = bBox.height;
941                    adjustedPlotHeight += headerHeight;
942                    if (headerTop) {
943                        distributionBoxTop -= headerHeight;
944                    }
945                }
946                const { anchorX, anchorY } = getAnchor(point);
947                if (typeof anchorY === 'number') {
948                    const size = bBox.height + 1;
949                    const boxPosition = (positioner ?
950                        positioner.call(tooltip, boxWidth, size, point) :
951                        defaultPositioner(anchorX, anchorY, isHeader, boxWidth));
952                    boxes.push({
953                        // 0-align to the top, 1-align to the bottom
954                        align: positioner ? 0 : void 0,
955                        anchorX,
956                        anchorY,
957                        boxWidth,
958                        point,
959                        rank: pick(boxPosition.rank, isHeader ? 1 : 0),
960                        size,
961                        target: boxPosition.y,
962                        tt,
963                        x: boxPosition.x
964                    });
965                }
966                else {
967                    // Hide tooltips which anchorY is outside the visible plot
968                    // area
969                    tt.isActive = false;
970                }
971            }
972            return boxes;
973        }, []);
974        // Realign the tooltips towards the right if there is not enough space
975        // to the left and there is space to the right
976        if (!positioner && boxes.some((box) => {
977            // Always realign if the beginning of a label is outside bounds
978            const { outside } = tooltip;
979            const boxStart = (outside ? chartLeft : 0) + box.anchorX;
980            if (boxStart < bounds.left &&
981                boxStart + box.boxWidth < bounds.right) {
982                return true;
983            }
984            // Otherwise, check if there is more space available to the right
985            return boxStart < (chartLeft - bounds.left) + box.boxWidth &&
986                bounds.right - boxStart > boxStart;
987        })) {
988            boxes = boxes.map((box) => {
989                const { x, y } = defaultPositioner(box.anchorX, box.anchorY, box.point.isHeader, box.boxWidth, false);
990                return extend(box, {
991                    target: y,
992                    x
993                });
994            });
995        }
996        // Clean previous run (for missing points)
997        tooltip.cleanSplit();
998        // Distribute and put in place
999        distribute(boxes, adjustedPlotHeight);
1000        const boxExtremes = {
1001            left: chartLeft,
1002            right: chartLeft
1003        };
1004        // Get the extremes from series tooltips
1005        boxes.forEach(function (box) {
1006            const { x, boxWidth, isHeader } = box;
1007            if (!isHeader) {
1008                if (tooltip.outside && chartLeft + x < boxExtremes.left) {
1009                    boxExtremes.left = chartLeft + x;
1010                }
1011                if (!isHeader &&
1012                    tooltip.outside &&
1013                    boxExtremes.left + boxWidth > boxExtremes.right) {
1014                    boxExtremes.right = chartLeft + x;
1015                }
1016            }
1017        });
1018        boxes.forEach(function (box) {
1019            const { x, anchorX, anchorY, pos, point: { isHeader } } = box;
1020            const attributes = {
1021                visibility: typeof pos === 'undefined' ? 'hidden' : 'inherit',
1022                x,
1023                /* NOTE: y should equal pos to be consistent with !split
1024                 * tooltip, but is currently relative to plotTop. Is left as is
1025                 * to avoid breaking change. Remove distributionBoxTop to make
1026                 * it consistent.
1027                 */
1028                y: (pos || 0) + distributionBoxTop,
1029                anchorX,
1030                anchorY
1031            };
1032            // Handle left-aligned tooltips overflowing the chart area
1033            if (tooltip.outside && x < anchorX) {
1034                const offset = chartLeft - boxExtremes.left;
1035                // Skip this if there is no overflow
1036                if (offset > 0) {
1037                    if (!isHeader) {
1038                        attributes.x = x + offset;
1039                        attributes.anchorX = anchorX + offset;
1040                    }
1041                    if (isHeader) {
1042                        attributes.x = (boxExtremes.right - boxExtremes.left) / 2;
1043                        attributes.anchorX = anchorX + offset;
1044                    }
1045                }
1046            }
1047            // Put the label in place
1048            box.tt.attr(attributes);
1049        });
1050        /* If we have a separate tooltip container, then update the necessary
1051         * container properties.
1052         * Test that tooltip has its own container and renderer before executing
1053         * the operation.
1054         */
1055        const { container, outside, renderer } = tooltip;
1056        if (outside && container && renderer) {
1057            // Set container size to fit the bounds
1058            const { width, height, x, y } = tooltipLabel.getBBox();
1059            renderer.setSize(width + x, height + y, false);
1060            // Position the tooltip container to the chart container
1061            container.style.left = boxExtremes.left + 'px';
1062            container.style.top = chartTop + 'px';
1063        }
1064        // Workaround for #18927, artefacts left by the shadows of split
1065        // tooltips in Safari v16 (2023). Check again with later versions if we
1066        // can remove this.
1067        if (isSafari) {
1068            tooltipLabel.attr({
1069                // Force a redraw of the whole group by chaining the opacity
1070                // slightly
1071                opacity: tooltipLabel.opacity === 1 ? 0.999 : 1
1072            });
1073        }
1074    }
1075    /**
1076     * If the `stickOnContact` option is active, this will add a tracker shape.
1077     *
1078     * @private
1079     * @function Highcharts.Tooltip#drawTracker
1080     */
1081    drawTracker() {
1082        const tooltip = this;
1083        if (!this.shouldStickOnContact()) {
1084            if (tooltip.tracker) {
1085                tooltip.tracker = tooltip.tracker.destroy();
1086            }
1087            return;
1088        }
1089        const chart = tooltip.chart;
1090        const label = tooltip.label;
1091        const points = tooltip.shared ? chart.hoverPoints : chart.hoverPoint;
1092        if (!label || !points) {
1093            return;
1094        }
1095        const box = {
1096            x: 0,
1097            y: 0,
1098            width: 0,
1099            height: 0
1100        };
1101        // Combine anchor and tooltip
1102        const anchorPos = this.getAnchor(points);
1103        const labelBBox = label.getBBox();
1104        anchorPos[0] += chart.plotLeft - (label.translateX || 0);
1105        anchorPos[1] += chart.plotTop - (label.translateY || 0);
1106        // When the mouse pointer is between the anchor point and the label,
1107        // the label should stick.
1108        box.x = Math.min(0, anchorPos[0]);
1109        box.y = Math.min(0, anchorPos[1]);
1110        box.width = (anchorPos[0] < 0 ?
1111            Math.max(Math.abs(anchorPos[0]), labelBBox.width - anchorPos[0]) :
1112            Math.max(Math.abs(anchorPos[0]), labelBBox.width));
1113        box.height = (anchorPos[1] < 0 ?
1114            Math.max(Math.abs(anchorPos[1]), labelBBox.height - Math.abs(anchorPos[1])) :
1115            Math.max(Math.abs(anchorPos[1]), labelBBox.height));
1116        if (tooltip.tracker) {
1117            tooltip.tracker.attr(box);
1118        }
1119        else {
1120            tooltip.tracker = label.renderer
1121                .rect(box)
1122                .addClass('highcharts-tracker')
1123                .add(label);
1124            if (!chart.styledMode) {
1125                tooltip.tracker.attr({
1126                    fill: 'rgba(0,0,0,0)'
1127                });
1128            }
1129        }
1130    }
1131    /**
1132     * @private
1133     */
1134    styledModeFormat(formatString) {
1135        return formatString
1136            .replace('style="font-size: 0.8em"', 'class="highcharts-header"')
1137            .replace(/style="color:{(point|series)\.color}"/g, 'class="highcharts-color-{$1.colorIndex} ' +
1138            '{series.options.className} ' +
1139            '{point.options.className}"');
1140    }
1141    /**
1142     * Format the footer/header of the tooltip
1143     * #3397: abstraction to enable formatting of footer and header
1144     *
1145     * @private
1146     * @function Highcharts.Tooltip#headerFooterFormatter
1147     */
1148    headerFooterFormatter(point, isFooter) {
1149        const series = point.series, tooltipOptions = series.tooltipOptions, xAxis = series.xAxis, dateTime = xAxis && xAxis.dateTime, e = {
1150            isFooter,
1151            point
1152        };
1153        let xDateFormat = tooltipOptions.xDateFormat || '', formatString = tooltipOptions[isFooter ? 'footerFormat' : 'headerFormat'];
1154        fireEvent(this, 'headerFormatter', e, function (e) {
1155            // Guess the best date format based on the closest point distance
1156            // (#568, #3418)
1157            if (dateTime && !xDateFormat && isNumber(point.key)) {
1158                xDateFormat = dateTime.getXDateFormat(point.key, tooltipOptions.dateTimeLabelFormats);
1159            }
1160            // Insert the footer date format if any
1161            if (dateTime && xDateFormat) {
1162                if (isObject(xDateFormat)) {
1163                    const format = xDateFormat;
1164                    dateFormats[0] = (timestamp) => series.chart.time.dateFormat(format, timestamp);
1165                    xDateFormat = '%0';
1166                }
1167                (point.tooltipDateKeys || ['key']).forEach((key) => {
1168                    formatString = formatString.replace(new RegExp('point\\.' + key + '([ \\)}])', ''), `(point.${key}:${xDateFormat})$1`);
1169                });
1170            }
1171            // Replace default header style with class name
1172            if (series.chart.styledMode) {
1173                formatString = this.styledModeFormat(formatString);
1174            }
1175            e.text = format(formatString, point, this.chart);
1176        });
1177        return e.text || '';
1178    }
1179    /**
1180     * Updates the tooltip with the provided tooltip options.
1181     *
1182     * @function Highcharts.Tooltip#update
1183     *
1184     * @param {Highcharts.TooltipOptions} options
1185     *        The tooltip options to update.
1186     */
1187    update(options) {
1188        this.destroy();
1189        this.init(this.chart, merge(true, this.options, options));
1190    }
1191    /**
1192     * Find the new position and perform the move
1193     *
1194     * @private
1195     * @function Highcharts.Tooltip#updatePosition
1196     *
1197     * @param {Highcharts.Point} point
1198     */
1199    updatePosition(point) {
1200        const { chart, container, distance, options, pointer, renderer } = this, { height = 0, width = 0 } = this.getLabel(), 
1201        // Needed for outside: true (#11688)
1202        { left, top, scaleX, scaleY } = pointer.getChartPosition(), pos = (options.positioner || this.getPosition).call(this, width, height, point), doc = H.doc;
1203        let anchorX = (point.plotX || 0) + chart.plotLeft, anchorY = (point.plotY || 0) + chart.plotTop, pad;
1204        // Set the renderer size dynamically to prevent document size to change.
1205        // Renderer only exists when tooltip is outside.
1206        if (renderer && container) {
1207            // Corrects positions, occurs with tooltip positioner (#16944)
1208            if (options.positioner) {
1209                pos.x += left - distance;
1210                pos.y += top - distance;
1211            }
1212            // Pad it by the border width and distance. Add 2 to make room for
1213            // the default shadow (#19314).
1214            pad = (options.borderWidth || 0) + 2 * distance + 2;
1215            renderer.setSize(
1216            // Clamp width to keep tooltip in viewport (#21698)
1217            // and subtract one since tooltip container has 'left: 1px;'
1218            clamp(width + pad, 0, doc.documentElement.clientWidth) - 1, height + pad, false);
1219            // Anchor and tooltip container need scaling if chart container has
1220            // scale transform/css zoom. #11329.
1221            if (scaleX !== 1 || scaleY !== 1) {
1222                css(container, {
1223                    transform: `scale(${scaleX}, ${scaleY})`
1224                });
1225                anchorX *= scaleX;
1226                anchorY *= scaleY;
1227            }
1228            anchorX += left - pos.x;
1229            anchorY += top - pos.y;
1230        }
1231        // Do the move
1232        this.move(Math.round(pos.x), Math.round(pos.y || 0), // Can be undefined (#3977)
1233        anchorX, anchorY);
1234    }
1235}
1236/* *
1237 *
1238 *  Class namespace
1239 *
1240 * */
1241(function (Tooltip) {
1242    /* *
1243     *
1244     *  Declarations
1245     *
1246     * */
1247    /* *
1248     *
1249     *  Functions
1250     *
1251     * */
1252    /**
1253     * @private
1254     */
1255    function compose(PointerClass) {
1256        if (pushUnique(composed, 'Core.Tooltip')) {
1257            addEvent(PointerClass, 'afterInit', function () {
1258                const chart = this.chart;
1259                if (chart.options.tooltip) {
1260                    /**
1261                     * Tooltip object for points of series.
1262                     *
1263                     * @name Highcharts.Chart#tooltip
1264                     * @type {Highcharts.Tooltip}
1265                     */
1266                    chart.tooltip = new Tooltip(chart, chart.options.tooltip, this);
1267                }
1268            });
1269        }
1270    }
1271    Tooltip.compose = compose;
1272})(Tooltip || (Tooltip = {}));
1273/* *
1274 *
1275 *  Default export
1276 *
1277 * */
1278export default Tooltip;
1279/* *
1280 *
1281 *  API Declarations
1282 *
1283 * */
1284/**
1285 * Callback function to format the text of the tooltip from scratch.
1286 *
1287 * In case of single or shared tooltips, a string should be returned. In case
1288 * of split tooltips, it should return an array where the first item is the
1289 * header, and subsequent items are mapped to the points. Return `false` to
1290 * disable tooltip for a specific point on series.
1291 *
1292 * @callback Highcharts.TooltipFormatterCallbackFunction
1293 *
1294 * @param {Highcharts.Point} this
1295 * The formatter's context is the hovered `Point` instance. In case of shared or
1296 * split tooltips, all points are available in `this.points`.
1297 *
1298 * @param {Highcharts.Tooltip} tooltip
1299 * The tooltip instance
1300 *
1301 * @return {false|string|Array<(string|null|undefined)>|null|undefined}
1302 * Formatted text or false
1303 */
1304/**
1305 * A callback function to place the tooltip in a specific position.
1306 *
1307 * @callback Highcharts.TooltipPositionerCallbackFunction
1308 *
1309 * @param {Highcharts.Tooltip} this
1310 * Tooltip context of the callback.
1311 *
1312 * @param {number} labelWidth
1313 * Width of the tooltip.
1314 *
1315 * @param {number} labelHeight
1316 * Height of the tooltip.
1317 *
1318 * @param {Highcharts.TooltipPositionerPointObject} point
1319 * Point information for positioning a tooltip.
1320 *
1321 * @return {Highcharts.PositionObject}
1322 * New position for the tooltip.
1323 */
1324/**
1325 * Point information for positioning a tooltip.
1326 *
1327 * @interface Highcharts.TooltipPositionerPointObject
1328 * @extends Highcharts.Point
1329 */ /**
1330* If `tooltip.split` option is enabled and positioner is called for each of the
1331* boxes separately, this property indicates the call on the xAxis header, which
1332* is not a point itself.
1333* @name Highcharts.TooltipPositionerPointObject#isHeader
1334* @type {boolean}
1335*/ /**
1336* The reference point relative to the plot area. Add chart.plotLeft to get the
1337* full coordinates.
1338* @name Highcharts.TooltipPositionerPointObject#plotX
1339* @type {number}
1340*/ /**
1341* The reference point relative to the plot area. Add chart.plotTop to get the
1342* full coordinates.
1343* @name Highcharts.TooltipPositionerPointObject#plotY
1344* @type {number}
1345*/
1346/**
1347 * @typedef {"callout"|"circle"|"rect"} Highcharts.TooltipShapeValue
1348 */
1349''; // 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.