'use strict' ; import type Axis from './Axis/Axis' ; import type Chart from './Chart/Chart' ; import type ColorType from './Color/ColorType' ; import type Point from './Series/Point' ; import type PointerEvent from './PointerEvent' ; import type PositionObject from './Renderer/PositionObject' ; import type RectangleObject from './Renderer/RectangleObject' ; import type Series from './Series/Series' ; import type SVGAttributes from './Renderer/SVG/SVGAttributes' ; import type SVGElement from './Renderer/SVG/SVGElement' ; import type SVGRenderer from './Renderer/SVG/SVGRenderer' ; import type TooltipOptions from './TooltipOptions' ; import F from './FormatUtilities.js' ; const { format } = F ; import H from './Globals.js' ; const { doc } = H ; import { Palette } from './Color/Palettes.js' ; import R from './Renderer/RendererUtilities.js' ; const { distribute } = R ; import RendererRegistry from './Renderer/RendererRegistry.js' ; import U from './Utilities.js' ; const { addEvent , clamp , css , defined , discardElement , extend , fireEvent , isArray , isNumber , isString , merge , pick , splat , syncTimeout } = U ; declare module './Series/PointLike' { interface PointLike { isHeader ? : boolean ; tooltipPos ? : Array < number > ; } } declare module './Series/SeriesLike' { interface SeriesLike { noSharedTooltip ? : boolean ; tt ? : SVGElement ; } } declare module './Series/SeriesOptions' { interface SeriesOptions { tooltip ? : DeepPartial < TooltipOptions > ; } } declare module '../Core/TooltipOptions' { interface TooltipOptions { distance ? : number ; } } interface BoxObject extends R . BoxObject { anchorX : number ; anchorY : number ; boxWidth : number ; point : Point ; tt : SVGElement ; x : number ; } class name param { Highcharts . Chart } chart param { Highcharts . TooltipOptions } options class Tooltip { public constructor ( chart , options ) { this . chart = chart ; this . init ( chart , options ) ; } public allowShared : boolean = true ; public chart : Chart ; public container : globalThis . HTMLElement = void 0 as any ; public crosshairs : Array < null > = [ ] ; public distance : number = 0 ; public followPointer ? : boolean ; public hideTimer ? : number ; public inContact ? : boolean ; public isHidden : boolean = true ; public isSticky : boolean = false ; public label ? : SVGElement ; public len ? : number ; public now : Record < string , number > = { } ; public options : TooltipOptions = { } as any ; public outside : boolean = false ; public renderer ? : SVGRenderer ; public shared ? : boolean ; public split ? : boolean ; public tooltipTimeout ? : number ; public tracker ? : SVGElement ; public tt ? : SVGElement ; private function public applyFilter ( ) { const chart = this . chart ; chart . renderer . definition ( { tagName : 'filter' , attributes : { id : 'drop-shadow-' + chart . index , opacity : 0.5 } , children : [ { tagName : 'feGaussianBlur' , attributes : { 'in' : 'SourceAlpha' , stdDeviation : 1 } } , { tagName : 'feOffset' , attributes : { dx : 1 , dy : 1 } } , { tagName : 'feComponentTransfer' , children : [ { tagName : 'feFuncA' , attributes : { type : 'linear' , slope : 0.3 } } ] } , { tagName : 'feMerge' , children : [ { tagName : 'feMergeNode' } , { tagName : 'feMergeNode' , attributes : { 'in' : 'SourceGraphic' } } ] } ] } ) ; } private function public bodyFormatter ( items ) { return items . map ( function ( item ) : string { const tooltipOptions = ( item as any ) . series . tooltipOptions ; return ( ( tooltipOptions as any ) [ ( ( item as any ) . point . formatPrefix || 'point' ) + 'Formatter' ] || ( item as any ) . point . tooltipFormatter ) . call ( ( item as any ) . point , tooltipOptions [ ( ( item as any ) . point . formatPrefix || 'point' ) + 'Format' ] || '' ) ; } ) ; } private function param { boolean } force public cleanSplit ( force ? ) { this . chart . series . forEach ( function ( series ) : void { const tt = series && series . tt ; if ( tt ) { if ( ! tt . isActive || force ) { series . tt = tt . destroy ( ) ; } else { tt . isActive = false ; } } } ) ; } function param { Highcharts . Tooltip } tooltip return { string | Array < string > } public defaultFormatter ( this , tooltip ) { const items = this . points || splat ( this ) ; let s ; s = [ tooltip . tooltipFooterHeaderFormatter ( items [ 0 ] ) ] ; s = s . concat ( tooltip . bodyFormatter ( items ) ) ; s . push ( tooltip . tooltipFooterHeaderFormatter ( items [ 0 ] , true ) ) ; return s ; } function public destroy ( ) { if ( this . label ) { this . label = this . label . destroy ( ) ; } if ( this . split && this . tt ) { this . cleanSplit ( true ) ; this . tt = this . tt . destroy ( ) ; } if ( this . renderer ) { this . renderer = this . renderer . destroy ( ) as any ; discardElement ( this . container ) ; } U . clearTimeout ( this . hideTimer as any ) ; U . clearTimeout ( this . tooltipTimeout as any ) ; } private function public getAnchor ( points , mouseEvent ? ) { const chart = this . chart , pointer = chart . pointer , inverted = chart . inverted , plotTop = chart . plotTop , plotLeft = chart . plotLeft ; let ret , yAxis , xAxis , plotX = 0 , plotY = 0 ; points = splat ( points ) ; if ( this . followPointer && mouseEvent ) { if ( typeof mouseEvent . chartX === 'undefined' ) { mouseEvent = pointer . normalize ( mouseEvent ) ; } ret = [ mouseEvent . chartX - plotLeft , mouseEvent . chartY - plotTop ] ; } else if ( points [ 0 ] . tooltipPos ) { ret = points [ 0 ] . tooltipPos ; } else { points . forEach ( function ( point ) : void { yAxis = point . series . yAxis ; xAxis = point . series . xAxis ; plotX += point . plotX || 0 ; plotY += ( point . plotLow ? ( point . plotLow + ( point . plotHigh || 0 ) ) / 2 : ( point . plotY || 0 ) ) ; if ( xAxis && yAxis ) { if ( ! inverted ) { plotX += xAxis . pos - plotLeft ; plotY += yAxis . pos - plotTop ; } else { plotX += ( plotTop + chart . plotHeight - xAxis . len - xAxis . pos ) ; plotY += ( plotLeft + chart . plotWidth - yAxis . len - yAxis . pos ) ; } } } ) ; plotX /= points . length ; plotY /= points . length ; ret = [ inverted ? chart . plotWidth - plotY : plotX , inverted ? chart . plotHeight - plotX : plotY ] ; if ( this . shared && points . length > 1 && mouseEvent ) { if ( inverted ) { ret [ 0 ] = mouseEvent . chartX - plotLeft ; } else { ret [ 1 ] = mouseEvent . chartY - plotTop ; } } } return ret . map ( Math . round ) ; } function return { Highcharts . SVGElement } public getLabel ( ) { const tooltip = this , styledMode = this . chart . styledMode , options = this . options , doSplit = this . split && this . allowShared , className = ( 'tooltip' + ( defined ( options . className ) ? ' ' + options . className : '' ) ) , pointerEvents = ( options . style . pointerEvents || ( ! this . followPointer && options . stickOnContact ? 'auto' : 'none' ) ) , onMouseEnter = function ( ) : void { tooltip . inContact = true ; } , onMouseLeave = function ( e ) : void { const series = tooltip . chart . hoverSeries ; tooltip . inContact = tooltip . shouldStickOnContact ( ) && tooltip . chart . pointer . inClass ( e . relatedTarget as any , 'highcharts-tooltip' ) ; if ( ! tooltip . inContact && series && series . onMouseOut ) { series . onMouseOut ( ) ; } } ; let container , renderer = this . chart . renderer ; if ( tooltip . label ) { const wasSplit = ! tooltip . label . hasClass ( 'highcharts-label' ) ; if ( ( doSplit && ! wasSplit ) || ( ! doSplit && wasSplit ) ) { tooltip . destroy ( ) ; } } if ( ! this . label ) { if ( this . outside ) { const chartStyle = this . chart . options . chart . style , Renderer = RendererRegistry . getRendererType ( ) ; name type { Highcharts . HTMLDOMElement | undefined } this . container = container = H . doc . createElement ( 'div' ) ; container . className = 'highcharts-tooltip-container' ; css ( container , { position : 'absolute' , top : '1px' , pointerEvents , zIndex : Math . max ( this . options . style . zIndex || 0 , ( chartStyle && chartStyle . zIndex || 0 ) + 3 ) } ) ; addEvent ( container , 'mouseenter' , onMouseEnter ) ; addEvent ( container , 'mouseleave' , onMouseLeave ) ; H . doc . body . appendChild ( container ) ; name type { Highcharts . SVGRenderer | undefined } this . renderer = renderer = new Renderer ( container , 0 , 0 , chartStyle , void 0 , void 0 , renderer . styledMode ) ; } if ( doSplit ) { this . label = renderer . g ( className ) ; } else { this . label = renderer . label ( '' , 0 , 0 , options . shape , void 0 , void 0 , options . useHTML , void 0 , className ) . attr ( { padding : options . padding , r : options . borderRadius } ) ; if ( ! styledMode ) { this . label . attr ( { fill : options . backgroundColor , 'stroke-width' : options . borderWidth } ) . css ( options . style ) . css ( { pointerEvents } ) . shadow ( options . shadow ) ; } } if ( styledMode && options . shadow ) { this . applyFilter ( ) ; this . label . attr ( { filter : 'url(#drop-shadow-' + this . chart . index + ')' } ) ; } if ( tooltip . outside && ! tooltip . split ) { const label = this . label ; const { xSetter , ySetter } = label ; label . xSetter = function ( value ) : void { xSetter . call ( label , tooltip . distance ) ; container . style . left = value + 'px' ; } ; label . ySetter = function ( value ) : void { ySetter . call ( label , tooltip . distance ) ; container . style . top = value + 'px' ; } ; } this . label . on ( 'mouseenter' , onMouseEnter ) . on ( 'mouseleave' , onMouseLeave ) . attr ( { zIndex : 8 } ) . add ( ) ; } return this . label ; } private function public getPosition ( boxWidth , boxHeight , point ) { const chart = this . chart , distance = this . distance , ret = { } as PositionObject , h = ( chart . inverted && ( point as any ) . h ) || 0 , outside = this . outside , outerWidth = outside ? doc . documentElement . clientWidth - 2 * distance : chart . chartWidth , outerHeight = outside ? Math . max ( doc . body . scrollHeight , doc . documentElement . scrollHeight , doc . body . offsetHeight , doc . documentElement . offsetHeight , doc . documentElement . clientHeight ) : chart . chartHeight , chartPosition = chart . pointer . getChartPosition ( ) , scaleX = ( val ) : number => ( val * chartPosition . scaleX ) , scaleY = ( val ) : number => ( val * chartPosition . scaleY ) , buildDimensionArray = ( dim ) : Array < number | string > => { const isX = dim === 'x' ; return [ dim , isX ? outerWidth : outerHeight , isX ? boxWidth : boxHeight ] . concat ( outside ? [ isX ? scaleX ( boxWidth ) : scaleY ( boxHeight ) , isX ? chartPosition . left - distance + scaleX ( ( point . plotX as any ) + chart . plotLeft ) : chartPosition . top - distance + scaleY ( ( point . plotY as any ) + chart . plotTop ) , 0 , isX ? outerWidth : outerHeight ] : [ isX ? boxWidth : boxHeight , isX ? ( point . plotX as any ) + chart . plotLeft : ( point . plotY as any ) + chart . plotTop , isX ? chart . plotLeft : chart . plotTop , isX ? chart . plotLeft + chart . plotWidth : chart . plotTop + chart . plotHeight ] ) ; } ; let first = buildDimensionArray ( 'y' ) , second = buildDimensionArray ( 'x' ) , swapped ; let flipped = ! ! point . negative ; if ( ! chart . polar && chart . hoverSeries && chart . hoverSeries . yAxis && chart . hoverSeries . yAxis . reversed ) { flipped = ! flipped ; } const preferFarSide = ! this . followPointer && pick ( point . ttBelow , ! chart . inverted === flipped ) , firstDimension = function ( dim , outerSize , innerSize , scaledInnerSize , point , min , max ) : ( boolean | undefined ) { const scaledDist = outside ? ( dim === 'y' ? scaleY ( distance ) : scaleX ( distance ) ) : distance , scaleDiff = ( innerSize - scaledInnerSize ) / 2 , roomLeft = scaledInnerSize < point - distance , roomRight = point + distance + scaledInnerSize < outerSize , alignedLeft = point - scaledDist - innerSize + scaleDiff , alignedRight = point + scaledDist - scaleDiff ; if ( preferFarSide && roomRight ) { ret [ dim ] = alignedRight ; } else if ( ! preferFarSide && roomLeft ) { ret [ dim ] = alignedLeft ; } else if ( roomLeft ) { ret [ dim ] = Math . min ( max - scaledInnerSize , alignedLeft - h < 0 ? alignedLeft : alignedLeft - h ) ; } else if ( roomRight ) { ret [ dim ] = Math . max ( min , alignedRight + h + innerSize > outerSize ? alignedRight : alignedRight + h ) ; } else { return false ; } } , secondDimension = function ( dim , outerSize , innerSize , scaledInnerSize , point ) : ( boolean | undefined ) { let retVal ; if ( point < distance || point > outerSize - distance ) { retVal = false ; } else if ( point < innerSize / 2 ) { ret [ dim ] = 1 ; } else if ( point > outerSize - scaledInnerSize / 2 ) { ret [ dim ] = outerSize - scaledInnerSize - 2 ; } else { ret [ dim ] = point - innerSize / 2 ; } return retVal ; } , swap = function ( count ? ) : void { const temp = first ; first = second ; second = temp ; swapped = count ; } , run = function ( ) : void { if ( firstDimension . apply ( 0 , first as any ) !== false ) { if ( secondDimension . apply ( 0 , second as any ) === false && ! swapped ) { swap ( true ) ; run ( ) ; } } else if ( ! swapped ) { swap ( true ) ; run ( ) ; } else { ret . x = ret . y = 0 ; } } ; if ( chart . inverted || ( this . len as any ) > 1 ) { swap ( ) ; } run ( ) ; return ret ; } function param { number } delay public hide ( delay ? ) { const tooltip = this ; U . clearTimeout ( this . hideTimer as any ) ; delay = pick ( delay , this . options . hideDelay ) ; if ( ! this . isHidden ) { this . hideTimer = syncTimeout ( function ( ) : void { tooltip . getLabel ( ) . fadeOut ( delay ? void 0 : delay ) ; tooltip . isHidden = true ; } , delay ) ; } } private function param { Highcharts . Chart } chart param { Highcharts . TooltipOptions } options public init ( chart , options ) { readonly name type { Highcharts . Chart } this . chart = chart ; readonly name type { Highcharts . TooltipOptions } this . options = options ; private readonly name type { Array < null > } this . crosshairs = [ ] ; private readonly name type { Highcharts . PositionObject } this . now = { x : 0 , y : 0 } ; private readonly name type { boolean } this . isHidden = true ; readonly name type { boolean | undefined } this . split = options . split && ! chart . inverted && ! chart . polar ; readonly name type { boolean | undefined } this . shared = options . shared || this . split ; readonly name type { boolean } todo this . outside = pick ( options . outside , Boolean ( chart . scrollablePixelsX || chart . scrollablePixelsY ) ) ; } public shouldStickOnContact ( ) { return ! ! ( ! this . followPointer && this . options . stickOnContact ) ; } /**
     * Returns true, if the pointer is in contact with the tooltip tracker.
     */ public isStickyOnContact ( ) { return ! ! ( this . shouldStickOnContact ( ) && this . inContact ) ; } private function param { number } x param { number } y param { number } anchorX param { number } anchorY public move ( x , y , anchorX , anchorY ) { const tooltip = this , now = tooltip . now , animate = tooltip . options . animation !== false && ! tooltip . isHidden && ( Math . abs ( x - now . x ) > 1 || Math . abs ( y - now . y ) > 1 ) , skipAnchor = tooltip . followPointer || ( tooltip . len as any ) > 1 ; extend ( now , { x : animate ? ( 2 * now . x + x ) / 3 : x , y : animate ? ( now . y + y ) / 2 : y , anchorX : skipAnchor ? void 0 : animate ? ( 2 * now . anchorX + anchorX ) / 3 : anchorX , anchorY : skipAnchor ? void 0 : animate ? ( now . anchorY + anchorY ) / 2 : anchorY } ) ; tooltip . getLabel ( ) . attr ( now ) ; tooltip . drawTracker ( ) ; if ( animate ) { U . clearTimeout ( this . tooltipTimeout as any ) ; this . tooltipTimeout = setTimeout ( function ( ) : void { if ( tooltip ) { tooltip . move ( x , y , anchorX , anchorY ) ; } } , 32 ) as any ; } } function param { Highcharts . Point | Array < Highcharts . Point > } pointOrPoints param { Highcharts . PointerEventObject } mouseEvent public refresh ( pointOrPoints , mouseEvent ? ) { const tooltip = this , chart = this . chart , options = tooltip . options , points = splat ( pointOrPoints ) , point = points [ 0 ] , pointConfig = [ ] as Array < Tooltip . FormatterContextObject > , formatter = options . formatter || tooltip . defaultFormatter , shared = tooltip . shared , styledMode = chart . styledMode ; let textConfig = { } as Tooltip . FormatterContextObject ; if ( ! options . enabled || ! point . series ) { return ; } U . clearTimeout ( this . hideTimer ) ; tooltip . allowShared = ! ( ! isArray ( pointOrPoints ) && pointOrPoints . series && pointOrPoints . series . noSharedTooltip ) ; tooltip . followPointer = ( ! tooltip . split && point . series . tooltipOptions . followPointer ) ; const anchor = tooltip . getAnchor ( pointOrPoints , mouseEvent ) , x = anchor [ 0 ] , y = anchor [ 1 ] ; if ( shared && tooltip . allowShared ) { chart . pointer . applyInactiveState ( points ) ; points . forEach ( function ( item ) : void { item . setState ( 'hover' ) ; pointConfig . push ( item . getLabelConfig ( ) ) ; } ) ; textConfig = { x : point . category , y : point . y } as any ; textConfig . points = pointConfig ; } else { textConfig = point . getLabelConfig ( ) ; } this . len = pointConfig . length ; const text = formatter . call ( textConfig , tooltip ) ; const currentSeries = point . series ; this . distance = pick ( currentSeries . tooltipOptions . distance , 16 ) ; if ( text === false ) { this . hide ( ) ; } else { if ( tooltip . split && tooltip . allowShared ) { this . renderSplit ( text , points ) ; } else { let checkX = x ; let checkY = y ; if ( mouseEvent && chart . pointer . isDirectTouch ) { checkX = mouseEvent . chartX - chart . plotLeft ; checkY = mouseEvent . chartY - chart . plotTop ; } if ( chart . polar || currentSeries . options . clip === false || points . some ( ( p ) : boolean => p . series . shouldShowTooltip ( checkX , checkY ) ) ) { const label = tooltip . getLabel ( ) ; if ( ! options . style . width || styledMode ) { label . css ( { width : this . chart . spacingBox . width + 'px' } ) ; } label . attr ( { text : text && ( text as any ) . join ? ( text as any ) . join ( '' ) : text } ) ; label . removeClass ( /highcharts-color-[\d]+/g ) . addClass ( 'highcharts-color-' + pick ( point . colorIndex , currentSeries . colorIndex ) ) ; if ( ! styledMode ) { label . attr ( { stroke : ( options . borderColor || point . color || currentSeries . color || Palette . neutralColor60 ) } ) ; } tooltip . updatePosition ( { plotX : x , plotY : y , negative : point . negative , ttBelow : point . ttBelow , h : anchor [ 2 ] || 0 } as any ) ; } else { tooltip . hide ( ) ; return ; } } if ( tooltip . isHidden && tooltip . label ) { tooltip . label . attr ( { opacity : 1 } ) . show ( ) ; } tooltip . isHidden = false ; } fireEvent ( this , 'refresh' ) ; } private function param { string | Array < ( boolean | string ) > } labels param { Array < Highcharts . Point > } points public renderSplit ( labels , points ) { const tooltip = this ; const { chart , chart : { chartWidth , chartHeight , plotHeight , plotLeft , plotTop , pointer , scrollablePixelsY = 0 , scrollablePixelsX , scrollingContainer : { scrollLeft , scrollTop } = { scrollLeft : 0 , scrollTop : 0 } , styledMode } , distance , options , options : { positioner } } = tooltip ; const bounds = ( tooltip . outside && typeof scrollablePixelsX !== 'number' ) ? doc . documentElement . getBoundingClientRect ( ) : { left : scrollLeft , right : scrollLeft + chartWidth , top : scrollTop , bottom : scrollTop + chartHeight } ; const tooltipLabel = tooltip . getLabel ( ) ; const ren = this . renderer || chart . renderer ; const headerTop = Boolean ( chart . xAxis [ 0 ] && chart . xAxis [ 0 ] . opposite ) ; const { left : chartLeft , top : chartTop } = pointer . getChartPosition ( ) ; let distributionBoxTop = plotTop + scrollTop ; let headerHeight = 0 ; let adjustedPlotHeight = plotHeight - scrollablePixelsY ; private param { Highcharts . Point } point return { Object } function getAnchor ( point ) { const { isHeader , plotX = 0 , plotY = 0 , series } = point ; let anchorX ; let anchorY ; if ( isHeader ) { anchorX = plotLeft + plotX ; anchorY = plotTop + plotHeight / 2 ; } else { const { xAxis , yAxis } = series ; anchorX = xAxis . pos + clamp ( plotX , - distance , xAxis . len + distance ) ; if ( series . shouldShowTooltip ( 0 , yAxis . pos - plotTop + plotY , { ignoreX : true } ) ) { anchorY = yAxis . pos + plotY ; } } anchorX = clamp ( anchorX , bounds . left - distance , bounds . right + distance ) ; return { anchorX , anchorY } ; } private param { number } anchorX param { number } anchorY param { boolean | undefined } isHeader param { number } boxWidth return { Highcharts . PositionObject } function defaultPositioner ( anchorX , anchorY , isHeader , boxWidth , alignedLeft = true ) { let y ; let x ; if ( isHeader ) { y = headerTop ? 0 : adjustedPlotHeight ; x = clamp ( anchorX - ( boxWidth / 2 ) , bounds . left , bounds . right - boxWidth - ( tooltip . outside ? chartLeft : 0 ) ) ; } else { y = anchorY - distributionBoxTop ; x = alignedLeft ? anchorX - boxWidth - distance : anchorX + distance ; x = clamp ( x , alignedLeft ? x : bounds . left , bounds . right ) ; } return { x , y } ; } private param { Highcharts . SVGElement | undefined } partialTooltip param { Highcharts . Point } point param { boolean | string } str return { Highcharts . SVGElement } function updatePartialTooltip ( partialTooltip , point , str ) { let tt = partialTooltip ; const { isHeader , series } = point ; const colorClass = 'highcharts-color-' + pick ( point . colorIndex , series . colorIndex , 'none' ) ; if ( ! tt ) { const attribs = { padding : options . padding , r : options . borderRadius } ; if ( ! styledMode ) { attribs . fill = options . backgroundColor ; attribs [ 'stroke-width' ] = options . borderWidth ; } tt = ren . label ( '' , 0 , 0 , ( options [ isHeader ? 'headerShape' : 'shape' ] ) , void 0 , void 0 , options . useHTML ) . addClass ( ( isHeader ? 'highcharts-tooltip-header ' : '' ) + 'highcharts-tooltip-box ' + colorClass ) . attr ( attribs ) . add ( tooltipLabel ) ; } tt . isActive = true ; tt . attr ( { text : str } ) ; if ( ! styledMode ) { tt . css ( options . style ) . shadow ( options . shadow ) . attr ( { stroke : ( options . borderColor || point . color || series . color || Palette . neutralColor80 ) } ) ; } return tt ; } if ( isString ( labels ) ) { labels = [ false , labels ] ; } let boxes = labels . slice ( 0 , points . length + 1 ) . reduce ( function ( boxes , str , i ) : Array < BoxObject > { if ( str !== false && str !== '' ) { const point = ( points [ i - 1 ] || { isHeader : true , plotX : points [ 0 ] . plotX , plotY : plotHeight , series : { } } ) ; const isHeader = ( point as any ) . isHeader ; const owner = isHeader ? tooltip : point . series ; const tt = owner . tt = updatePartialTooltip ( owner . tt , point , str . toString ( ) ) ; const bBox = tt . getBBox ( ) ; const boxWidth = bBox . width + tt . strokeWidth ( ) ; if ( isHeader ) { headerHeight = bBox . height ; adjustedPlotHeight += headerHeight ; if ( headerTop ) { distributionBoxTop -= headerHeight ; } } const { anchorX , anchorY } = getAnchor ( point ) ; if ( typeof anchorY === 'number' ) { const size = bBox . height + 1 ; const boxPosition = ( positioner ? positioner . call ( tooltip , boxWidth , size , point ) : defaultPositioner ( anchorX , anchorY , isHeader , boxWidth ) ) ; boxes . push ( { align : positioner ? 0 : void 0 , anchorX , anchorY , boxWidth , point , rank : pick ( ( boxPosition as any ) . rank , isHeader ? 1 : 0 ) , size , target : boxPosition . y , tt , x : boxPosition . x } ) ; } else { tt . isActive = false ; } } return boxes ; } , [ ] ) ; if ( ! positioner && boxes . some ( ( box ) : boolean => { const { outside } = tooltip ; const boxStart = ( outside ? chartLeft : 0 ) + box . anchorX ; if ( boxStart < bounds . left && boxStart + box . boxWidth < bounds . right ) { return true ; } return boxStart < ( chartLeft - bounds . left ) + box . boxWidth && bounds . right - boxStart > boxStart ; } ) ) { boxes = boxes . map ( ( box ) : BoxObject => { const { x , y } = defaultPositioner ( box . anchorX , box . anchorY , box . point . isHeader , box . boxWidth , false ) ; return extend ( box , { target : y , x } ) ; } ) ; } tooltip . cleanSplit ( ) ; distribute ( boxes , adjustedPlotHeight ) ; const boxExtremes = { left : chartLeft , right : chartLeft } ; boxes . forEach ( function ( box ) : void { const { x , boxWidth , isHeader } = box ; if ( ! isHeader ) { if ( tooltip . outside && chartLeft + x < boxExtremes . left ) { boxExtremes . left = chartLeft + x ; } if ( ! isHeader && tooltip . outside && boxExtremes . left + boxWidth > boxExtremes . right ) { boxExtremes . right = chartLeft + x ; } } } ) ; boxes . forEach ( function ( box ) : void { const { x , anchorX , anchorY , pos , point : { isHeader } } = box ; const attributes = { visibility : typeof pos === 'undefined' ? 'hidden' : 'inherit' , x , y : pos + distributionBoxTop , anchorX , anchorY } ; if ( tooltip . outside && x < anchorX ) { const offset = chartLeft - boxExtremes . left ; if ( offset > 0 ) { if ( ! isHeader ) { attributes . x = x + offset ; attributes . anchorX = anchorX + offset ; } if ( isHeader ) { attributes . x = ( boxExtremes . right - boxExtremes . left ) / 2 ; attributes . anchorX = anchorX + offset ; } } } box . tt . attr ( attributes ) ; } ) ; const { container , outside , renderer } = tooltip ; if ( outside && container && renderer ) { const { width , height , x , y } = tooltipLabel . getBBox ( ) ; renderer . setSize ( width + x , height + y , false ) ; container . style . left = boxExtremes . left + 'px' ; container . style . top = chartTop + 'px' ; } } private function private drawTracker ( ) { const tooltip = this ; if ( tooltip . followPointer || ! tooltip . options . stickOnContact ) { if ( tooltip . tracker ) { tooltip . tracker . destroy ( ) ; } return ; } const chart = tooltip . chart ; const label = tooltip . label ; const points = tooltip . shared ? chart . hoverPoints : chart . hoverPoint ; if ( ! label || ! points ) { return ; } const box = { x : 0 , y : 0 , width : 0 , height : 0 } ; const anchorPos = this . getAnchor ( points ) ; const labelBBox = label . getBBox ( ) ; anchorPos [ 0 ] += chart . plotLeft - label . translateX ; anchorPos [ 1 ] += chart . plotTop - label . translateY ; box . x = Math . min ( 0 , anchorPos [ 0 ] ) ; box . y = Math . min ( 0 , anchorPos [ 1 ] ) ; box . width = ( anchorPos [ 0 ] < 0 ? Math . max ( Math . abs ( anchorPos [ 0 ] ) , ( labelBBox . width - anchorPos [ 0 ] ) ) : Math . max ( Math . abs ( anchorPos [ 0 ] ) , labelBBox . width ) ) ; box . height = ( anchorPos [ 1 ] < 0 ? Math . max ( Math . abs ( anchorPos [ 1 ] ) , ( labelBBox . height - Math . abs ( anchorPos [ 1 ] ) ) ) : Math . max ( Math . abs ( anchorPos [ 1 ] ) , labelBBox . height ) ) ; if ( tooltip . tracker ) { tooltip . tracker . attr ( box ) ; } else { tooltip . tracker = label . renderer . rect ( box ) . addClass ( 'highcharts-tracker' ) . add ( label ) ; if ( ! chart . styledMode ) { tooltip . tracker . attr ( { fill : 'rgba(0,0,0,0)' } ) ; } } } private public styledModeFormat ( formatString ) { return formatString . replace ( 'style="font-size: 10px"' , 'class="highcharts-header"' ) . replace ( /style="color:{(point|series)\.color}"/g , 'class="highcharts-color-{$1.colorIndex}"' ) ; } private function public tooltipFooterHeaderFormatter ( labelConfig , isFooter ? ) { const series = labelConfig . series , tooltipOptions = series . tooltipOptions , xAxis = series . xAxis , dateTime = xAxis && xAxis . dateTime , e = { isFooter : isFooter , labelConfig : labelConfig } as AnyRecord ; let xDateFormat = tooltipOptions . xDateFormat , formatString = tooltipOptions [ isFooter ? 'footerFormat' : 'headerFormat' ] ; fireEvent ( this , 'headerFormatter' , e , function ( this , e ) : void { if ( dateTime && ! xDateFormat && isNumber ( labelConfig . key ) ) { xDateFormat = dateTime . getXDateFormat ( labelConfig . key , tooltipOptions . dateTimeLabelFormats ) ; } if ( dateTime && xDateFormat ) { ( ( labelConfig . point && labelConfig . point . tooltipDateKeys ) || [ 'key' ] ) . forEach ( function ( key ) : void { formatString = formatString . replace ( '{point.' + key + '}' , '{point.' + key + ':' + xDateFormat + '}' ) ; } ) ; } if ( series . chart . styledMode ) { formatString = this . styledModeFormat ( formatString ) ; } ( e as any ) . text = format ( formatString , { point : labelConfig , series : series } , this . chart ) ; } ) ; return e . text ; } function param { Highcharts . TooltipOptions } options public update ( options ) { this . destroy ( ) ; merge ( true , ( this . chart . options . tooltip as any ) . userOptions , options ) ; this . init ( this . chart , merge ( true , this . options , options ) ) ; } private function param { Highcharts . Point } point public updatePosition ( point ) { const chart = this . chart , options = this . options , pointer = chart . pointer , label = this . getLabel ( ) , chartPosition = pointer . getChartPosition ( ) , pos = ( options . positioner || this . getPosition ) . call ( this , label . width , label . height , point ) ; let anchorX = ( point . plotX as any ) + chart . plotLeft , anchorY = ( point . plotY as any ) + chart . plotTop , pad ; if ( this . outside ) { pad = options . borderWidth + 2 * this . distance ; ( this . renderer as any ) . setSize ( label . width + pad , label . height + pad , false ) ; if ( chartPosition . scaleX !== 1 || chartPosition . scaleY !== 1 ) { css ( this . container , { transform : `scale(${ chartPosition . scaleX }, ${ chartPosition . scaleY })` } ) ; anchorX *= chartPosition . scaleX ; anchorY *= chartPosition . scaleY ; } anchorX += chartPosition . left - pos . x ; anchorY += chartPosition . top - pos . y ; } this . move ( Math . round ( pos . x ) , Math . round ( pos . y || 0 ) , anchorX , anchorY ) ; } } namespace Tooltip { export interface FormatterCallbackFunction { ( this , tooltip ) : ( false | string | Array < string > ) ; } export interface FormatterContextObject extends Point . PointLabelObject { points ? : Array < FormatterContextObject > ; } export interface PositionerCallbackFunction { ( this , labelWidth , labelHeight , point ) : PositionObject ; } export interface PositionerPointObject { isHeader : true ; plotX : number ; plotY : number ; } export type ShapeValue = ( 'callout' | 'circle' | 'square' | 'rect' ) ; } export default Tooltip ; callback Highcharts . TooltipFormatterCallbackFunction param { Highcharts . TooltipFormatterContextObject } this param { Highcharts . Tooltip } tooltip return { false | string | Array < ( string | null | undefined ) > | null | undefined } interface extends Highcharts . PointLabelObject name type { Array < Highcharts . TooltipFormatterContextObject > | undefined } callback Highcharts . TooltipPositionerCallbackFunction param { Highcharts . Tooltip } this param { number } labelWidth param { number } labelHeight param { Highcharts . TooltipPositionerPointObject } point return { Highcharts . PositionObject } interface extends Highcharts . Point name type { boolean } name type { number } name type { number } typedef { "callout" | "circle" | "square" } Highcharts . TooltipShapeValue '' ;