'use strict' ; import type BBoxObject from '../BBoxObject' ; import type CSSObject from '../CSSObject' ; import type { DOMElementType , HTMLDOMElement } from '../DOMElementType' ; import type HTMLRenderer from './HTMLRenderer' ; import type SVGRenderer from '../SVG/SVGRenderer.js' ; import H from '../../Globals.js' ; const { isFirefox , isMS , isWebKit , win } = H ; import SVGElement from '../SVG/SVGElement.js' ; import U from '../../Utilities.js' ; const { css , defined , extend , pick , pInt } = U ; type TransformKeyType = ( '-ms-transform' | '-webkit-transform' | 'MozTransform' | '-o-transform' ) ; declare module '../SVG/SVGElementLike' { interface SVGElementLike { requires appendChild : HTMLDOMElement [ 'appendChild' ] ; element : DOMElementType ; parentGroup ? : ( HTMLElement | SVGElement ) ; renderer : ( HTMLRenderer | SVGRenderer ) ; style : ( CSSObject & CSSStyleDeclaration ) ; xCorr : number ; yCorr : number ; afterSetters ( ) : void ; requires getSpanCorrection ( width , baseline , alignCorrection ) : void ; requires htmlCss ( styles ) : HTMLElement ; requires htmlGetBBox ( ) : BBoxObject ; requires htmlUpdateTransform ( ) : void ; requires setSpanRotation ( rotation , alignCorrection , baseline ) : void ; textSetter ( value ) : void ; translateXSetter ( value , key ) : void ; translateYSetter ( value , key ) : void ; } } class HTMLElement extends SVGElement { private static readonly composedClasses : Array < Function > = [ ] ; private public static compose < T extends typeof SVGElement > ( SVGElementClass ) { if ( HTMLElement . composedClasses . indexOf ( SVGElementClass ) === - 1 ) { HTMLElement . composedClasses . push ( SVGElementClass ) ; const htmlElementProto = HTMLElement . prototype , svgElementProto = SVGElementClass . prototype ; svgElementProto . getSpanCorrection = htmlElementProto . getSpanCorrection ; svgElementProto . htmlCss = htmlElementProto . htmlCss ; svgElementProto . htmlGetBBox = htmlElementProto . htmlGetBBox ; svgElementProto . htmlUpdateTransform = htmlElementProto . htmlUpdateTransform ; svgElementProto . setSpanRotation = htmlElementProto . setSpanRotation ; } return SVGElementClass as ( T & typeof HTMLElement ) ; } public div ? : HTMLDOMElement ; public parentGroup ? : HTMLElement ; private public getSpanCorrection ( width , baseline , alignCorrection ) { this . xCorr = - width * alignCorrection ; this . yCorr = - baseline ; } private public htmlCss ( styles ) { const wrapper = this , element = wrapper . element , isSettingWidth = ( element . tagName === 'SPAN' && styles && 'width' in styles ) , textWidth = pick ( isSettingWidth && styles . width , void 0 ) ; let doTransform ; if ( isSettingWidth ) { delete styles . width ; wrapper . textWidth = textWidth as any ; doTransform = true ; } if ( styles && styles . textOverflow === 'ellipsis' ) { styles . whiteSpace = 'nowrap' ; styles . overflow = 'hidden' ; } wrapper . styles = extend ( wrapper . styles , styles ) ; css ( wrapper . element , styles ) ; if ( doTransform ) { wrapper . htmlUpdateTransform ( ) ; } return wrapper ; } /**
     * VML and useHTML method for calculating the bounding box based on offsets.
     */ public htmlGetBBox ( ) { const wrapper = this , element = wrapper . element ; return { x : element . offsetLeft , y : element . offsetTop , width : element . offsetWidth , height : element . offsetHeight } ; } private public htmlUpdateTransform ( ) { if ( ! this . added ) { this . alignOnAdd = true ; return ; } const wrapper = this , renderer = wrapper . renderer , elem = wrapper . element , translateX = wrapper . translateX || 0 , translateY = wrapper . translateY || 0 , x = wrapper . x || 0 , y = wrapper . y || 0 , align = wrapper . textAlign || 'left' , alignCorrection = ( { left : 0 , center : 0.5 , right : 1 } as Record < string , number > ) [ align ] , styles = wrapper . styles , whiteSpace = styles && styles . whiteSpace ; private function getTextPxLength ( ) { if ( wrapper . textPxLength ) { return wrapper . textPxLength ; } css ( elem , { width : '' , whiteSpace : whiteSpace || 'nowrap' } ) ; return elem . offsetWidth ; } css ( elem , { marginLeft : translateX as any , marginTop : translateY as any } ) ; if ( ! renderer . styledMode && wrapper . shadows ) { wrapper . shadows . forEach ( function ( shadow ) : void { css ( shadow , { marginLeft : translateX + 1 as any , marginTop : translateY + 1 as any } ) ; } ) ; } if ( wrapper . inverted ) { [ ] . forEach . call ( elem . childNodes , function ( child ) : void { renderer . invertChild ( child as any , elem ) ; } ) ; } if ( elem . tagName === 'SPAN' ) { const rotation = wrapper . rotation , textWidth = wrapper . textWidth && pInt ( wrapper . textWidth ) , currentTextTransform = [ rotation , align , elem . innerHTML , wrapper . textWidth , wrapper . textAlign ] . join ( ',' ) ; let baseline , hasBoxWidthChanged = false ; if ( textWidth !== wrapper . oldTextWidth ) { const textPxLength = getTextPxLength ( ) ; if ( ( ( textWidth > wrapper . oldTextWidth ) || textPxLength > textWidth ) && ( /[ \-]/ . test ( elem . textContent || elem . innerText ) || elem . style . textOverflow === 'ellipsis' ) ) { css ( elem , { width : ( textPxLength > textWidth ) || rotation ? textWidth + 'px' : 'auto' , display : 'block' , whiteSpace : whiteSpace || 'normal' } ) ; wrapper . oldTextWidth = textWidth ; hasBoxWidthChanged = true ; } } wrapper . hasBoxWidthChanged = hasBoxWidthChanged ; if ( currentTextTransform !== wrapper . cTT ) { baseline = renderer . fontMetrics ( elem . style . fontSize as any , elem ) . b ; if ( defined ( rotation ) && ( ( rotation !== ( wrapper . oldRotation || 0 ) ) || ( align !== wrapper . oldAlign ) ) ) { wrapper . setSpanRotation ( rotation as any , alignCorrection , baseline ) ; } ( wrapper . getSpanCorrection as any ) ( ( ( ! defined ( rotation ) && wrapper . textPxLength ) || elem . offsetWidth ) , baseline , alignCorrection , rotation , align ) ; } css ( elem , { left : ( x + ( wrapper . xCorr || 0 ) ) + 'px' , top : ( y + ( wrapper . yCorr || 0 ) ) + 'px' } ) ; wrapper . cTT = currentTextTransform ; wrapper . oldRotation = rotation ; wrapper . oldAlign = align ; } } private public setSpanRotation ( rotation , alignCorrection , baseline ) { const getTransformKey = ( ) : ( TransformKeyType | undefined ) => ( isMS && ! /Edge/ . test ( win . navigator . userAgent ) ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : win . opera ? '-o-transform' : void 0 ) ; const rotationStyle = { } , cssTransformKey = getTransformKey ( ) ; if ( cssTransformKey ) { rotationStyle [ cssTransformKey ] = rotationStyle . transform = 'rotate(' + rotation + 'deg)' ; ( rotationStyle as any ) [ cssTransformKey + ( isFirefox ? 'Origin' : '-origin' ) ] = rotationStyle . transformOrigin = ( alignCorrection * 100 ) + '% ' + baseline + 'px' ; css ( this . element , rotationStyle ) ; } } } interface HTMLElement { element : HTMLDOMElement ; renderer : HTMLRenderer ; } export default HTMLElement ;