'use strict' ; import type AnimationOptions from '../Animation/AnimationOptions' ; import type BBoxObject from '../Renderer/BBoxObject' ; import type BubbleLegendItem from '../../Series/Bubble/BubbleLegendItem' ; import type Chart from '../Chart/Chart' ; import type ColorAxis from '../Axis/Color/ColorAxis' ; import type CSSObject from '../Renderer/CSSObject' ; import type FontMetricsObject from '../Renderer/FontMetricsObject' ; import type { HTMLDOMElement } from '../Renderer/DOMElementType' ; import type LegendLike from './LegendLike' ; import type LegendOptions from './LegendOptions' ; import type Series from '../Series/Series' ; import type { StatesOptionsKey } from '../Series/StatesOptions' ; import type SVGAttributes from '../Renderer/SVG/SVGAttributes' ; import type SVGElement from '../Renderer/SVG/SVGElement' ; import A from '../Animation/AnimationUtilities.js' ; const { animObject , setAnimation } = A ; import F from '../FormatUtilities.js' ; const { format } = F ; import H from '../Globals.js' ; const { isFirefox , marginNames , win } = H ; import Point from '../Series/Point.js' ; import R from '../Renderer/RendererUtilities.js' ; const { distribute } = R ; import U from '../Utilities.js' ; const { addEvent , createElement , css , defined , discardElement , find , fireEvent , isNumber , merge , pick , relativeLength , stableSort , syncTimeout , wrap } = U ; declare module '../Series/SeriesOptions' { interface SeriesOptions { legendIndex ? : number ; legendType ? : ( 'point' | 'series' ) ; showCheckbox ? : boolean ; showInLegend ? : boolean ; } } interface BoxObject extends R . BoxObject { item : Legend . Item ; } class name param { Highcharts . Chart } chart param { Highcharts . LegendOptions } options class Legend { public constructor ( chart , options ) { this . chart = chart ; this . init ( chart , options ) ; } public allItems : Array < Legend . Item > = [ ] ; public baseline ? : number ; public box : SVGElement = void 0 as any ; public chart : Chart ; public clipHeight ? : number ; public clipRect ? : SVGElement ; public contentGroup : SVGElement = void 0 as any ; public currentPage ? : number ; public display : boolean = false ; public down ? : SVGElement ; public downTracker ? : SVGElement ; public fontMetrics ? : FontMetricsObject ; public fullHeight ? : number ; public group : SVGElement = void 0 as any ; public initialItemY : number = 0 ; public itemHeight : number = 0 ; public itemHiddenStyle ? : CSSObject ; public itemMarginBottom : number = 0 ; public itemMarginTop : number = 0 ; public itemStyle ? : CSSObject ; public itemX : number = 0 ; public itemY : number = 0 ; public lastItemY : number = 0 ; public lastLineHeight : number = 0 ; public legendHeight : number = 0 ; public legendWidth : number = 0 ; public maxItemWidth : number = 0 ; public maxLegendWidth : number = 0 ; public nav ? : SVGElement ; public offsetWidth : number = 0 ; public options : LegendOptions = void 0 as any ; public padding : number = 0 ; public pager ? : SVGElement ; public pages : Array < number > = [ ] ; public proximate : boolean = false ; public scrollGroup : SVGElement = void 0 as any ; public scrollOffset ? : number ; public symbolHeight : number = 0 ; public symbolWidth : number = 0 ; public title ? : SVGElement ; public titleHeight : number = 0 ; public totalItemWidth : number = 0 ; public unchartrender ? : Function ; public up ? : SVGElement ; public upTracker ? : SVGElement ; public widthOption : number = 0 ; private function param { Highcharts . Chart } chart param { Highcharts . LegendOptions } options public init ( chart , options ) { readonly name type { Highcharts . Chart } this . chart = chart ; this . setOptions ( options ) ; if ( options . enabled ) { this . render ( ) ; addEvent ( this . chart , 'endResize' , function ( ) : void { this . legend . positionCheckboxes ( ) ; } ) ; if ( this . proximate ) { this . unchartrender = addEvent ( this . chart , 'render' , function ( ) : void { this . legend . proximatePositions ( ) ; this . legend . positionItems ( ) ; } ) ; } else if ( this . unchartrender ) { this . unchartrender ( ) ; } } } private function param { Highcharts . LegendOptions } options public setOptions ( options ) { const padding = pick ( options . padding , 8 ) as number ; readonly name type { Highcharts . LegendOptions } this . options = options ; if ( ! this . chart . styledMode ) { this . itemStyle = options . itemStyle ; this . itemHiddenStyle = merge ( this . itemStyle , options . itemHiddenStyle ) ; } this . itemMarginTop = options . itemMarginTop || 0 ; this . itemMarginBottom = options . itemMarginBottom || 0 ; this . padding = padding ; this . initialItemY = padding - 5 ; this . symbolWidth = pick ( options . symbolWidth , 16 ) ; this . pages = [ ] ; this . proximate = options . layout === 'proximate' && ! this . chart . inverted ; this . baseline = void 0 ; } sample function param { Highcharts . LegendOptions } options param { boolean } redraw Whether to redraw the chart after the axis is altered. If doing more
     * operations on the chart, it is a good idea to set redraw to false and
     * call  Chart after. Whether to redraw the chart.
     *
     *  emits public update ( options , redraw ? ) { const chart = this . chart ; this . setOptions ( merge ( true , this . options , options ) ) ; this . destroy ( ) ; chart . isDirtyLegend = chart . isDirtyBox = true ; if ( pick ( redraw , true ) ) { chart . redraw ( ) ; } fireEvent ( this , 'afterUpdate' ) ; } private function param { Highcharts . BubbleLegendItem | Highcharts . Point | Highcharts . Series } item param { boolean } visible todo public colorizeItem ( item , visible ? ) { ( item . legendGroup as any ) [ visible ? 'removeClass' : 'addClass' ] ( 'highcharts-legend-item-hidden' ) ; if ( ! this . chart . styledMode ) { const legend = this , options = legend . options , legendItem = item . legendItem , legendLine = item . legendLine , legendSymbol = item . legendSymbol , hiddenColor = ( legend . itemHiddenStyle as any ) . color , textColor = visible ? options . itemStyle . color : hiddenColor , symbolColor = visible ? ( item . color || hiddenColor ) : hiddenColor , markerOptions = item . options && ( item . options as any ) . marker ; let symbolAttr = { fill : symbolColor } ; if ( legendItem ) { legendItem . css ( { fill : textColor , color : textColor } ) ; } if ( legendLine ) { legendLine . attr ( { stroke : symbolColor } ) ; } if ( legendSymbol ) { if ( markerOptions && legendSymbol . isMarker ) { symbolAttr = ( item as any ) . pointAttribs ( ) ; if ( ! visible ) { symbolAttr . stroke = symbolAttr . fill = hiddenColor ; } } legendSymbol . attr ( symbolAttr ) ; } } fireEvent ( this , 'afterColorizeItem' , { item : item , visible : visible } ) ; } private function public positionItems ( ) { this . allItems . forEach ( this . positionItem , this ) ; if ( ! this . chart . isResizing ) { this . positionCheckboxes ( ) ; } } private function param { Highcharts . BubbleLegendItem | Highcharts . Point | Highcharts . Series } item public positionItem ( item ) { const legend = this , options = legend . options , symbolPadding = options . symbolPadding , ltr = ! options . rtl , legendItemPos = item . _legendItemPos , itemX = ( legendItemPos as any ) [ 0 ] , itemY = ( legendItemPos as any ) [ 1 ] , checkbox = item . checkbox , legendGroup = item . legendGroup ; if ( legendGroup && legendGroup . element ) { const attribs = { translateX : ltr ? itemX : legend . legendWidth - itemX - 2 * symbolPadding - 4 , translateY : itemY } ; const complete = ( ) : void => { fireEvent ( this , 'afterPositionItem' , { item } ) ; } ; if ( defined ( legendGroup . translateY ) ) { legendGroup . animate ( attribs , void 0 , complete ) ; } else { legendGroup . attr ( attribs ) ; complete ( ) ; } } if ( checkbox ) { checkbox . x = itemX ; checkbox . y = itemY ; } } private function param { Highcharts . BubbleLegendItem | Highcharts . Point | Highcharts . Series } item public destroyItem ( item ) { const checkbox = item . checkbox ; [ 'legendItem' , 'legendLine' , 'legendSymbol' , 'legendGroup' ] . forEach ( function ( key ) : void { if ( ( item as any ) [ key ] ) { ( item as any ) [ key ] = ( item as any ) [ key ] . destroy ( ) ; } } ) ; if ( checkbox ) { discardElement ( ( item as any ) . checkbox ) ; } } private function public destroy ( ) { private param { string } key function destroyItems ( this , key ) { if ( ( this as any ) [ key ] ) { ( this as any ) [ key ] = ( this as any ) [ key ] . destroy ( ) ; } } this . getAllItems ( ) . forEach ( function ( item ) : void { [ 'legendItem' , 'legendGroup' ] . forEach ( destroyItems , item ) ; } ) ; [ 'clipRect' , 'up' , 'down' , 'pager' , 'nav' , 'box' , 'title' , 'group' ] . forEach ( destroyItems , this ) ; this . display = null as any ; } private function public positionCheckboxes ( ) { const alignAttr = this . group && this . group . alignAttr , clipHeight = this . clipHeight || this . legendHeight , titleHeight = this . titleHeight ; let translateY ; if ( alignAttr ) { translateY = alignAttr . translateY ; this . allItems . forEach ( function ( item ) : void { const checkbox = item . checkbox ; let top ; if ( checkbox ) { top = translateY + titleHeight + checkbox . y + ( this . scrollOffset || 0 ) + 3 ; css ( checkbox , { left : ( alignAttr . translateX + item . checkboxOffset + checkbox . x - 20 ) + 'px' , top : top + 'px' , display : this . proximate || ( top > translateY - 6 && top < translateY + clipHeight - 6 ) ? '' : 'none' } ) ; } } , this ) ; } } private function public renderTitle ( ) { const options = this . options , padding = this . padding , titleOptions = options . title ; let bBox , titleHeight = 0 ; if ( titleOptions . text ) { if ( ! this . title ) { readonly name type { Highcharts . SVGElement } this . title = this . chart . renderer . label ( titleOptions . text , padding - 3 , padding - 4 , void 0 , void 0 , void 0 , options . useHTML , void 0 , 'legend-title' ) . attr ( { zIndex : 1 } ) ; if ( ! this . chart . styledMode ) { this . title . css ( titleOptions . style ) ; } this . title . add ( this . group ) ; } if ( ! titleOptions . width ) { this . title . css ( { width : this . maxLegendWidth + 'px' } ) ; } bBox = this . title . getBBox ( ) ; titleHeight = bBox . height ; this . offsetWidth = bBox . width ; this . contentGroup . attr ( { translateY : titleHeight } ) ; } this . titleHeight = titleHeight ; } function param { Highcharts . Point | Highcharts . Series } item public setText ( item ) { const options = this . options ; ( item . legendItem as any ) . attr ( { text : options . labelFormat ? format ( options . labelFormat , item , this . chart ) : options . labelFormatter . call ( item ) } ) ; } private function param { Highcharts . BubbleLegendItem | Highcharts . Point | Highcharts . Series } item public renderItem ( item ) { const legend = this , 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 as any ) . series , series = ! isSeries && ( item as any ) . series . drawLegendSymbol ? ( item as any ) . series : item , seriesOptions = series . options , showCheckbox = legend . createCheckboxForItem && seriesOptions && seriesOptions . showCheckbox , useHTML = options . useHTML , itemClassName = item . options . className ; let li = item . legendItem , itemExtraWidth = symbolWidth + symbolPadding + itemDistance + ( showCheckbox ? 20 : 0 ) ; if ( ! li ) { item . legendGroup = renderer . g ( 'legend-item' ) . addClass ( 'highcharts-' + series . type + '-series ' + 'highcharts-color-' + ( item as any ) . colorIndex + ( itemClassName ? ' ' + itemClassName : '' ) + ( isSeries ? ' highcharts-series-' + ( item as any ) . index : '' ) ) . attr ( { zIndex : 1 } ) . add ( legend . scrollGroup ) ; item . legendItem = li = renderer . text ( '' , ltr ? symbolWidth + symbolPadding : - symbolPadding , legend . baseline || 0 , useHTML ) ; if ( ! chart . styledMode ) { li . css ( merge ( item . visible ? itemStyle : itemHiddenStyle ) ) ; } li . attr ( { align : ltr ? 'left' : 'right' , zIndex : 2 } ) . add ( item . legendGroup ) ; if ( ! legend . baseline ) { legend . fontMetrics = renderer . fontMetrics ( chart . styledMode ? 12 : ( itemStyle as any ) . fontSize , li ) ; legend . baseline = legend . fontMetrics . f + 3 + legend . itemMarginTop ; li . attr ( 'y' , legend . baseline ) ; legend . symbolHeight = options . symbolHeight || legend . fontMetrics . f ; if ( options . squareSymbol ) { legend . symbolWidth = pick ( options . symbolWidth , Math . max ( legend . symbolHeight , 16 ) ) ; itemExtraWidth = legend . symbolWidth + symbolPadding + itemDistance + ( showCheckbox ? 20 : 0 ) ; if ( ltr ) { li . attr ( 'x' , legend . symbolWidth + symbolPadding ) ; } } } series . drawLegendSymbol ( legend , item ) ; if ( legend . setItemEvents ) { legend . setItemEvents ( item , li , useHTML ) ; } } if ( showCheckbox && ! item . checkbox && legend . createCheckboxForItem ) { legend . createCheckboxForItem ( item ) ; } legend . colorizeItem ( item , item . visible ) ; if ( chart . styledMode || ! ( itemStyle as any ) . width ) { li . css ( { width : ( ( options . itemWidth || legend . widthOption || chart . spacingBox . width ) - itemExtraWidth ) + 'px' } ) ; } legend . setText ( item as Legend . Item ) ; const bBox = li . getBBox ( ) ; const fontMetricsH = ( legend . fontMetrics && legend . fontMetrics . h ) || 0 ; item . itemWidth = item . checkboxOffset = options . itemWidth || item . legendItemWidth || bBox . width + itemExtraWidth ; legend . maxItemWidth = Math . max ( legend . maxItemWidth , ( item . itemWidth as any ) ) ; legend . totalItemWidth += item . itemWidth as any ; legend . itemHeight = item . itemHeight = Math . round ( item . legendItemHeight || ( bBox . height > fontMetricsH * 1.5 ? bBox . height : fontMetricsH ) ) ; } private function param { Highcharts . BubbleLegendItem | Highcharts . Point | Highcharts . Series } item public layoutItem ( item ) { 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 && this . totalItemWidth > maxLegendWidth ) ? this . maxItemWidth : item . itemWidth ; if ( horizontal && this . itemX - padding + ( itemWidth as any ) > maxLegendWidth ) { this . itemX = padding ; if ( this . lastLineHeight ) { this . itemY += ( itemMarginTop + this . lastLineHeight + itemMarginBottom ) ; } this . lastLineHeight = 0 ; } this . lastItemY = itemMarginTop + this . itemY + itemMarginBottom ; this . lastLineHeight = Math . max ( itemHeight as any , this . lastLineHeight ) ; item . _legendItemPos = [ this . itemX , this . itemY ] ; if ( horizontal ) { this . itemX += ( itemWidth as any ) ; } else { this . itemY += itemMarginTop + ( itemHeight as any ) + itemMarginBottom ; this . lastLineHeight = itemHeight as any ; } this . offsetWidth = this . widthOption || Math . max ( ( horizontal ? this . itemX - padding - ( item . checkbox ? 0 : itemDistance ) : itemWidth as any ) + padding , this . offsetWidth ) ; } private function return { Array < ( Highcharts . BubbleLegendItem | Highcharts . Point | Highcharts . Series ) > } emits public getAllItems ( ) { let allItems = [ ] ; this . chart . series . forEach ( function ( series ) : void { const seriesOptions = series && series . options ; if ( series && pick ( seriesOptions . showInLegend , ! defined ( seriesOptions . linkedTo ) ? void 0 : false , true ) ) { allItems = allItems . concat ( series . legendItems as any || ( seriesOptions . legendType === 'point' ? series . data : series ) ) ; } } ) ; fireEvent ( this , 'afterGetAllItems' , { allItems } ) ; return allItems ; } private function return { string } public getAlignment ( ) { const options = this . options ; if ( this . proximate ) { return options . align . charAt ( 0 ) + 'tv' ; } return options . floating ? '' : ( options . align . charAt ( 0 ) + options . verticalAlign . charAt ( 0 ) + options . layout . charAt ( 0 ) ) ; } private function param { Array < number > } margin param { Array < number > } spacing public adjustMargins ( margin , spacing ) { const chart = this . chart , options = this . options , alignment = this . getAlignment ( ) ; if ( alignment ) { ( [ /(lth|ct|rth)/ , /(rtv|rm|rbv)/ , /(rbh|cb|lbh)/ , /(lbv|lm|ltv)/ ] ) . forEach ( function ( alignments , side ) : void { if ( alignments . test ( alignment ) && ! defined ( margin [ side ] ) ) { ( chart as any ) [ marginNames [ side ] ] = Math . max ( ( chart as any ) [ marginNames [ side ] ] , ( chart . legend [ ( side + 1 ) % 2 ? 'legendHeight' : 'legendWidth' ] + [ 1 , - 1 , - 1 , 1 ] [ side ] * ( options [ ( side % 2 ) ? 'x' : 'y' ] as any ) + pick ( options . margin as any , 12 ) + spacing [ side ] + ( chart . titleOffset [ side ] || 0 ) ) ) ; } } ) ; } } private function public proximatePositions ( ) { const chart = this . chart , boxes = [ ] , alignLeft = this . options . align === 'left' ; this . allItems . forEach ( function ( item ) : void { let lastPoint , height , useFirstPoint = alignLeft , target , top ; if ( ( item as any ) . yAxis ) { if ( ( item as any ) . xAxis . options . reversed ) { useFirstPoint = ! useFirstPoint ; } if ( ( item as any ) . points ) { lastPoint = find ( useFirstPoint ? ( item as any ) . points : ( item as any ) . points . slice ( 0 ) . reverse ( ) , function ( item ) : boolean { return isNumber ( item . plotY ) ; } ) ; } height = this . itemMarginTop + ( item . legendItem as any ) . getBBox ( ) . height + this . itemMarginBottom ; top = ( item as any ) . yAxis . top - chart . plotTop ; if ( item . visible ) { target = lastPoint ? lastPoint . plotY : ( item as any ) . yAxis . height ; target += top - 0.3 * height ; } else { target = top + ( item as any ) . yAxis . height ; } boxes . push ( { target : target , size : height , item } ) ; } } , this ) ; distribute ( boxes , chart . plotHeight ) . forEach ( ( box ) : void => { if ( box . item . _legendItemPos ) { box . item . _legendItemPos [ 1 ] = chart . plotTop - chart . spacing [ 0 ] + box . pos ; } } ) ; } private function public render ( ) { const legend = this , chart = legend . chart , renderer = chart . renderer , options = legend . options , padding = legend . padding , allItems = legend . getAllItems ( ) ; let display , legendWidth , legendHeight , legendGroup = legend . group , allowedWidth , box = legend . box ; legend . itemX = padding ; legend . itemY = legend . initialItemY ; legend . offsetWidth = 0 ; legend . lastItemY = 0 ; legend . widthOption = relativeLength ( options . width as any , chart . spacingBox . width - padding ) ; allowedWidth = chart . spacingBox . width - 2 * padding - options . x ; if ( [ 'rm' , 'lm' ] . indexOf ( legend . getAlignment ( ) . substring ( 0 , 2 ) ) > - 1 ) { allowedWidth /= 2 ; } legend . maxLegendWidth = legend . widthOption || allowedWidth ; if ( ! legendGroup ) { readonly name type { Highcharts . SVGElement } legend . group = legendGroup = renderer . g ( 'legend' ) . addClass ( options . className || '' ) . attr ( { zIndex : 7 } ) . add ( ) ; legend . contentGroup = renderer . g ( ) . attr ( { zIndex : 1 } ) . add ( legendGroup ) ; legend . scrollGroup = renderer . g ( ) . add ( legend . contentGroup ) ; } legend . renderTitle ( ) ; stableSort ( allItems , ( a , b ) : number => ( ( a . options && a . options . legendIndex ) || 0 ) - ( ( b . options && b . options . legendIndex ) || 0 ) ) ; if ( options . reversed ) { allItems . reverse ( ) ; } readonly name type { Array < ( Highcharts . Point | Highcharts . Series ) > } legend . allItems = allItems ; legend . display = display = ! ! allItems . length ; legend . lastLineHeight = 0 ; legend . maxItemWidth = 0 ; legend . totalItemWidth = 0 ; legend . itemHeight = 0 ; allItems . forEach ( legend . renderItem , legend ) ; allItems . forEach ( legend . layoutItem , legend ) ; legendWidth = ( legend . widthOption || legend . offsetWidth ) + padding ; legendHeight = legend . lastItemY + legend . lastLineHeight + legend . titleHeight ; legendHeight = legend . handleOverflow ( legendHeight ) ; legendHeight += padding ; if ( ! box ) { readonly name type { Highcharts . SVGElement } legend . box = box = renderer . rect ( ) . addClass ( 'highcharts-legend-box' ) . attr ( { r : options . borderRadius } ) . add ( legendGroup ) ; } if ( ! chart . styledMode ) { box . attr ( { stroke : options . borderColor , 'stroke-width' : options . borderWidth || 0 , fill : options . backgroundColor || 'none' } ) . shadow ( options . shadow ) ; } if ( legendWidth > 0 && legendHeight > 0 ) { box [ box . placed ? 'animate' : 'attr' ] ( box . crisp . call ( { } , { x : 0 , y : 0 , width : legendWidth , height : legendHeight } , box . strokeWidth ( ) ) ) ; } box [ display ? 'show' : 'hide' ] ( ) ; if ( chart . styledMode && legendGroup . getStyle ( 'display' ) === 'none' ) { legendWidth = legendHeight = 0 ; } legend . legendWidth = legendWidth ; legend . legendHeight = legendHeight ; if ( display ) { legend . align ( ) ; } if ( ! this . proximate ) { this . positionItems ( ) ; } fireEvent ( this , 'afterRender' ) ; } private function param { Highcharts . BBoxObject } alignTo public align ( alignTo = this . chart . spacingBox ) { const chart = this . chart , options = this . options ; let y = alignTo . y ; if ( /(lth|ct|rth)/ . test ( this . getAlignment ( ) ) && chart . titleOffset [ 0 ] > 0 ) { y += chart . titleOffset [ 0 ] ; } else if ( /(lbh|cb|rbh)/ . test ( this . getAlignment ( ) ) && chart . titleOffset [ 2 ] > 0 ) { y -= chart . titleOffset [ 2 ] ; } if ( y !== alignTo . y ) { alignTo = merge ( alignTo , { y } ) ; } this . group . placed = chart . hasRendered ; this . group . align ( merge ( options , { width : this . legendWidth , height : this . legendHeight , verticalAlign : this . proximate ? 'top' : options . verticalAlign } ) , true , alignTo ) ; } private function public handleOverflow ( legendHeight ) { 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 ? ) : void { if ( typeof height === 'number' ) { ( clipRect as any ) . attr ( { height : height } ) ; } else if ( clipRect ) { legend . clipRect = clipRect . destroy ( ) ; legend . contentGroup . clip ( ) ; } if ( legend . contentGroup . div ) { legend . contentGroup . div . style . clip = height ? 'rect(' + padding + 'px,9999px,' + ( padding + height ) + 'px,0)' : 'auto' ; } } , addTracker = function ( key ) : SVGElement { ( legend as any ) [ key ] = renderer . circle ( 0 , 0 , arrowSize * 1.3 ) . translate ( arrowSize / 2 , arrowSize / 2 ) . add ( nav ) ; if ( ! chart . styledMode ) { ( legend as any ) [ key ] . attr ( 'fill' , 'rgba(0,0,0,0.0001)' ) ; } return ( legend as any ) [ key ] ; } ; let clipHeight , lastY , spaceHeight = ( chart . spacingBox . height + ( alignTop ? - optionsY : optionsY ) - padding ) , nav = this . nav , clipRect = this . clipRect ; if ( options . layout === 'horizontal' && options . verticalAlign !== 'middle' && ! options . floating ) { spaceHeight /= 2 ; } if ( maxHeight ) { spaceHeight = Math . min ( spaceHeight , maxHeight ) ; } pages . length = 0 ; if ( legendHeight && spaceHeight > 0 && legendHeight > spaceHeight && navOptions . enabled !== false ) { this . clipHeight = clipHeight = Math . max ( spaceHeight - 20 - this . titleHeight - padding , 0 ) ; this . currentPage = pick ( this . currentPage , 1 ) ; this . fullHeight = legendHeight ; allItems . forEach ( function ( item , i ) : void { const y = ( item . _legendItemPos as any ) [ 1 ] , h = Math . round ( ( item . legendItem as any ) . getBBox ( ) . height ) ; let len = pages . length ; if ( ! len || ( y - pages [ len - 1 ] > clipHeight && ( lastY || y ) !== pages [ len - 1 ] ) ) { pages . push ( lastY || y ) ; len ++ ; } item . pageIx = len - 1 ; if ( lastY ) { allItems [ i - 1 ] . pageIx = len - 1 ; } if ( i === allItems . length - 1 && y + h - pages [ len - 1 ] > clipHeight && h <= clipHeight ) { pages . push ( y ) ; item . pageIx = len ; } if ( y !== lastY ) { lastY = y ; } } ) ; if ( ! clipRect ) { clipRect = legend . clipRect = renderer . clipRect ( 0 , padding , 9999 , 0 ) ; legend . contentGroup . clip ( clipRect ) ; } clipToHeight ( clipHeight ) ; if ( ! nav ) { this . nav = nav = renderer . g ( ) . attr ( { zIndex : 1 } ) . add ( this . group ) ; this . up = renderer . symbol ( 'triangle' , 0 , 0 , arrowSize , arrowSize ) . add ( nav ) ; addTracker ( 'upTracker' ) . on ( 'click' , function ( ) : void { legend . scroll ( - 1 , animation ) ; } ) ; this . pager = renderer . text ( '' , 15 , 10 ) . addClass ( 'highcharts-legend-navigation' ) ; if ( ! chart . styledMode && navOptions . style ) { this . pager . css ( navOptions . style ) ; } this . pager . add ( nav ) ; this . down = renderer . symbol ( 'triangle-down' , 0 , 0 , arrowSize , arrowSize ) . add ( nav ) ; addTracker ( 'downTracker' ) . on ( 'click' , function ( ) : void { legend . scroll ( 1 , animation ) ; } ) ; } legend . scroll ( 0 ) ; legendHeight = spaceHeight ; } else if ( nav ) { clipToHeight ( ) ; this . nav = nav . destroy ( ) ; this . scrollGroup . attr ( { translateY : 1 } ) ; this . clipHeight = 0 ; } return legendHeight ; } private function param { number } scrollBy param { boolean | Partial < Highcharts . AnimationOptionsObject > } animation public scroll ( scrollBy , animation ? ) { const chart = this . chart , pages = this . pages , pageCount = pages . length , clipHeight = this . clipHeight , navOptions = this . options . navigation , pager = this . pager , padding = this . padding ; let currentPage = ( this . currentPage as any ) + scrollBy ; if ( currentPage > pageCount ) { currentPage = pageCount ; } if ( currentPage > 0 ) { if ( typeof animation !== 'undefined' ) { setAnimation ( animation , chart ) ; } ( this . nav as any ) . attr ( { translateX : padding , translateY : ( clipHeight as any ) + this . padding + 7 + this . titleHeight , visibility : 'visible' } ) ; [ this . up , this . upTracker ] . forEach ( function ( elem ) : void { ( elem as any ) . attr ( { 'class' : currentPage === 1 ? 'highcharts-legend-nav-inactive' : 'highcharts-legend-nav-active' } ) ; } ) ; ( pager as any ) . attr ( { text : currentPage + '/' + pageCount } ) ; [ this . down , this . downTracker ] . forEach ( function ( elem ) : void { ( elem as any ) . attr ( { x : 18 + ( this . pager as any ) . getBBox ( ) . width , 'class' : currentPage === pageCount ? 'highcharts-legend-nav-inactive' : 'highcharts-legend-nav-active' } ) ; } , this ) ; if ( ! chart . styledMode ) { ( this . up as any ) . attr ( { fill : currentPage === 1 ? ( navOptions as any ) . inactiveColor : ( navOptions as any ) . activeColor } ) ; ( this . upTracker as any ) . css ( { cursor : currentPage === 1 ? 'default' : 'pointer' } ) ; ( this . down as any ) . attr ( { fill : currentPage === pageCount ? ( navOptions as any ) . inactiveColor : ( navOptions as any ) . activeColor } ) ; ( this . downTracker as any ) . css ( { cursor : currentPage === pageCount ? 'default' : 'pointer' } ) ; } this . scrollOffset = - pages [ currentPage - 1 ] + this . initialItemY ; this . scrollGroup . animate ( { translateY : this . scrollOffset } ) ; this . currentPage = currentPage ; this . positionCheckboxes ( ) ; const animOptions = animObject ( pick ( animation , chart . renderer . globalAnimation , true ) ) ; syncTimeout ( ( ) : void => { fireEvent ( this , 'afterScroll' , { currentPage } ) ; } , animOptions . duration ) ; } } private function param { Highcharts . BubbleLegendItem | Point | Highcharts . Series } item param { Highcharts . SVGElement } legendItem param { boolean } useHTML emits emits public setItemEvents ( item , legendItem , useHTML ? ) { const legend = this , boxWrapper = legend . chart . renderer . boxWrapper , isPoint = item instanceof Point , activeClass = 'highcharts-legend-' + ( isPoint ? 'point' : 'series' ) + '-active' , styledMode = legend . chart . styledMode , legendItems = useHTML ? [ legendItem , item . legendSymbol ] : [ item . legendGroup ] ; const setOtherItemsState = ( state ) : void => { legend . allItems . forEach ( ( otherItem ) : void => { if ( item !== otherItem ) { [ otherItem ] . concat ( ( otherItem as Series ) . linkedSeries || [ ] ) . forEach ( ( otherItem ) : void => { otherItem . setState ( state , ! isPoint ) ; } ) ; } } ) ; } ; legendItems . forEach ( function ( element ) : void { if ( element ) { element . on ( 'mouseover' , function ( ) : void { if ( item . visible ) { setOtherItemsState ( 'inactive' ) ; } item . setState ( 'hover' ) ; if ( item . visible ) { boxWrapper . addClass ( activeClass ) ; } if ( ! styledMode ) { legendItem . css ( legend . options . itemHoverStyle ) ; } } ) . on ( 'mouseout' , function ( ) : void { if ( ! legend . chart . styledMode ) { legendItem . css ( merge ( item . visible ? legend . itemStyle : legend . itemHiddenStyle ) ) ; } setOtherItemsState ( '' ) ; boxWrapper . removeClass ( activeClass ) ; item . setState ( ) ; } ) . on ( 'click' , function ( event ) : void { const strLegendItemClick = 'legendItemClick' , fnLegendItemClick = function ( ) : void { if ( ( item as any ) . setVisible ) { ( item as any ) . setVisible ( ) ; } setOtherItemsState ( item . visible ? 'inactive' : '' ) ; } ; boxWrapper . removeClass ( activeClass ) ; event = { browserEvent : event } as any ; if ( ( item as any ) . firePointEvent ) { ( item as any ) . firePointEvent ( strLegendItemClick , event , fnLegendItemClick ) ; } else { fireEvent ( item , strLegendItemClick , event , fnLegendItemClick ) ; } } ) ; } } ) ; } private function param { Highcharts . BubbleLegendItem | Point | Highcharts . Series } item emits public createCheckboxForItem ( item ) { const legend = this ; item . checkbox = createElement ( 'input' , { type : 'checkbox' , className : 'highcharts-legend-checkbox' , checked : item . selected , defaultChecked : item . selected } , legend . options . itemCheckboxStyle , legend . chart . container ) as any ; addEvent ( item . checkbox , 'click' , function ( event ) : void { const target = event . target as Legend . CheckBoxElement ; fireEvent ( ( item as any ) . series || item , 'checkboxClick' , { checked : target . checked , item : item } , function ( ) : void { ( item as any ) . select ( ) ; } ) ; } ) ; } } interface Legend extends LegendLike { } if ( /Trident\/7\.0/ . test ( win . navigator && win . navigator . userAgent ) || isFirefox ) { wrap ( Legend . prototype , 'positionItem' , function ( this , proceed , item ) : void { const legend = this , runPositionItem = function ( ) : void { if ( item . _legendItemPos ) { proceed . call ( legend , item ) ; } } ; runPositionItem ( ) ; if ( ! legend . bubbleLegend ) { setTimeout ( runPositionItem ) ; } } ) ; } namespace Legend { export interface CheckBoxElement extends HTMLDOMElement { checked ? : boolean ; x : number ; y : number ; } export type Item = ( BubbleLegendItem | Series | Point ) ; } export default Legend ; callback Highcharts . PointLegendItemClickCallbackFunction param { Highcharts . Point } this param { Highcharts . PointLegendItemClickEventObject } event interface name type { Highcharts . PointerEvent } name type { Function } name type { Highcharts . Point } name type { "legendItemClick" } callback Highcharts . SeriesLegendItemClickCallbackFunction param { Highcharts . Series } this param { Highcharts . SeriesLegendItemClickEventObject } event interface name type { Highcharts . PointerEvent } name type { Function } name type { Highcharts . Series } name type { "legendItemClick" } ( '' ) ;