'use strict' ; import type AnimationOptions from '../../Animation/AnimationOptions' ; import type BBoxObject from '../BBoxObject' ; import type ColorString from '../../Color/ColorString' ; import type CSSObject from '../CSSObject' ; import type { DOMElementType , HTMLDOMElement , SVGDOMElement } from '../DOMElementType' ; import type EventCallback from '../../../Core/EventCallback' ; import type FontMetricsObject from '../FontMetricsObject' ; import type PositionObject from '../PositionObject' ; import type SVGAttributes from './SVGAttributes' ; import type SVGPath from './SVGPath' ; import type SVGRendererLike from './SVGRendererLike' ; import type SymbolOptions from './SymbolOptions' ; import type { SymbolKey } from './SymbolType' ; import AST from '../HTML/AST.js' ; import Color from '../../Color/Color.js' ; import H from '../../Globals.js' ; const { charts , deg2rad , doc , isFirefox , isMS , isWebKit , noop , SVG_NS , symbolSizes , win } = H ; import { Palette } from '../../Color/Palettes.js' ; import RendererRegistry from '../RendererRegistry.js' ; import SVGElement from './SVGElement.js' ; import SVGLabel from './SVGLabel.js' ; import Symbols from './Symbols.js' ; import TextBuilder from './TextBuilder.js' ; import U from '../../Utilities.js' ; const { addEvent , attr , createElement , css , defined , destroyObjectProperties , extend , isArray , isNumber , isObject , isString , merge , pick , pInt , uniqueKey } = U ; let hasInternalReferenceBug ; /**
 * Allows direct access to the Highcharts rendering layer in order to draw
 * primitive shapes like circles, rectangles, paths or text directly on a chart,
 * or independent from any chart. The SVGRenderer represents a wrapper object
 * for SVG in modern browsers. Through the VMLRenderer, part of the `oldie.js`
 * module, it also brings vector graphics to IE <= 8.
 *
 * An existing chart's renderer can be accessed through  Chart . renderer .
 * The renderer can also be used completely decoupled from a chart.
 *
 *  sample sample example class name param { Highcharts . HTMLDOMElement } container param { number } width param { number } height param { Highcharts . CSSObject } style param { boolean } forExport param { boolean } allowHTML param { boolean } styledMode class SVGRenderer implements SVGRendererLike { public constructor ( container , width , height , style ? , forExport ? , allowHTML ? , styledMode ? ) { this . init ( container , width , height , style , forExport , allowHTML , styledMode ) ; } public alignedObjects : Array < SVGElement > = void 0 as any ; public allowHTML ? : boolean ; name type { Highcharts . SVGDOMElement } public box : globalThis . SVGElement = void 0 as any ; name type { Highcharts . SVGElement } public boxWrapper : SVGElement = void 0 as any ; public cache : Record < string , BBoxObject > = void 0 as any ; public cacheKeys : Array < string > = void 0 as any ; public chartIndex : number = void 0 as any ; name type { Highcharts . SVGElement } public defs : SVGElement = void 0 as any ; public forExport ? : boolean ; public globalAnimation : ( boolean | Partial < AnimationOptions > ) = void 0 as any ; public gradients : Record < string , SVGElement > = void 0 as any ; public height : number = void 0 as any ; public imgCount : number = void 0 as any ; public isSVG : boolean = void 0 as any ; public style : CSSObject = void 0 as any ; public styledMode ? : boolean ; public unSubPixelFix ? : Function ; private name type { string } public url : string = void 0 as any ; public width : number = void 0 as any ; function param { Highcharts . HTMLDOMElement } container param { number } width param { number } height param { Highcharts . CSSObject } style param { boolean } forExport param { boolean } allowHTML param { boolean } styledMode public init ( container , width , height , style ? , forExport ? , allowHTML ? , styledMode ? ) { const renderer = this , boxWrapper = renderer . createElement ( 'svg' ) . attr ( { version : '1.1' , 'class' : 'highcharts-root' } ) as any , element = boxWrapper . element ; if ( ! styledMode ) { boxWrapper . css ( this . getStyle ( style as any ) ) ; } container . appendChild ( element ) ; attr ( container , 'dir' , 'ltr' ) ; if ( container . innerHTML . indexOf ( 'xmlns' ) === - 1 ) { attr ( element , 'xmlns' , this . SVG_NS ) ; } renderer . isSVG = true ; this . box = element as any ; this . boxWrapper = boxWrapper ; renderer . alignedObjects = [ ] ; this . url = this . getReferenceURL ( ) ; const desc = this . createElement ( 'desc' ) . add ( ) ; desc . element . appendChild ( doc . createTextNode ( 'Created with @product.name@ @product.version@' ) ) ; renderer . defs = this . createElement ( 'defs' ) . add ( ) ; renderer . allowHTML = allowHTML ; renderer . forExport = forExport ; renderer . styledMode = styledMode ; renderer . gradients = { } ; renderer . cache = { } ; renderer . cacheKeys = [ ] ; renderer . imgCount = 0 ; renderer . setSize ( width , height , false ) ; let subPixelFix , rect ; if ( isFirefox && container . getBoundingClientRect ) { subPixelFix = function ( ) : void { css ( container , { left : 0 , top : 0 } ) ; rect = container . getBoundingClientRect ( ) ; css ( container , { left : ( Math . ceil ( rect . left ) - rect . left ) + 'px' , top : ( Math . ceil ( rect . top ) - rect . top ) + 'px' } ) ; } ; subPixelFix ( ) ; renderer . unSubPixelFix = addEvent ( win , 'resize' , subPixelFix ) ; } } function param { Highcharts . ASTNode } def return { Highcharts . SVGElement } public definition ( def ) { const ast = new AST ( [ def ] ) ; return ast . addToDOM ( this . defs . element ) as unknown as SVGElement ; } todo return { string } public getReferenceURL ( ) { if ( ( isFirefox || isWebKit ) && doc . getElementsByTagName ( 'base' ) . length ) { if ( ! defined ( hasInternalReferenceBug ) ) { const id = uniqueKey ( ) ; const ast = new AST ( [ { tagName : 'svg' , attributes : { width : 8 , height : 8 } , children : [ { tagName : 'defs' , children : [ { tagName : 'clipPath' , attributes : { id } , children : [ { tagName : 'rect' , attributes : { width : 4 , height : 4 } } ] } ] } , { tagName : 'rect' , attributes : { id : 'hitme' , width : 8 , height : 8 , 'clip-path' : `url(#${ id })` , fill : 'rgba(0,0,0,0.001)' } } ] } ] ) ; const svg = ast . addToDOM ( doc . body ) ; css ( svg , { position : 'fixed' , top : 0 , left : 0 , zIndex : 9e5 } ) ; const hitElement = doc . elementFromPoint ( 6 , 6 ) ; hasInternalReferenceBug = ( hitElement && hitElement . id ) === 'hitme' ; doc . body . removeChild ( svg ) ; } if ( hasInternalReferenceBug ) { return win . location . href . split ( '#' ) [ 0 ] . replace ( /<[^>]*>/g , '' ) . replace ( /([\('\)])/g , '\\$1' ) . replace ( / /g , '%20' ) ; } } return '' ; } private function param { Highcharts . CSSObject } style return { Highcharts . CSSObject } public getStyle ( style ) { this . style = extend < CSSObject > ( { fontFamily : '"Lucida Grande", "Lucida Sans Unicode", ' + 'Arial, Helvetica, sans-serif' , fontSize : '12px' } , style ) ; return this . style ; } function param { Highcharts . CSSObject } style public setStyle ( style ) { this . boxWrapper . css ( this . getStyle ( style ) ) ; } function return { boolean } public isHidden ( ) { return ! this . boxWrapper . getBBox ( ) . width ; } function return { null } public destroy ( ) { const renderer = this , rendererDefs = renderer . defs ; renderer . box = null as any ; renderer . boxWrapper = renderer . boxWrapper . destroy ( ) as any ; destroyObjectProperties ( renderer . gradients || { } ) ; renderer . gradients = null as any ; if ( rendererDefs ) { renderer . defs = rendererDefs . destroy ( ) as any ; } if ( renderer . unSubPixelFix ) { renderer . unSubPixelFix ( ) ; } renderer . alignedObjects = null as any ; return null ; } /**
     * Create a wrapper for an SVG element. Serves as a factory for
     *  SVGElement , but this function is itself mostly called from
     * primitive factories like  SVGRenderer ,  {@link
     * SVGRenderer#rect} or  SVGRenderer .
     *
     *  function param { string } nodeName return { Highcharts . SVGElement } public createElement ( nodeName ) { const wrapper = new this . Element ( ) ; wrapper . init ( this as any , nodeName ) ; return wrapper ; } /**
     * Get converted radial gradient attributes according to the radial
     * reference. Used internally from the  SVGElement * function.
     *
     *  private function public getRadialAttr ( radialReference , gradAttr ) { return { cx : ( radialReference [ 0 ] - radialReference [ 2 ] / 2 ) + ( gradAttr . cx || 0 ) * radialReference [ 2 ] , cy : ( radialReference [ 1 ] - radialReference [ 2 ] / 2 ) + ( gradAttr . cy || 0 ) * radialReference [ 2 ] , r : ( gradAttr . r || 0 ) * radialReference [ 2 ] } ; } private function param { Highcharts . SVGElement } wrapper public buildText ( wrapper ) { new TextBuilder ( wrapper ) . buildSVG ( ) ; } function param { Highcharts . ColorString } rgba return { Highcharts . ColorString } public getContrast ( rgba ) { rgba = Color . parse ( rgba ) . rgba as any ; ( rgba [ 0 ] as any ) *= 1 ; ( rgba [ 1 ] as any ) *= 1.2 ; ( rgba [ 2 ] as any ) *= 0.5 ; return ( rgba [ 0 ] as any ) + ( rgba [ 1 ] as any ) + ( rgba [ 2 ] as any ) > 1.8 * 255 ? '#000000' : '#FFFFFF' ; } function param { string } text param { number } x param { number } y param { Highcharts . EventCallbackFunction < Highcharts . SVGElement > } callback param { Highcharts . SVGAttributes } theme param { Highcharts . SVGAttributes } hoverState param { Highcharts . SVGAttributes } pressedState param { Highcharts . SVGAttributes } disabledState param { Highcharts . SymbolKeyValue } shape param { boolean } useHTML return { Highcharts . SVGElement } public button ( text , x , y , callback , theme ? , hoverState ? , pressedState ? , disabledState ? , shape ? , useHTML ? ) { const label = this . label ( text , x , y , shape , void 0 , void 0 , useHTML , void 0 , 'button' ) , styledMode = this . styledMode ; let curState = 0 , normalState = theme ? merge ( theme ) : { } ; const normalStyle = merge ( { color : Palette . neutralColor80 , cursor : 'pointer' , fontWeight : 'normal' } , normalState . style ) ; delete normalState . style ; normalState = AST . filterUserAttributes ( normalState ) ; label . attr ( merge ( { padding : 8 , r : 2 } , normalState ) ) ; let hoverStyle , pressedStyle , disabledStyle ; if ( ! styledMode ) { normalState = merge ( { fill : Palette . neutralColor3 , stroke : Palette . neutralColor20 , 'stroke-width' : 1 } , normalState ) ; hoverState = merge ( normalState , { fill : Palette . neutralColor10 } , AST . filterUserAttributes ( hoverState || { } ) ) ; hoverStyle = hoverState . style ; delete hoverState . style ; pressedState = merge ( normalState , { fill : Palette . highlightColor10 , style : { color : Palette . neutralColor100 , fontWeight : 'bold' } } , AST . filterUserAttributes ( pressedState || { } ) ) ; pressedStyle = pressedState . style ; delete pressedState . style ; disabledState = merge ( normalState , { style : { color : Palette . neutralColor20 } } , AST . filterUserAttributes ( disabledState || { } ) ) ; disabledStyle = disabledState . style ; delete disabledState . style ; } addEvent ( label . element , isMS ? 'mouseover' : 'mouseenter' , function ( ) : void { if ( curState !== 3 ) { label . setState ( 1 ) ; } } ) ; addEvent ( label . element , isMS ? 'mouseout' : 'mouseleave' , function ( ) : void { if ( curState !== 3 ) { label . setState ( curState ) ; } } ) ; label . setState = function ( state ) : void { if ( state !== 1 ) { label . state = curState = state ; } label . removeClass ( /highcharts-button-(normal|hover|pressed|disabled)/ ) . addClass ( 'highcharts-button-' + [ 'normal' , 'hover' , 'pressed' , 'disabled' ] [ state || 0 ] ) ; if ( ! styledMode ) { label . attr ( [ normalState , hoverState , pressedState , disabledState ] [ state || 0 ] ) ; const css = [ normalStyle , hoverStyle , pressedStyle , disabledStyle ] [ state || 0 ] ; if ( isObject ( css ) ) { label . css ( css ) ; } } } ; if ( ! styledMode ) { ( label . attr ( normalState ) as any ) . css ( extend ( { cursor : 'default' } , normalStyle ) ) ; } return label . on ( 'touchstart' , ( e ) : void => e . stopPropagation ( ) ) . on ( 'click' , function ( e ) : void { if ( curState !== 3 ) { callback . call ( label , e ) ; } } ) ; } function param { Highcharts . SVGPathArray } points param { number } width param { string } roundingFunction return { Highcharts . SVGPathArray } public crispLine ( points , width , roundingFunction = 'round' ) { const start = points [ 0 ] ; const end = points [ 1 ] ; if ( defined ( start [ 1 ] ) && start [ 1 ] === end [ 1 ] ) { start [ 1 ] = end [ 1 ] = Math [ roundingFunction ] ( start [ 1 ] ) - ( width % 2 / 2 ) ; } if ( defined ( start [ 2 ] ) && start [ 2 ] === end [ 2 ] ) { start [ 2 ] = end [ 2 ] = Math [ roundingFunction ] ( start [ 2 ] ) + ( width % 2 / 2 ) ; } return points ; } sample sample example function param { Highcharts . SVGPathArray } path return { Highcharts . SVGElement } function param { Highcharts . SVGAttributes } attribs return { Highcharts . SVGElement } public path ( path ? ) { const attribs = ( this . styledMode ? { } : { fill : 'none' } ) ; if ( isArray ( path ) ) { attribs . d = path ; } else if ( isObject ( path ) ) { extend ( attribs , path as any ) ; } return this . createElement ( 'path' ) . attr ( attribs ) as any ; } sample function param { number } x param { number } y param { number } r return { Highcharts . SVGElement } function param { Highcharts . SVGAttributes } attribs return { Highcharts . SVGElement } public circle ( x ? , y ? , r ? ) { const attribs = ( isObject ( x ) ? x : typeof x === 'undefined' ? { } : { x : x , y : y , r : r } ) , wrapper = this . createElement ( 'circle' ) ; wrapper . xSetter = wrapper . ySetter = function ( value , key , element ) : void { element . setAttribute ( 'c' + key , value ) ; } ; return wrapper . attr ( attribs ) ; } public arc ( attribs ) ; public arc ( x ? , y ? , r ? , innerR ? , start ? , end ? ) ; sample function param { number } x param { number } y param { number } r param { number } innerR param { number } start param { number } end return { Highcharts . SVGElement } function param { Highcharts . SVGAttributes } attribs return { Highcharts . SVGElement } public arc ( x ? , y ? , r ? , innerR ? , start ? , end ? ) { let options ; if ( isObject ( x ) ) { options = x as SymbolOptions ; y = options . y ; r = options . r ; innerR = options . innerR ; start = options . start ; end = options . end ; x = options . x ; } else { options = { innerR , start , end } ; } const arc = this . symbol ( 'arc' , x as number , y , r , r , options ) ; arc . r = r ; return arc ; } function param { number } x param { number } y param { number } width param { number } height param { number } r param { number } strokeWidth return { Highcharts . SVGElement } sample sample function param { Highcharts . SVGAttributes } attributes return { Highcharts . SVGElement } public rect ( x ? , y ? , width ? , height ? , r ? , strokeWidth ? ) { r = isObject ( x ) ? ( x as any ) . r : r ; const wrapper = this . createElement ( 'rect' ) ; let attribs = ( isObject ( x ) ? x as SVGAttributes : typeof x === 'undefined' ? { } : { x : x , y : y , width : Math . max ( width as any , 0 ) , height : Math . max ( height as any , 0 ) } ) ; if ( ! this . styledMode ) { if ( typeof strokeWidth !== 'undefined' ) { attribs [ 'stroke-width' ] = strokeWidth ; attribs = wrapper . crisp ( attribs as any ) ; } attribs . fill = 'none' ; } if ( r ) { attribs . r = r ; } wrapper . rSetter = function ( value , _key , element ) : void { wrapper . r = value ; attr ( element , { rx : value , ry : value } ) ; } ; wrapper . rGetter = function ( ) : number { return wrapper . r || 0 ; } ; return wrapper . attr ( attribs ) ; } /**
     * Resize the  SVGRenderer and re-align all aligned child
     * elements.
     *
     *  sample function param { number } width param { number } height param { boolean | Partial < Highcharts . AnimationOptionsObject > } animate public setSize ( width , height , animate ? ) { const renderer = this ; renderer . width = width ; renderer . height = height ; renderer . boxWrapper . animate ( { width : width , height : height } , { step : function ( this ) : void { this . attr ( { viewBox : '0 0 ' + this . attr ( 'width' ) + ' ' + this . attr ( 'height' ) } ) ; } , duration : pick ( animate , true ) ? void 0 : 0 } ) ; renderer . alignElements ( ) ; } /**
     * Create and return an svg group element. Child
     *  Highcharts . SVGElement objects are added to the group by using the
     * group as the first parameter in  Highcharts . SVGElement .
     *
     *  function param { string } name return { Highcharts . SVGElement } public g ( name ? ) { const elem = this . createElement ( 'g' ) ; return name ? elem . attr ( { 'class' : 'highcharts-' + name } ) as any : elem ; } sample sample function param { string } src param { number } x param { number } y param { number } width param { number } height param { Function } onload return { Highcharts . SVGElement } public image ( src , x ? , y ? , width ? , height ? , onload ? ) { const attribs = { preserveAspectRatio : 'none' } , setSVGImageSource = function ( el , src ) : void { if ( el . setAttributeNS ) { el . setAttributeNS ( 'http://www.w3.org/1999/xlink' , 'href' , src ) ; } else { el . setAttribute ( 'hc-svg-href' , src ) ; } } ; if ( isNumber ( x ) ) { attribs . x = x ; } if ( isNumber ( y ) ) { attribs . y = y ; } if ( isNumber ( width ) ) { attribs . width = width ; } if ( isNumber ( height ) ) { attribs . height = height ; } const elemWrapper = this . createElement ( 'image' ) . attr ( attribs ) as any , onDummyLoad = function ( e ) : void { setSVGImageSource ( elemWrapper . element as any , src ) ; ( onload as any ) . call ( elemWrapper , e ) ; } ; if ( onload ) { setSVGImageSource ( elemWrapper . element as any , 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' ) ; const dummy = new win . Image ( ) ; addEvent ( dummy , 'load' , onDummyLoad as any ) ; dummy . src = src ; if ( dummy . complete ) { onDummyLoad ( { } as any ) ; } } else { setSVGImageSource ( elemWrapper . element as any , src ) ; } return elemWrapper ; } /**
     * Draw a symbol out of pre-defined shape paths from
     *  SVGRenderer .
     * It is used in Highcharts for point makers, which cake a `symbol` option,
     * and label and button backgrounds like in the tooltip and stock flags.
     *
     *  function param { string } symbol param { number } x param { number } y param { number } width param { number } height param { Highcharts . SymbolOptionsObject } options return { Highcharts . SVGElement } public symbol ( symbol , x ? , y ? , width ? , height ? , options ? ) { const ren = this , imageRegex = /^url\((.*?)\)$/ , isImage = imageRegex . test ( symbol ) , sym = ( ! isImage && ( this . symbols [ symbol ] ? symbol : 'circle' ) ) , symbolFn = ( sym && this . symbols [ sym ] ) ; let obj , path , imageSrc , centerImage ; if ( symbolFn ) { if ( typeof x === 'number' ) { path = ( symbolFn as any ) . call ( this . symbols , Math . round ( x || 0 ) , Math . round ( y || 0 ) , width || 0 , height || 0 , options ) ; } obj = this . path ( path ) ; if ( ! ren . styledMode ) { obj . attr ( 'fill' , 'none' ) ; } extend ( obj , { symbolName : ( sym || void 0 ) , x : x , y : y , width : width , height : height } ) ; if ( options ) { extend ( obj , options ) ; } } else if ( isImage ) { imageSrc = ( symbol . match ( imageRegex ) as any ) [ 1 ] ; const img = obj = this . image ( imageSrc ) ; img . imgwidth = pick ( symbolSizes [ imageSrc ] && symbolSizes [ imageSrc ] . width , options && options . width ) ; img . imgheight = pick ( symbolSizes [ imageSrc ] && symbolSizes [ imageSrc ] . height , options && options . height ) ; /**
             * Set the size and position
             */ centerImage = ( obj ) : SVGElement => obj . attr ( { width : obj . width , height : obj . height } ) ; /**
             * Width and height setters that take both the image's physical size
             * and the label size into consideration, and translates the image
             * to center within the label.
             */ [ 'width' , 'height' ] . forEach ( function ( key ) : void { img [ key + 'Setter' ] = function ( value , key ) : void { let imgSize = this [ 'img' + key ] ; this [ key ] = value ; if ( defined ( imgSize ) ) { if ( options && options . backgroundSize === 'within' && this . width && this . height ) { imgSize = Math . round ( imgSize * Math . min ( this . width / this . imgwidth , this . height / this . imgheight ) ) ; } if ( this . element ) { this . element . setAttribute ( key , imgSize ) ; } if ( ! this . alignByTranslate ) { const translate = ( ( this [ key ] || 0 ) - imgSize ) / 2 ; const attribs = key === 'width' ? { translateX : translate } : { translateY : translate } ; this . attr ( attribs ) ; } } } ; } ) ; if ( defined ( x ) ) { img . attr ( { x : x , y : y } ) ; } img . isImg = true ; if ( defined ( img . imgwidth ) && defined ( img . imgheight ) ) { centerImage ( img ) ; } else { img . attr ( { width : 0 , height : 0 } ) ; createElement ( 'img' , { onload : function ( this ) : void { const chart = charts [ ren . chartIndex ] ; if ( ( this as any ) . width === 0 ) { css ( this , { position : 'absolute' , top : '-999em' } ) ; doc . body . appendChild ( this ) ; } symbolSizes [ imageSrc ] = { width : ( this as any ) . width , height : ( this as any ) . height } ; img . imgwidth = ( this as any ) . width ; img . imgheight = ( this as any ) . height ; if ( img . element ) { centerImage ( img ) ; } if ( this . parentNode ) { this . parentNode . removeChild ( this ) ; } ren . imgCount -- ; if ( ! ren . imgCount && chart && ! chart . hasLoaded ) { chart . onload ( ) ; } } , src : imageSrc } ) ; this . imgCount ++ ; } } return obj as any ; } public clipRect ( attribs ) ; public clipRect ( x ? , y ? , width ? , height ? ) ; /**
     * Define a clipping rectangle. The clipping rectangle is later applied
     * to  SVGElement objects through the  SVGElement * function.
     *
     *  example function param { number } x param { number } y param { number } width param { number } height return { Highcharts . ClipRectElement } public clipRect ( x ? , y ? , width ? , height ? ) { const id = uniqueKey ( ) + '-' , clipPath = ( this . createElement ( 'clipPath' ) . attr ( { id : id } ) as any ) . add ( this . defs ) , wrapper = this . rect ( x , y , width , height , 0 ) . add ( clipPath ) ; wrapper . id = id ; wrapper . clipPath = clipPath ; wrapper . count = 0 ; return wrapper ; } /**
     * Draw text. The text can contain a subset of HTML, like spans and anchors
     * and some basic text styling of these. For more advanced features like
     * border and background, use  Highcharts . SVGRenderer instead.
     * To update the text after render, run `text.attr({ text: 'New text' })`.
     *
     *  sample sample sample function param { string } str param { number } x param { number } y param { boolean } useHTML return { Highcharts . SVGElement } public text ( str ? , x ? , y ? , useHTML ? ) { const renderer = this , attribs = { } ; if ( useHTML && ( renderer . allowHTML || ! renderer . forExport ) ) { return ( renderer as any ) . html ( str , x , y ) ; } attribs . x = Math . round ( x || 0 ) ; if ( y ) { attribs . y = Math . round ( y ) ; } if ( defined ( str ) ) { attribs . text = str ; } const wrapper = renderer . createElement ( 'text' ) . attr ( attribs ) ; if ( ! useHTML || ( renderer . forExport && ! renderer . allowHTML ) ) { wrapper . xSetter = function ( value , key , element ) : void { const tspans = element . getElementsByTagName ( 'tspan' ) , parentVal = element . getAttribute ( key ) ; for ( let i = 0 , tspan ; i < tspans . length ; i ++ ) { tspan = tspans [ i ] ; if ( tspan . getAttribute ( key ) === parentVal ) { tspan . setAttribute ( key , value ) ; } } element . setAttribute ( key , value ) ; } ; } return wrapper ; } function param { number | string } fontSize param { Highcharts . SVGElement | Highcharts . SVGDOMElement } elem return { Highcharts . FontMetricsObject } public fontMetrics ( fontSize ? , elem ? ) { if ( ( this . styledMode || ! /px/ . test ( fontSize as any ) ) && win . getComputedStyle ) { fontSize = elem && SVGElement . prototype . getStyle . call ( elem , 'font-size' ) ; } else { fontSize = fontSize || ( elem && elem . style && elem . style . fontSize ) || ( this . style && this . style . fontSize ) ; } if ( /px/ . test ( fontSize as any ) ) { fontSize = pInt ( fontSize ) ; } else { fontSize = 12 ; } const lineHeight = ( fontSize < 24 ? fontSize + 3 : Math . round ( fontSize * 1.2 ) ) , baseline = Math . round ( lineHeight * 0.8 ) ; return { h : lineHeight , b : baseline , f : fontSize } ; } private function public rotCorr ( baseline , rotation , alterY ? ) { let y = baseline ; if ( rotation && alterY ) { y = Math . max ( y * Math . cos ( rotation * deg2rad ) , 4 ) ; } return { x : ( - baseline / 3 ) * Math . sin ( rotation * deg2rad ) , y : y } ; } private function public pathToSegments ( path ) { const ret = [ ] ; const segment = [ ] ; const commandLength = { A : 8 , C : 7 , H : 2 , L : 3 , M : 3 , Q : 5 , S : 5 , T : 3 , V : 2 } ; for ( let i = 0 ; i < path . length ; i ++ ) { if ( isString ( segment [ 0 ] ) && isNumber ( path [ i ] ) && segment . length === commandLength [ ( segment [ 0 ] . toUpperCase ( ) ) ] ) { path . splice ( i , 0 , segment [ 0 ] . replace ( 'M' , 'L' ) . replace ( 'm' , 'l' ) ) ; } if ( typeof path [ i ] === 'string' ) { if ( segment . length ) { ret . push ( segment . slice ( 0 ) as any ) ; } segment . length = 0 ; } segment . push ( path [ i ] ) ; } ret . push ( segment . slice ( 0 ) as any ) ; return ret ; } /**
     * Draw a label, which is an extended text element with support for border
     * and background. Highcharts creates a `g` element with a text and a `path`
     * or `rect` inside, to make it behave somewhat like a HTML div. Border and
     * background are set through `stroke`, `stroke-width` and `fill` attributes
     * using the  Highcharts . SVGElement method. To update the
     * text after render, run `label.attr({ text: 'New text' })`.
     *
     *  sample function param { string } str param { number } x param { number } y param { string } shape The shape of the label's border/background, if any. Defaults to
     *        `rect`. Other possible values are `callout` or other shapes
     *        defined in  Highcharts . SVGRenderer .
     *
     *  param { number } anchorX param { number } anchorY param { boolean } useHTML param { boolean } baseline Whether to position the label relative to the text baseline,
     *        like  Highcharts . SVGRenderer , or to the
     *        upper border of the rectangle.
     *
     *  param { string } className return { Highcharts . SVGElement } public label ( str , x , y ? , shape ? , anchorX ? , anchorY ? , useHTML ? , baseline ? , className ? ) { return new SVGLabel ( this , str , x , y , shape , anchorX , anchorY , useHTML , baseline , className ) ; } private function public alignElements ( ) { this . alignedObjects . forEach ( ( el ) : SVGElement => el . align ( ) ) ; } } interface SVGRenderer extends SVGRendererLike { Element : typeof SVGElement ; SVG_NS : string ; escapes : Record < string , string > ; symbols : typeof Symbols ; draw : Function ; } extend ( SVGRenderer . prototype , { name type { Highcharts . SVGElement } Element : SVGElement , SVG_NS , example name type { Highcharts . Dictionary < string > } escapes : { '&' : '&amp;' , '<' : '&lt;' , '>' : '&gt;' , "'" : '&#39;' , '"' : '&quot;' } , name type { Highcharts . SymbolDictionary } symbols : Symbols , deprecated function draw : noop } ) ; namespace SVGRenderer { export type ClipRectElement = SVGElement ; } RendererRegistry . registerRendererType ( 'svg' , SVGRenderer , true ) ; export default SVGRenderer ; /**
 * A clipping rectangle that can be applied to one or more  SVGElement * instances. It is instanciated with the  SVGRenderer function
 * and applied with the  SVGElement function.
 *
 *  example typedef { Highcharts . SVGElement } Highcharts . ClipRectElement interface name type { number } name type { number } name type { number } interface name type { number } name type { number } interface name type { number } name type { number } name type { number } name type { number } interface name type { Highcharts . ColorString | undefined } default name type { number | undefined } default name type { number | undefined } default name type { number | undefined } default name type { number | undefined } default interface name type { number } name type { number } typedef { Array < ( Array < Highcharts . SVGPathCommand > | Array < Highcharts . SVGPathCommand , number > | Array < Highcharts . SVGPathCommand , number , number > | Array < Highcharts . SVGPathCommand , number , number , number , number > | Array < Highcharts . SVGPathCommand , number , number , number , number , number , number > | Array < Highcharts . SVGPathCommand , number , number , number , number , number , number , number > ) > } Highcharts . SVGPathArray typedef { string } Highcharts . SVGPathCommand validvalue /**
 * An extendable collection of functions for defining symbol paths. Symbols are
 * used internally for point markers, button and label borders and backgrounds,
 * or custom shapes. Extendable by adding to  SVGRenderer .
 *
 *  interface name type { Function | undefined } name type { Function | undefined } name type { Function | undefined } name type { Function | undefined } name type { Function | undefined } name type { Function | undefined } name type { Function | undefined } /**
 * Can be one of `arc`, `callout`, `circle`, `diamond`, `square`, `triangle`,
 * and `triangle-down`. Symbols are used internally for point markers, button
 * and label borders and backgrounds, or custom shapes. Extendable by adding to
 *  SVGRenderer .
 *
 *  typedef { "arc" | "callout" | "circle" | "diamond" | "square" | "triangle" | "triangle-down" } Highcharts . SymbolKeyValue interface name type { number | undefined } name type { number | undefined } name type { number | undefined } name type { boolean | undefined } name type { number | undefined } name type { number | undefined } ( '' ) ;