'use strict' ; import type AlignObject from '../AlignObject' ; import type AnimationOptions from '../../Animation/AnimationOptions' ; import type BBoxObject from '../BBoxObject' ; import type ColorString from '../../Color/ColorString' ; import type ColorType from '../../Color/ColorType' ; import type CSSObject from '../CSSObject' ; import type { DOMElementType , HTMLDOMElement , SVGDOMElement } from '../DOMElementType' ; import type FontMetricsObject from '../FontMetricsObject' ; import type GradientColor from '../../Color/GradientColor' ; import type RectangleObject from '../RectangleObject' ; import type ShadowOptionsObject from '../ShadowOptionsObject' ; import type SVGAttributes from './SVGAttributes' ; import type SVGElementLike from './SVGElementLike' ; import type SVGPath from './SVGPath' ; import type SVGRenderer from './SVGRenderer' ; import A from '../../Animation/AnimationUtilities.js' ; const { animate , animObject , stop } = A ; import AST from '../HTML/AST.js' ; import Color from '../../Color/Color.js' ; import H from '../../Globals.js' ; const { deg2rad , doc , noop , svg , SVG_NS , win } = H ; import { Palette } from '../../Color/Palettes.js' ; import U from '../../Utilities.js' ; const { addEvent , attr , createElement , css , defined , erase , extend , fireEvent , isArray , isFunction , isNumber , isString , merge , objectEach , pick , pInt , syncTimeout , uniqueKey } = U ; private declare module '../CSSObject' { interface CSSObject { stroke ? : ColorString ; strokeWidth ? : ( number | string ) ; } } /**
 * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
 * rendering layer of Highcharts. Combined with the
 *  Highcharts . SVGRenderer * object, these prototypes allow freeform annotation in the charts or even in
 * HTML pages without instanciating a chart. The SVGElement can also wrap HTML
 * labels, when `text` or `label` elements are created with the `useHTML`
 * parameter.
 *
 * The SVGElement instances are created through factory functions on the
 *  Highcharts . SVGRenderer * object, like
 *  Highcharts . SVGRenderer ,
 *  Highcharts . SVGRenderer ,
 *  Highcharts . SVGRenderer ,
 *  Highcharts . SVGRenderer ,
 *  Highcharts . SVGRenderer * and more.
 *
 *  class name class SVGElement implements SVGElementLike { public added ? : boolean ; public alignByTranslate ? : boolean ; public alignTo ? : string ; public alignValue ? : ( 'left' | 'center' | 'right' ) ; public clipPath ? : SVGElement ; public doTransform ? : boolean ; public element : DOMElementType = void 0 as any ; public fakeTS ? : boolean ; public firstLineMetrics ? : FontMetricsObject ; public handleZ ? : boolean ; public hasBoxWidthChanged ? : boolean ; public hasStroke ? : boolean ; public inverted ? : boolean ; public matrix ? : Array < number > ; public oldShadowOptions ? : ShadowOptionsObject ; public onEvents : Record < string , Function > = { } ; public opacity = 1 ; public parentInverted ? : boolean ; public parentGroup ? : SVGElement ; public pathArray ? : SVGPath ; public placed ? : boolean ; public r ? : number ; public radAttr ? : SVGAttributes ; public renderer : SVGRenderer = void 0 as any ; public rotation ? : number ; public rotationOriginX ? : number ; public rotationOriginY ? : number ; public scaleX ? : number ; public scaleY ? : number ; public shadows ? : Array < SVGDOMElement > ; public stops ? : Array < SVGElement > ; public stroke ? : ColorType ; public styledMode ? : boolean ; public styles ? : CSSObject ; public SVG_NS = SVG_NS ; public symbolCustomAttribs : Array < string > = [ 'x' , 'y' , 'width' , 'height' , 'r' , 'start' , 'end' , 'innerR' , 'anchorX' , 'anchorY' , 'rounded' ] ; public symbolName ? : string ; public text ? : SVGElement ; public textStr ? : string ; public textPathWrapper ? : SVGElement ; public x ? : number ; public y ? : number ; /**
     * Get the current value of an attribute or pseudo attribute,
     * used mainly for animation. Called internally from
     * the  Highcharts . SVGRenderer function.
     *
     *  private function param { string } key return { number | string } private _defaultGetter ( key ) { let ret = pick ( ( this as AnyRecord ) [ key + 'Value' ] , ( this as AnyRecord ) [ key ] , this . element ? this . element . getAttribute ( key ) : null , 0 ) ; if ( /^[\-0-9\.]+$/ . test ( ret ) ) { ret = parseFloat ( ret ) ; } return ret ; } private function param { string } value param { string } key param { Highcharts . SVGDOMElement } element public _defaultSetter ( value , key , element ) { element . setAttribute ( key , value ) ; } sample function param { Highcharts . SVGElement } parent The parent item to add it to. If undefined, the element is added
     *        to the  Highcharts . SVGRenderer . box .
     *
     *  return { Highcharts . SVGElement } public add ( parent ? ) { const renderer = this . renderer , element = this . element ; let inserted ; if ( parent ) { this . parentGroup = parent ; } this . parentInverted = parent && ( parent as any ) . inverted ; if ( typeof this . textStr !== 'undefined' && this . element . nodeName === 'text' ) { renderer . buildText ( this ) ; } this . added = true ; if ( ! parent || parent . handleZ || this . zIndex ) { inserted = this . zIndexSetter ( ) ; } if ( ! inserted ) { ( parent ? parent . element : renderer . box ) . appendChild ( element ) ; } if ( this . onAdd ) { this . onAdd ( ) ; } return this ; } function param { string } className param { boolean } replace return { Highcharts . SVGElement } public addClass ( className , replace ? ) { const currentClassName = replace ? '' : ( this . attr ( 'class' ) || '' ) ; className = ( className || '' ) . split ( / /g ) . reduce ( function ( newClassName , name ) : Array < string > { if ( ( currentClassName as any ) . indexOf ( name ) === - 1 ) { newClassName . push ( name ) ; } return newClassName ; } , ( currentClassName ? [ currentClassName ] : [ ] ) as Array < string > ) . join ( ' ' ) ; if ( className !== currentClassName ) { this . attr ( 'class' , className ) ; } return this ; } private function public afterSetters ( ) { if ( this . doTransform ) { this . updateTransform ( ) ; this . doTransform = false ; } } function param { Highcharts . AlignObject } alignOptions param { boolean } alignByTranslate param { string | Highcharts . BBoxObject } box return { Highcharts . SVGElement } public align ( alignOptions ? , alignByTranslate ? , box ? ) { const attribs = { } as SVGAttributes , renderer = this . renderer , alignedObjects = renderer . alignedObjects as any ; let x , y , alignTo , alignFactor , vAlignFactor ; if ( alignOptions ) { this . alignOptions = alignOptions ; this . alignByTranslate = alignByTranslate ; if ( ! box || isString ( box ) ) { this . alignTo = alignTo = box || 'renderer' ; erase ( alignedObjects , this ) ; alignedObjects . push ( this ) ; box = void 0 ; } } else { alignOptions = this . alignOptions ; alignByTranslate = this . alignByTranslate ; alignTo = this . alignTo ; } box = pick ( box , ( renderer as any ) [ alignTo as any ] , alignTo === 'scrollablePlotBox' ? ( renderer as any ) . plotBox : void 0 , renderer as any ) ; const align = ( alignOptions as any ) . align , vAlign = ( alignOptions as any ) . verticalAlign ; x = ( ( box as any ) . x || 0 ) + ( ( alignOptions as any ) . x || 0 ) ; y = ( ( box as any ) . y || 0 ) + ( ( alignOptions as any ) . y || 0 ) ; if ( align === 'right' ) { alignFactor = 1 ; } else if ( align === 'center' ) { alignFactor = 2 ; } if ( alignFactor ) { x += ( ( box as any ) . width - ( ( alignOptions as any ) . width || 0 ) ) / alignFactor ; } attribs [ alignByTranslate ? 'translateX' : 'x' ] = Math . round ( x ) ; if ( vAlign === 'bottom' ) { vAlignFactor = 1 ; } else if ( vAlign === 'middle' ) { vAlignFactor = 2 ; } if ( vAlignFactor ) { y += ( ( box as any ) . height - ( ( alignOptions as any ) . height || 0 ) ) / vAlignFactor ; } attribs [ alignByTranslate ? 'translateY' : 'y' ] = Math . round ( y ) ; this [ this . placed ? 'animate' : 'attr' ] ( attribs ) ; this . placed = true ; this . alignAttr = attribs ; return this ; } private function param { "left" | "center" | "right" } value public alignSetter ( value ) { const convert = { left : 'start' , center : 'middle' , right : 'end' } ; if ( convert [ value ] ) { this . alignValue = value ; this . element . setAttribute ( 'text-anchor' , convert [ value ] ) ; } } sample function param { Highcharts . SVGAttributes } params param { boolean | Partial < Highcharts . AnimationOptionsObject > } options param { Function } complete return { Highcharts . SVGElement } public animate ( params , options ? , complete ? ) { const animOptions = animObject ( pick ( options , this . renderer . globalAnimation , true ) ) , deferTime = animOptions . defer ; if ( pick ( doc . hidden , doc . msHidden , doc . webkitHidden , false ) ) { animOptions . duration = 0 ; } if ( animOptions . duration !== 0 ) { if ( complete ) { animOptions . complete = complete ; } syncTimeout ( ( ) : void => { if ( this . element ) { animate ( this , params , animOptions ) ; } } , deferTime ) ; } else { this . attr ( params , void 0 , complete ) ; objectEach ( params , function ( val , prop ) : void { if ( animOptions . step ) { animOptions . step . call ( this , val , { prop : prop , pos : 1 , elem : this } ) ; } } , this ) ; } return this ; } example private function param { string } textOutline public applyTextOutline ( textOutline ) { const elem = this . element , hasContrast = textOutline . indexOf ( 'contrast' ) !== - 1 , styles = { } ; if ( hasContrast ) { styles . textOutline = textOutline = textOutline . replace ( /contrast/g , this . renderer . getContrast ( elem . style . fill ) ) ; } const parts = textOutline . split ( ' ' ) ; const color = parts [ parts . length - 1 ] ; let strokeWidth = parts [ 0 ] ; if ( strokeWidth && strokeWidth !== 'none' && H . svg ) { this . fakeTS = true ; this . ySetter = this . xSetter ; strokeWidth = strokeWidth . replace ( /(^[\d\.]+)(.*?)$/g , function ( match , digit , unit ) : string { return ( 2 * Number ( digit ) ) + unit ; } ) ; this . removeTextOutline ( ) ; const outline = doc . createElementNS ( SVG_NS , 'tspan' ) as DOMElementType ; attr ( outline , { 'class' : 'highcharts-text-outline' , fill : color , stroke : color , 'stroke-width' : strokeWidth as any , 'stroke-linejoin' : 'round' } ) ; [ ] . forEach . call ( elem . childNodes , ( childNode ) : void => { const clone = childNode . cloneNode ( true ) ; if ( ( clone as any ) . removeAttribute ) { [ 'fill' , 'stroke' , 'stroke-width' , 'stroke' ] . forEach ( ( prop ) : void => ( clone as any ) . removeAttribute ( prop ) ) ; } outline . appendChild ( clone ) ; } ) ; const br = doc . createElementNS ( SVG_NS , 'tspan' ) as DOMElementType ; br . textContent = '\u200B' ; ( [ 'x' , 'y' ] as Array < 'x' | 'y' > ) . forEach ( ( key ) : void => { const value = elem . getAttribute ( key ) ; if ( value ) { br . setAttribute ( key , value ) ; } } ) ; outline . appendChild ( br ) ; elem . insertBefore ( outline , elem . firstChild ) ; } } public attr ( key ) ; public attr ( key , val , complete ? , continueAnimation ? ) ; public attr ( hash ? , val ? , complete ? , continueAnimation ? ) ; function param { string } key return { number | string } sample example function param { string | Highcharts . SVGAttributes } hash param { number | string | Highcharts . SVGPathArray } val param { Function } complete A callback function to execute after setting the attributes. This
    *        makes the function compliant and interchangeable with the
    *         SVGElement function.
    *
    *  param { boolean } continueAnimation return { Highcharts . SVGElement } *         If used as a setter, it returns the current
    *          Highcharts . SVGElement so the calls can be chained. If
    *         used as a getter, the current value of the attribute is returned.
     public attr ( hash ? , val ? , complete ? , continueAnimation ? ) { const element = this . element , symbolCustomAttribs = this . symbolCustomAttribs ; let key , hasSetSymbolSize , ret = this , skipAttr , setter ; if ( typeof hash === 'string' && typeof val !== 'undefined' ) { key = hash ; hash = { } ; ( hash as any ) [ key ] = val ; } if ( typeof hash === 'string' ) { ret = ( ( this as AnyRecord ) [ hash + 'Getter' ] || ( this as AnyRecord ) . _defaultGetter ) . call ( this , hash , element ) ; } else { objectEach ( hash , function eachAttribute ( val , key ) : void { skipAttr = false ; if ( ! continueAnimation ) { stop ( this as any , key ) ; } if ( this . symbolName && symbolCustomAttribs . indexOf ( key ) !== - 1 ) { if ( ! hasSetSymbolSize ) { this . symbolAttr ( hash as any ) ; hasSetSymbolSize = true ; } skipAttr = true ; } if ( this . rotation && ( key === 'x' || key === 'y' ) ) { this . doTransform = true ; } if ( ! skipAttr ) { setter = ( ( this as AnyRecord ) [ key + 'Setter' ] || ( this as AnyRecord ) . _defaultSetter ) ; setter . call ( this , val , key , element ) ; if ( ! this . styledMode && this . shadows && /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/ . test ( key ) ) { this . updateShadows ( key , val , setter ) ; } } } , this ) ; this . afterSetters ( ) ; } if ( complete ) { complete . call ( this ) ; } return ret ; } function param { Highcharts . ClipRectElement } clipRect return { Highcharts . SVGElement } public clip ( clipRect ? ) { return this . attr ( 'clip-path' , clipRect ? 'url(' + this . renderer . url + '#' + clipRect . id + ')' : 'none' ) ; } function param { Highcharts . RectangleObject } rect param { number } strokeWidth return { Highcharts . RectangleObject } public crisp ( rect , strokeWidth ? ) { const wrapper = this ; strokeWidth = strokeWidth || rect . strokeWidth || 0 ; const normalizer = Math . round ( strokeWidth ) % 2 / 2 ; rect . x = Math . floor ( rect . x || wrapper . x || 0 ) + normalizer ; rect . y = Math . floor ( rect . y || wrapper . y || 0 ) + normalizer ; rect . width = Math . floor ( ( rect . width || wrapper . width || 0 ) - 2 * normalizer ) ; rect . height = Math . floor ( ( rect . height || wrapper . height || 0 ) - 2 * normalizer ) ; if ( defined ( rect . strokeWidth ) ) { rect . strokeWidth = strokeWidth ; } return rect ; } private function param { Highcharts . GradientColorObject | Highcharts . PatternObject } colorOptions param { string } prop param { Highcharts . SVGDOMElement } elem public complexColor ( colorOptions , prop , elem ) { const renderer = this . renderer ; let colorObject , gradName , gradAttr , radAttr , gradients , stops , stopColor , stopOpacity , radialReference , id , key = [ ] , value ; fireEvent ( this . renderer , 'complexColor' , { args : arguments } , function ( ) : void { if ( ( colorOptions as GradientColor ) . radialGradient ) { gradName = 'radialGradient' ; } else if ( ( colorOptions as GradientColor ) . linearGradient ) { gradName = 'linearGradient' ; } if ( gradName ) { gradAttr = ( colorOptions as GradientColor ) [ gradName ] as any ; gradients = renderer . gradients as any ; stops = ( colorOptions as GradientColor ) . stops ; radialReference = ( elem as any ) . radialReference ; if ( isArray ( gradAttr ) ) { ( colorOptions as any ) [ gradName ] = gradAttr = { x1 : gradAttr [ 0 ] as number , y1 : gradAttr [ 1 ] as number , x2 : gradAttr [ 2 ] as number , y2 : gradAttr [ 3 ] as number , gradientUnits : 'userSpaceOnUse' } ; } if ( gradName === 'radialGradient' && radialReference && ! defined ( gradAttr . gradientUnits ) ) { radAttr = gradAttr ; gradAttr = merge ( gradAttr , renderer . getRadialAttr ( radialReference , radAttr ) , { gradientUnits : 'userSpaceOnUse' } ) ; } objectEach ( gradAttr , function ( value , n ) : void { if ( n !== 'id' ) { ( key as any ) . push ( n , value ) ; } } ) ; objectEach ( stops , function ( val ) : void { ( key as any ) . push ( val ) ; } ) ; key = ( key as any ) . join ( ',' ) ; if ( gradients [ key as any ] ) { id = gradients [ key as any ] . attr ( 'id' ) ; } else { gradAttr . id = id = uniqueKey ( ) ; const gradientObject = gradients [ key as any ] = renderer . createElement ( gradName ) . attr ( gradAttr ) . add ( renderer . defs ) as any ; gradientObject . radAttr = radAttr ; gradientObject . stops = [ ] ; ( stops as any ) . forEach ( function ( stop ) : void { if ( stop [ 1 ] . indexOf ( 'rgba' ) === 0 ) { colorObject = Color . parse ( stop [ 1 ] ) ; stopColor = colorObject . get ( 'rgb' ) as any ; stopOpacity = colorObject . get ( 'a' ) as any ; } else { stopColor = stop [ 1 ] ; stopOpacity = 1 ; } const stopObject = renderer . createElement ( 'stop' ) . attr ( { offset : stop [ 0 ] , 'stop-color' : stopColor , 'stop-opacity' : stopOpacity } ) . add ( gradientObject as any ) ; gradientObject . stops . push ( stopObject ) ; } ) ; } value = 'url(' + renderer . url + '#' + id + ')' ; elem . setAttribute ( prop , value ) ; elem . gradient = key as any ; colorOptions . toString = function ( ) : string { return value ; } ; } } ) ; } sample function param { Highcharts . CSSObject } styles return { Highcharts . SVGElement } public css ( styles ) { const oldStyles = this . styles , newStyles = { } , elem = this . element ; let textWidth , hasNew = ! oldStyles ; if ( styles . color ) { styles . fill = styles . color ; } if ( oldStyles ) { objectEach ( styles , function ( value , n ) : void { if ( oldStyles && oldStyles [ n ] !== value ) { ( newStyles as any ) [ n ] = value ; hasNew = true ; } } ) ; } if ( hasNew ) { if ( oldStyles ) { styles = extend ( oldStyles , newStyles ) ; } if ( styles . width === null || styles . width as any === 'auto' ) { delete this . textWidth ; } else if ( elem . nodeName . toLowerCase ( ) === 'text' && styles . width ) { textWidth = this . textWidth = pInt ( styles . width ) ; } this . styles = styles ; if ( textWidth && ( ! svg && this . renderer . forExport ) ) { delete styles . width ; } const stylesToApply = merge ( styles ) ; if ( elem . namespaceURI === this . SVG_NS ) { ( [ 'textOutline' , 'textOverflow' , 'width' ] as ( 'textOutline' | 'textOverflow' | 'width' ) [ ] ) . forEach ( ( key ) : boolean | undefined => ( stylesToApply && delete stylesToApply [ key ] ) ) ; } css ( elem , stylesToApply ) ; if ( this . added ) { if ( this . element . nodeName === 'text' ) { this . renderer . buildText ( this ) ; } if ( styles . textOutline ) { this . applyTextOutline ( styles . textOutline ) ; } } } return this ; } private function param { string } value public dashstyleSetter ( value ) { let i , strokeWidth = this [ 'stroke-width' ] ; if ( strokeWidth as unknown as string === 'inherit' ) { strokeWidth = 1 ; } value = value && value . toLowerCase ( ) ; if ( value ) { const v = value . replace ( 'shortdashdotdot' , '3,1,1,1,1,1,' ) . replace ( 'shortdashdot' , '3,1,1,1' ) . replace ( 'shortdot' , '1,1,' ) . replace ( 'shortdash' , '3,1,' ) . replace ( 'longdash' , '8,3,' ) . replace ( /dot/g , '1,3,' ) . replace ( 'dash' , '4,3,' ) . replace ( /,$/ , '' ) . split ( ',' ) ; i = v . length ; while ( i -- ) { v [ i ] = '' + ( pInt ( v [ i ] ) * pick ( strokeWidth , NaN ) ) ; } value = v . join ( ',' ) . replace ( /NaN/g , 'none' ) ; this . element . setAttribute ( 'stroke-dasharray' , value ) ; } } function public destroy ( ) { const wrapper = this , element = wrapper . element || { } , renderer = wrapper . renderer , ownerSVGElement = ( element as SVGDOMElement ) . ownerSVGElement ; let parentToClean = ( renderer . isSVG && element . nodeName === 'SPAN' && wrapper . parentGroup || void 0 ) , grandParent , i ; element . onclick = element . onmouseout = element . onmouseover = element . onmousemove = ( element as any ) . point = null ; stop ( wrapper as any ) ; if ( wrapper . clipPath && ownerSVGElement ) { const clipPath = wrapper . clipPath ; [ ] . forEach . call ( ownerSVGElement . querySelectorAll ( '[clip-path],[CLIP-PATH]' ) , function ( el ) : void { if ( ( el . getAttribute ( 'clip-path' ) as any ) . indexOf ( clipPath . element . id ) > - 1 ) { el . removeAttribute ( 'clip-path' ) ; } } ) ; wrapper . clipPath = clipPath . destroy ( ) ; } if ( wrapper . stops ) { for ( i = 0 ; i < wrapper . stops . length ; i ++ ) { wrapper . stops [ i ] . destroy ( ) ; } wrapper . stops . length = 0 ; wrapper . stops = void 0 ; } wrapper . safeRemoveChild ( element ) ; if ( ! renderer . styledMode ) { wrapper . destroyShadows ( ) ; } while ( parentToClean && parentToClean . div && parentToClean . div . childNodes . length === 0 ) { grandParent = ( parentToClean as any ) . parentGroup ; wrapper . safeRemoveChild ( ( parentToClean as any ) . div ) ; delete ( parentToClean as any ) . div ; parentToClean = grandParent ; } if ( wrapper . alignTo ) { erase ( renderer . alignedObjects , wrapper ) ; } objectEach ( wrapper , function ( val , key ) : void { if ( ( wrapper as AnyRecord ) [ key ] && ( wrapper as AnyRecord ) [ key ] . parentGroup === wrapper && ( wrapper as AnyRecord ) [ key ] . destroy ) { ( wrapper as AnyRecord ) [ key ] . destroy ( ) ; } delete ( wrapper as AnyRecord ) [ key ] ; } ) ; return ; } private function public destroyShadows ( ) { ( this . shadows || [ ] ) . forEach ( function ( shadow ) : void { this . safeRemoveChild ( shadow ) ; } , this ) ; this . shadows = void 0 ; } private public destroyTextPath ( elem , path ) { const textElement = elem . getElementsByTagName ( 'text' ) [ 0 ] ; let childNodes ; if ( textElement ) { textElement . removeAttribute ( 'dx' ) ; textElement . removeAttribute ( 'dy' ) ; path . element . setAttribute ( 'id' , '' ) ; if ( this . textPathWrapper && textElement . getElementsByTagName ( 'textPath' ) . length ) { childNodes = this . textPathWrapper . element . childNodes ; while ( childNodes . length ) { textElement . appendChild ( childNodes [ 0 ] ) ; } textElement . removeChild ( this . textPathWrapper . element ) ; } } else if ( elem . getAttribute ( 'dx' ) || elem . getAttribute ( 'dy' ) ) { elem . removeAttribute ( 'dx' ) ; elem . removeAttribute ( 'dy' ) ; } if ( this . textPathWrapper ) { this . textPathWrapper = this . textPathWrapper . destroy ( ) ; } } private function param { number | string | Highcharts . SVGPathArray } value param { string } key param { Highcharts . SVGDOMElement } element public dSetter ( value , key , element ) { if ( isArray ( value ) ) { if ( typeof value [ 0 ] === 'string' ) { value = this . renderer . pathToSegments ( value as any ) ; } this . pathArray = value ; value = value . reduce ( ( acc , seg , i ) : string => { if ( ! seg || ! seg . join ) { return ( seg || '' ) . toString ( ) ; } return ( i ? acc + ' ' : '' ) + seg . join ( ' ' ) ; } , '' ) ; } if ( /(NaN| {2}|^$)/ . test ( value ) ) { value = 'M 0 0' ; } if ( ( this as AnyRecord ) [ key ] !== value ) { element . setAttribute ( key , value ) ; ( this as AnyRecord ) [ key ] = value ; } } function param { number } duration public fadeOut ( duration ? ) { const elemWrapper = this ; elemWrapper . animate ( { opacity : 0 } , { duration : pick ( duration , 150 ) , complete : function ( ) : void { elemWrapper . attr ( { y : - 9999 } ) . hide ( ) ; } } ) ; } private function param { Highcharts . ColorType } value param { string } key param { Highcharts . SVGDOMElement } element public fillSetter ( value , key , element ) { if ( typeof value === 'string' ) { element . setAttribute ( key , value ) ; } else if ( value ) { this . complexColor ( value as any , key , element ) ; } } sample function param { boolean } reload param { number } rot return { Highcharts . BBoxObject } public getBBox ( reload ? , rot ? ) { const wrapper = this , renderer = wrapper . renderer , element = wrapper . element , styles = wrapper . styles , textStr = wrapper . textStr , cache = renderer . cache , cacheKeys = renderer . cacheKeys , isSVG = element . namespaceURI === wrapper . SVG_NS , rotation = pick ( rot , wrapper . rotation , 0 ) , fontSize = renderer . styledMode ? ( element && SVGElement . prototype . getStyle . call ( element , 'font-size' ) ) : ( styles && styles . fontSize ) ; let bBox , width , height , toggleTextShadowShim , cacheKey ; if ( defined ( textStr ) ) { cacheKey = textStr . toString ( ) ; if ( cacheKey . indexOf ( '<' ) === - 1 ) { cacheKey = cacheKey . replace ( /[0-9]/g , '0' ) ; } cacheKey += [ '' , rotation , fontSize , wrapper . textWidth , styles && styles . textOverflow , styles && styles . fontWeight ] . join ( ',' ) ; } if ( cacheKey && ! reload ) { bBox = cache [ cacheKey ] ; } if ( ! bBox ) { if ( isSVG || renderer . forExport ) { try { toggleTextShadowShim = this . fakeTS && function ( display ) : void { const outline = element . querySelector ( '.highcharts-text-outline' ) as DOMElementType | undefined ; if ( outline ) { css ( outline , { display } ) ; } } ; if ( isFunction ( toggleTextShadowShim ) ) { toggleTextShadowShim ( 'none' ) ; } bBox = ( element as any ) . getBBox ? extend ( { } as any , ( element as any ) . getBBox ( ) ) : { width : ( element as any ) . offsetWidth , height : ( element as any ) . offsetHeight } ; if ( isFunction ( toggleTextShadowShim ) ) { toggleTextShadowShim ( '' ) ; } } catch ( e ) { '' ; } if ( ! bBox || bBox . width < 0 ) { bBox = { width : 0 , height : 0 } as any ; } } else { bBox = wrapper . htmlGetBBox ( ) ; } if ( renderer . isSVG ) { width = bBox . width ; height = bBox . height ; if ( isSVG ) { bBox . height = height = ( ( { '11px,17' : 14 , '13px,20' : 16 } as Record < string , number > ) [ `${ fontSize || '' },${ Math . round ( height ) }` ] || height ) ; } if ( rotation ) { const rad = rotation * deg2rad ; bBox . width = Math . abs ( height * Math . sin ( rad ) ) + Math . abs ( width * Math . cos ( rad ) ) ; bBox . height = Math . abs ( height * Math . cos ( rad ) ) + Math . abs ( width * Math . sin ( rad ) ) ; } } if ( cacheKey && ( textStr === '' || bBox . height > 0 ) ) { while ( cacheKeys . length > 250 ) { delete cache [ cacheKeys . shift ( ) as any ] ; } if ( ! cache [ cacheKey ] ) { cacheKeys . push ( cacheKey ) ; } cache [ cacheKey ] = bBox ; } } return bBox ; } example function param { string } prop return { string } public getStyle ( prop ) { return win . getComputedStyle ( this . element || this , '' ) . getPropertyValue ( prop ) ; } function param { string } className return { boolean } public hasClass ( className ) { return ( '' + this . attr ( 'class' ) ) . split ( ' ' ) . indexOf ( className ) !== - 1 ; } function param { boolean } hideByTranslation return { Highcharts . SVGElement } public hide ( hideByTranslation ? ) { if ( hideByTranslation ) { this . attr ( { y : - 9999 } ) ; } else { this . attr ( { visibility : 'hidden' } ) ; } return this ; } private public htmlGetBBox ( ) { return { height : 0 , width : 0 , x : 0 , y : 0 } ; } function param { Highcharts . SVGRenderer } renderer param { string } nodeName public init ( renderer , nodeName ) { name type { Highcharts . SVGDOMElement | Highcharts . HTMLDOMElement } this . element = nodeName === 'span' ? createElement ( nodeName ) as HTMLDOMElement : doc . createElementNS ( this . SVG_NS , nodeName ) as SVGDOMElement ; name type { Highcharts . SVGRenderer } this . renderer = renderer ; fireEvent ( this , 'afterInit' ) ; } function param { boolean } inverted return { Highcharts . SVGElement } public invert ( inverted ) { this . inverted = inverted ; this . updateTransform ( ) ; return this ; } /**
     * Add an event listener. This is a simple setter that replaces the
     * previous event of the same type added by this function, as opposed to
     * the  Highcharts function.
     *
     *  sample function param { string } eventType param { Function } handler return { Highcharts . SVGElement } public on ( eventType , handler ) { const { onEvents } = this ; if ( onEvents [ eventType ] ) { onEvents [ eventType ] ( ) ; } onEvents [ eventType ] = addEvent ( this . element , eventType , handler ) ; return this ; } private function param { string } value param { string } key param { Highcharts . SVGDOMElement } element public opacitySetter ( value , key , element ) { const opacity = Number ( Number ( value ) . toFixed ( 3 ) ) ; this . opacity = opacity ; element . setAttribute ( key , opacity ) ; } function param { string | RegExp } className return { Highcharts . SVGElement } public removeClass ( className ) { return this . attr ( 'class' , ( '' + this . attr ( 'class' ) ) . replace ( isString ( className ) ? new RegExp ( `(^| )${ className }( |$)` ) : className , ' ' ) . replace ( / +/g , ' ' ) . trim ( ) ) ; } private public removeTextOutline ( ) { const outline = this . element . querySelector ( 'tspan.highcharts-text-outline' ) as SVGDOMElement ; if ( outline ) { this . safeRemoveChild ( outline ) ; } } private function param { Highcharts . SVGDOMElement | Highcharts . HTMLDOMElement } element public safeRemoveChild ( element ) { const parentNode = element . parentNode ; if ( parentNode ) { parentNode . removeChild ( element ) ; } } function param { Array < number > } coordinates return { Highcharts . SVGElement } public setRadialReference ( coordinates ) { const existingGradient = ( this . element . gradient && this . renderer . gradients [ this . element . gradient ] ) ; this . element . radialReference = coordinates ; if ( existingGradient && existingGradient . radAttr ) { existingGradient . animate ( this . renderer . getRadialAttr ( coordinates , existingGradient . radAttr ) ) ; } return this ; } private function param { Highcharts . SVGElement } path param { Highcharts . DataLabelsTextPathOptionsObject } textPathOptions return { Highcharts . SVGElement } public setTextPath ( path , textPathOptions ) { const elem = this . element , textNode = this . text ? this . text . element : elem , attribsMap = { textAnchor : 'text-anchor' } ; let adder = false , textPathElement , textPathId , textPathWrapper = this . textPathWrapper as any , firstTime = ! textPathWrapper ; textPathOptions = merge ( true , { enabled : true , attributes : { dy : - 5 , startOffset : '50%' , textAnchor : 'middle' } } , textPathOptions ) ; const attrs = AST . filterUserAttributes ( textPathOptions . attributes ) ; if ( path && textPathOptions && textPathOptions . enabled ) { if ( textPathWrapper && textPathWrapper . element . parentNode === null ) { firstTime = true ; textPathWrapper = textPathWrapper . destroy ( ) as any ; } else if ( textPathWrapper ) { this . removeTextOutline . call ( textPathWrapper . parentGroup ) ; } if ( this . options && this . options . padding ) { attrs . dx = - this . options . padding ; } if ( ! textPathWrapper ) { this . textPathWrapper = textPathWrapper = this . renderer . createElement ( 'textPath' ) as any ; adder = true ; } textPathElement = textPathWrapper . element ; textPathId = path . element . getAttribute ( 'id' ) ; if ( ! textPathId ) { path . element . setAttribute ( 'id' , textPathId = uniqueKey ( ) ) ; } if ( firstTime ) { textNode . setAttribute ( 'y' , 0 ) ; if ( isNumber ( attrs . dx ) ) { textNode . setAttribute ( 'x' , - attrs . dx ) ; } const childNodes = [ ] . slice . call ( textNode . childNodes ) ; for ( let i = 0 ; i < childNodes . length ; i ++ ) { const childNode = childNodes [ i ] ; if ( childNode . nodeType === win . Node . TEXT_NODE || childNode . nodeName === 'tspan' ) { textPathElement . appendChild ( childNode ) ; } } } if ( adder && textPathWrapper ) { textPathWrapper . add ( { element : textNode } as any ) ; } textPathElement . setAttributeNS ( 'http://www.w3.org/1999/xlink' , 'href' , this . renderer . url + '#' + textPathId ) ; if ( defined ( attrs . dy ) ) { ( textPathElement . parentNode as any ) . setAttribute ( 'dy' , attrs . dy ) ; delete attrs . dy ; } if ( defined ( attrs . dx ) ) { ( textPathElement . parentNode as any ) . setAttribute ( 'dx' , attrs . dx ) ; delete attrs . dx ; } objectEach ( attrs , function ( val , key ) : void { textPathElement . setAttribute ( ( attribsMap as any ) [ key ] || key , val as any ) ; } ) ; elem . removeAttribute ( 'transform' ) ; this . removeTextOutline . call ( textPathWrapper ) ; if ( this . text && ! this . renderer . styledMode ) { this . attr ( { fill : 'none' , 'stroke-width' : 0 } ) ; } this . updateTransform = noop ; this . applyTextOutline = noop ; } else if ( textPathWrapper ) { delete ( this as any ) . updateTransform ; delete ( this as any ) . applyTextOutline ; this . destroyTextPath ( elem as any , path ) ; this . updateTransform ( ) ; if ( this . options && this . options . rotation ) { this . applyTextOutline ( this . options . style . textOutline ) ; } } return this ; } example function param { boolean | Highcharts . ShadowOptionsObject } shadowOptions param { Highcharts . SVGElement } group param { boolean } cutOff return { Highcharts . SVGElement } public shadow ( shadowOptions ? , group ? , cutOff ? ) { const shadows = [ ] , element = this . element , oldShadowOptions = this . oldShadowOptions , defaultShadowOptions = { color : Palette . neutralColor100 , offsetX : this . parentInverted ? - 1 : 1 , offsetY : this . parentInverted ? - 1 : 1 , opacity : 0.15 , width : 3 } ; let i , shadow , strokeWidth , shadowElementOpacity , update = false , transform , options ; if ( shadowOptions === true ) { options = defaultShadowOptions ; } else if ( typeof shadowOptions === 'object' ) { options = extend ( defaultShadowOptions , shadowOptions ) ; } if ( options ) { if ( options && oldShadowOptions ) { objectEach ( options , ( value , key ) : void => { if ( value !== ( oldShadowOptions as any ) [ key ] ) { update = true ; } } ) ; } if ( update ) { this . destroyShadows ( ) ; } this . oldShadowOptions = options ; } if ( ! options ) { this . destroyShadows ( ) ; } else if ( ! this . shadows ) { shadowElementOpacity = options . opacity / options . width ; transform = this . parentInverted ? `translate(${ options . offsetY }, ${ options . offsetX })` : `translate(${ options . offsetX }, ${ options . offsetY })` ; for ( i = 1 ; i <= options . width ; i ++ ) { shadow = element . cloneNode ( false ) as any ; strokeWidth = ( options . width * 2 ) + 1 - ( 2 * i ) ; attr ( shadow , { stroke : ( ( shadowOptions as any ) . color || Palette . neutralColor100 ) , 'stroke-opacity' : shadowElementOpacity * i , 'stroke-width' : strokeWidth , transform , fill : 'none' } ) ; shadow . setAttribute ( 'class' , ( shadow . getAttribute ( 'class' ) || '' ) + ' highcharts-shadow' ) ; if ( cutOff ) { attr ( shadow , 'height' , Math . max ( ( attr ( shadow , 'height' ) as any ) - strokeWidth , 0 ) ) ; shadow . cutHeight = strokeWidth ; } if ( group ) { group . element . appendChild ( shadow ) ; } else if ( element . parentNode ) { element . parentNode . insertBefore ( shadow , element ) ; } shadows . push ( shadow ) ; } this . shadows = shadows ; } return this ; } function param { boolean } inherit return { Highcharts . SVGElement } public show ( inherit ? ) { return this . attr ( { visibility : inherit ? 'inherit' : 'visible' } ) ; } private function param { number | string | ColorType } value param { string } key param { Highcharts . SVGDOMElement } element public strokeSetter ( value , key , element ) { ( this as AnyRecord ) [ key ] = value ; if ( this . stroke && this [ 'stroke-width' ] ) { SVGElement . prototype . fillSetter . call ( this , this . stroke , 'stroke' , element ) ; element . setAttribute ( 'stroke-width' , this [ 'stroke-width' ] ) ; this . hasStroke = true ; } else if ( key === 'stroke-width' && value === 0 && this . hasStroke ) { element . removeAttribute ( 'stroke' ) ; this . hasStroke = false ; } else if ( this . renderer . styledMode && this [ 'stroke-width' ] ) { element . setAttribute ( 'stroke-width' , this [ 'stroke-width' ] ) ; this . hasStroke = true ; } } function return { number } public strokeWidth ( ) { if ( ! this . renderer . styledMode ) { return this [ 'stroke-width' ] || 0 ; } const val = this . getStyle ( 'stroke-width' ) ; let ret = 0 , dummy ; if ( val . indexOf ( 'px' ) === val . length - 2 ) { ret = pInt ( val ) ; } else if ( val !== '' ) { dummy = doc . createElementNS ( SVG_NS , 'rect' ) as SVGDOMElement ; attr ( dummy , { width : val as any , 'stroke-width' : 0 } ) ; ( this . element . parentNode as any ) . appendChild ( dummy ) ; ret = ( dummy as any ) . getBBox ( ) . width ; ( dummy . parentNode as any ) . removeChild ( dummy ) ; } return ret ; } private function param { Highcharts . SVGAttributes } hash public symbolAttr ( hash ) { const wrapper = this as AnyRecord ; [ 'x' , 'y' , 'r' , 'start' , 'end' , 'width' , 'height' , 'innerR' , 'anchorX' , 'anchorY' , 'clockwise' ] . forEach ( function ( key ) : void { wrapper [ key ] = pick ( ( hash as any ) [ key ] , wrapper [ key ] ) ; } ) ; wrapper . attr ( { d : ( wrapper . renderer . symbols [ wrapper . symbolName ] as any ) ( wrapper . x , wrapper . y , wrapper . width , wrapper . height , wrapper ) } ) ; } private function param { string } value public textSetter ( value ) { if ( value !== this . textStr ) { delete this . textPxLength ; this . textStr = value ; if ( this . added ) { this . renderer . buildText ( this as any ) ; } } } private function param { string } value public titleSetter ( value ) { const el = this . element ; const titleNode = el . getElementsByTagName ( 'title' ) [ 0 ] || doc . createElementNS ( this . SVG_NS , 'title' ) ; if ( el . insertBefore ) { el . insertBefore ( titleNode , el . firstChild ) ; } else { el . appendChild ( titleNode ) ; } titleNode . textContent = String ( pick ( value , '' ) ) . replace ( /<[^>]*>/g , '' ) . replace ( /&lt;/g , '<' ) . replace ( /&gt;/g , '>' ) ; } sample function return { Highcharts . SVGElement } public toFront ( ) { const element = this . element ; element . parentNode . appendChild ( element ) ; return this ; } function param { number } x param { number } y return { Highcharts . SVGElement } public translate ( x , y ) { return this . attr ( { translateX : x , translateY : y } ) as any ; } private function param { string } key param { number } value param { Function } setter public updateShadows ( key , value , setter ) { const shadows = this . shadows ; if ( shadows ) { let i = shadows . length ; while ( i -- ) { setter . call ( shadows [ i ] , key === 'height' ? Math . max ( value - ( shadows [ i ] . cutHeight || 0 ) , 0 ) : key === 'd' ? this . d : value , key , shadows [ i ] ) ; } } } private function public updateTransform ( ) { const wrapper = this , scaleX = wrapper . scaleX , scaleY = wrapper . scaleY , inverted = wrapper . inverted , rotation = wrapper . rotation , matrix = wrapper . matrix , element = wrapper . element ; let translateX = wrapper . translateX || 0 , translateY = wrapper . translateY || 0 ; if ( inverted ) { translateX += wrapper . width ; translateY += wrapper . height ; } const transform = [ 'translate(' + translateX + ',' + translateY + ')' ] ; if ( defined ( matrix ) ) { transform . push ( 'matrix(' + matrix . join ( ',' ) + ')' ) ; } if ( inverted ) { transform . push ( 'rotate(90) scale(-1,1)' ) ; } else if ( rotation ) { transform . push ( 'rotate(' + rotation + ' ' + pick ( this . rotationOriginX , element . getAttribute ( 'x' ) , 0 ) + ' ' + pick ( this . rotationOriginY , element . getAttribute ( 'y' ) || 0 ) + ')' ) ; } if ( defined ( scaleX ) || defined ( scaleY ) ) { transform . push ( 'scale(' + pick ( scaleX , 1 ) + ' ' + pick ( scaleY , 1 ) + ')' ) ; } if ( transform . length ) { element . setAttribute ( 'transform' , transform . join ( ' ' ) ) ; } } private function param { string } value param { string } key param { Highcharts . SVGDOMElement } element public visibilitySetter ( value , key , element ) { if ( value === 'inherit' ) { element . removeAttribute ( key ) ; } else if ( ( this as AnyRecord ) [ key ] !== value ) { element . setAttribute ( key , value ) ; } ( this as AnyRecord ) [ key ] = value ; } private function public xGetter ( key ) { if ( this . element . nodeName === 'circle' ) { if ( key === 'x' ) { key = 'cx' ; } else if ( key === 'y' ) { key = 'cy' ; } } return this . _defaultGetter ( key ) ; } private function public zIndexSetter ( value ? , key ? ) { const renderer = this . renderer , parentGroup = this . parentGroup , parentWrapper = parentGroup || renderer , parentNode = ( parentWrapper as any ) . element || renderer . box , element = this . element , svgParent = parentNode === renderer . box ; let childNodes , otherElement , otherZIndex , inserted = false , undefinedOtherZIndex , run = this . added , i ; if ( defined ( value ) ) { element . setAttribute ( 'data-z-index' , ( value as any ) ) ; ( value as any ) = + ( value as any ) ; if ( ( this as any ) [ key as any ] === value ) { run = false ; } } else if ( defined ( ( this as any ) [ key as any ] ) ) { element . removeAttribute ( 'data-z-index' ) ; } ( this as any ) [ key as any ] = value ; if ( run ) { value = this . zIndex ; if ( value && parentGroup ) { parentGroup . handleZ = true ; } childNodes = parentNode . childNodes ; for ( i = childNodes . length - 1 ; i >= 0 && ! inserted ; i -- ) { otherElement = childNodes [ i ] ; otherZIndex = otherElement . getAttribute ( 'data-z-index' ) ; undefinedOtherZIndex = ! defined ( otherZIndex ) ; if ( otherElement !== element ) { if ( value as any < 0 && undefinedOtherZIndex && ! svgParent && ! i ) { parentNode . insertBefore ( element , childNodes [ i ] ) ; inserted = true ; } else if ( pInt ( otherZIndex ) <= ( value as any ) || ( undefinedOtherZIndex && ( ! defined ( value ) || ( value as any ) >= 0 ) ) ) { parentNode . insertBefore ( element , childNodes [ i + 1 ] || null ) ; inserted = true ; } } } if ( ! inserted ) { parentNode . insertBefore ( element , childNodes [ svgParent ? 3 : 0 ] || null ) ; inserted = true ; } } return inserted ; } } interface SVGElement extends SVGElementLike { matrixSetter : SVGElement . SetterFunction < ( number | string | null ) > ; rotationOriginXSetter : SVGElement . SetterFunction < ( number | string | null ) > ; rotationOriginYSetter : SVGElement . SetterFunction < ( number | string | null ) > ; rotationSetter ( value , key ? ) : void ; scaleXSetter : SVGElement . SetterFunction < ( number | string | null ) > ; scaleYSetter : SVGElement . SetterFunction < ( number | string | null ) > ; 'stroke-widthSetter' ( value , key , element ) : void ; translateXSetter : SVGElement . SetterFunction < ( number | string | null ) > ; translateYSetter : SVGElement . SetterFunction < ( number | string | null ) > ; verticalAlignSetter : SVGElement . SetterFunction < ( number | string | null ) > ; yGetter ( key ) : ( number | string | null ) ; } SVGElement . prototype [ 'stroke-widthSetter' ] = SVGElement . prototype . strokeSetter ; SVGElement . prototype . yGetter = SVGElement . prototype . xGetter ; SVGElement . prototype . matrixSetter = SVGElement . prototype . rotationOriginXSetter = SVGElement . prototype . rotationOriginYSetter = SVGElement . prototype . rotationSetter = SVGElement . prototype . scaleXSetter = SVGElement . prototype . scaleYSetter = SVGElement . prototype . translateXSetter = SVGElement . prototype . translateYSetter = SVGElement . prototype . verticalAlignSetter = function ( value , key ) : void { ( this as AnyRecord ) [ key ] = value ; this . doTransform = true ; } ; namespace SVGElement { export interface ElementSetterFunction < T > { ( value , key , element ) : void ; } export interface GetterFunction < T > { ( key ) : T ; } export interface SetterFunction < T > { ( value , key ) : void ; } } export default SVGElement ; global typedef { global . SVGElement } GlobalSVGElement see https typedef { "center" | "left" | "right" } Highcharts . AlignValue interface name type { Highcharts . AlignValue | undefined } default name type { Highcharts . VerticalAlignValue | undefined } default name type { number | undefined } default name type { number | undefined } default name type { boolean | undefined } default interface extends Highcharts . PositionObject name type { number } name type { number } name type { number } name type { number } example interface name type { * } name type { string | Highcharts . SVGPathArray | undefined } name type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject | undefined } name type { boolean | undefined } name type { Array < number > | undefined } name type { number | undefined } name type { number | undefined } name type { number | undefined } name type { number | undefined } name type { number | undefined } name type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject | undefined } name type { string | Highcharts . CSSObject | undefined } name type { number | undefined } name type { number | undefined } name type { number | undefined } typedef { globals . GlobalSVGElement } Highcharts . SVGDOMElement see https typedef { "bottom" | "middle" | "top" } Highcharts . VerticalAlignValue '' ;