'use strict' ; import type CSSObject from '../CSSObject' ; import type HTMLAttributes from '../HTML/HTMLAttributes' ; import type SVGAttributes from '../SVG/SVGAttributes' ; import H from '../../Globals.js' ; const { SVG_NS , win } = H ; import U from '../../Utilities.js' ; const { attr , createElement , css , error , isFunction , isString , objectEach , splat } = U ; const { trustedTypes } = win ; const trustedTypesPolicy = ( trustedTypes && isFunction ( trustedTypes . createPolicy ) && trustedTypes . createPolicy ( 'highcharts' , { createHTML : ( s ) : string => s } ) ) ; const emptyHTML = trustedTypesPolicy ? trustedTypesPolicy . createHTML ( '' ) as unknown as string : '' ; const hasValidDOMParser = ( function ( ) : boolean { try { return Boolean ( new DOMParser ( ) . parseFromString ( emptyHTML , 'text/html' ) ) ; } catch ( e ) { return false ; } } ( ) ) ; class name param { string | Array < Highcharts . ASTNode > } source class AST { example name static public static allowedAttributes = [ 'aria-controls' , 'aria-describedby' , 'aria-expanded' , 'aria-haspopup' , 'aria-hidden' , 'aria-label' , 'aria-labelledby' , 'aria-live' , 'aria-pressed' , 'aria-readonly' , 'aria-roledescription' , 'aria-selected' , 'class' , 'clip-path' , 'color' , 'colspan' , 'cx' , 'cy' , 'd' , 'dx' , 'dy' , 'disabled' , 'fill' , 'height' , 'href' , 'id' , 'in' , 'markerHeight' , 'markerWidth' , 'offset' , 'opacity' , 'orient' , 'padding' , 'paddingLeft' , 'paddingRight' , 'patternUnits' , 'r' , 'refX' , 'refY' , 'role' , 'scope' , 'slope' , 'src' , 'startOffset' , 'stdDeviation' , 'stroke' , 'stroke-linecap' , 'stroke-width' , 'style' , 'tableValues' , 'result' , 'rowspan' , 'summary' , 'target' , 'tabindex' , 'text-align' , 'textAnchor' , 'textLength' , 'title' , 'type' , 'valign' , 'width' , 'x' , 'x1' , 'x2' , 'y' , 'y1' , 'y2' , 'zIndex' ] ; example name static public static allowedReferences = [ 'https://' , 'http://' , 'mailto:' , '/' , '../' , './' , '#' ] ; example name static public static allowedTags = [ 'a' , 'abbr' , 'b' , 'br' , 'button' , 'caption' , 'circle' , 'clipPath' , 'code' , 'dd' , 'defs' , 'div' , 'dl' , 'dt' , 'em' , 'feComponentTransfer' , 'feFuncA' , 'feFuncB' , 'feFuncG' , 'feFuncR' , 'feGaussianBlur' , 'feOffset' , 'feMerge' , 'feMergeNode' , 'filter' , 'h1' , 'h2' , 'h3' , 'h4' , 'h5' , 'h6' , 'hr' , 'i' , 'img' , 'li' , 'linearGradient' , 'marker' , 'ol' , 'p' , 'path' , 'pattern' , 'pre' , 'rect' , 'small' , 'span' , 'stop' , 'strong' , 'style' , 'sub' , 'sup' , 'svg' , 'table' , 'text' , 'thead' , 'tbody' , 'tspan' , 'td' , 'th' , 'tr' , 'u' , 'ul' , '#text' ] ; public static emptyHTML = emptyHTML ; example name static public static bypassHTMLFiltering = false ; static function param { Highcharts . SVGAttributes } attributes return { Highcharts . SVGAttributes } public static filterUserAttributes ( attributes ) { objectEach ( attributes , ( val , key ) : void => { let valid = true ; if ( AST . allowedAttributes . indexOf ( key ) === - 1 ) { valid = false ; } if ( [ 'background' , 'dynsrc' , 'href' , 'lowsrc' , 'src' ] . indexOf ( key ) !== - 1 ) { valid = isString ( val ) && AST . allowedReferences . some ( ( ref ) : boolean => val . indexOf ( ref ) === 0 ) ; } if ( ! valid ) { error ( `Highcharts warning: Invalid attribute '${ key }' in config` ) ; delete attributes [ key ] ; } } ) ; return attributes ; } public static parseStyle ( style ) { return style . split ( ';' ) . reduce ( ( styles , line ) : CSSObject => { const pair = line . split ( ':' ) . map ( ( s ) : string => s . trim ( ) ) , key = pair [ 0 ] . replace ( /-([a-z])/g , ( g ) : string => g [ 1 ] . toUpperCase ( ) ) ; if ( pair [ 1 ] ) { ( styles as any ) [ key ] = pair [ 1 ] ; } return styles ; } , { } as CSSObject ) ; } static function param { SVGDOMElement | HTMLDOMElement } el param { string } html public static setElementHTML ( el , html ) { el . innerHTML = AST . emptyHTML ; if ( html ) { const ast = new AST ( html ) ; ast . addToDOM ( el ) ; } } constructor ( source ) { this . nodes = typeof source === 'string' ? this . parseMarkup ( source ) : source ; } /**
     * List of the nodes of this tree, can be modified before adding the tree to
     * the DOM.
     */ public nodes : Array < AST . Node > ; function param { Highcharts . HTMLDOMElement | Highcharts . SVGDOMElement } parent return { Highcharts . HTMLDOMElement | Highcharts . SVGDOMElement } public addToDOM ( parent ) { private param { Highcharts . ASTNode } subtree param { Element } subParent return { Highcharts . SVGDOMElement | Highcharts . HTMLDOMElement } function recurse ( subtree , subParent ) { let ret ; splat ( subtree ) . forEach ( function ( item ) : void { const tagName = item . tagName ; const textNode = item . textContent ? H . doc . createTextNode ( item . textContent ) : void 0 ; const bypassHTMLFiltering = AST . bypassHTMLFiltering ; let node ; if ( tagName ) { if ( tagName === '#text' ) { node = textNode ; } else if ( AST . allowedTags . indexOf ( tagName ) !== - 1 || bypassHTMLFiltering ) { const NS = tagName === 'svg' ? SVG_NS : ( subParent . namespaceURI || SVG_NS ) ; const element = H . doc . createElementNS ( NS , tagName ) ; const attributes = item . attributes || { } ; objectEach ( item , function ( val , key ) : void { if ( key !== 'tagName' && key !== 'attributes' && key !== 'children' && key !== 'style' && key !== 'textContent' ) { ( attributes as any ) [ key ] = val ; } } ) ; attr ( element as any , bypassHTMLFiltering ? attributes : AST . filterUserAttributes ( attributes ) ) ; if ( item . style ) { css ( element as any , item . style ) ; } if ( textNode ) { element . appendChild ( textNode ) ; } recurse ( item . children || [ ] , element ) ; node = element ; } else { error ( 'Highcharts warning: Invalid tagName ' + tagName + ' in config' ) ; } } if ( node ) { subParent . appendChild ( node ) ; } ret = node ; } ) ; return ret ; } return recurse ( this . nodes , parent ) ; } private function param { string } markup return { Array < Highcharts . ASTNode > } private parseMarkup ( markup ) { interface Attribute { name : ( keyof SVGAttributes | keyof HTMLAttributes ) ; value : string ; } const nodes = [ ] ; markup = markup . trim ( ) . replace ( / style="/g , ' data-style="' ) ; let doc ; if ( hasValidDOMParser ) { doc = new DOMParser ( ) . parseFromString ( trustedTypesPolicy ? trustedTypesPolicy . createHTML ( markup ) as unknown as string : markup , 'text/html' ) ; } else { const body = createElement ( 'div' ) ; body . innerHTML = markup ; doc = { body } ; } const appendChildNodes = ( node , addTo ) : void => { const tagName = node . nodeName . toLowerCase ( ) ; const astNode = { tagName } ; if ( tagName === '#text' ) { astNode . textContent = node . textContent || '' ; } const parsedAttributes = ( node as any ) . attributes ; if ( parsedAttributes ) { const attributes = { } ; [ ] . forEach . call ( parsedAttributes , ( attrib ) : void => { if ( attrib . name as string === 'data-style' ) { astNode . style = AST . parseStyle ( attrib . value ) ; } else { attributes [ attrib . name ] = attrib . value ; } } ) ; astNode . attributes = attributes ; } if ( node . childNodes . length ) { const children = [ ] ; [ ] . forEach . call ( node . childNodes , ( childNode ) : void => { appendChildNodes ( childNode , children ) ; } ) ; if ( children . length ) { astNode . children = children ; } } addTo . push ( astNode ) ; } ; [ ] . forEach . call ( doc . body . childNodes , ( childNode ) : void => appendChildNodes ( childNode , nodes ) ) ; return nodes ; } } namespace AST { export interface Node { attributes ? : ( HTMLAttributes & SVGAttributes ) ; children ? : Array < Node > ; style ? : CSSObject ; tagName ? : string ; textContent ? : string ; } } export default AST ; interface name type { Highcharts . SVGAttributes | undefined } name type { Array < Highcharts . ASTNode > | undefined } name type { string | undefined } name type { string | undefined } ( '' ) ;