import { bsearch2 } from "./bsearch2" ; import { isBrowser } from "./isbrowser" ; const isElement = ( node ) : node is Element => node . nodeType === Node . ELEMENT_NODE ; export class Dom { public static readonly caretPositionFromPoint = isBrowser && document . caretRangeFromPoint ? ( x , y ) => { const range = document . caretRangeFromPoint ( x , y ) ; return { offsetNode : range . startContainer , offset : range . startOffset } ; } : ( x , y ) => document . caretPositionFromPoint ( x , y ) ; public static isAfterNode ( parent , node , previous ) { return previous ? previous . nextSibling === node : parent . firstChild === node ; } public static replaceOrRemoveChild ( parent , newChild , oldChild ) { if ( newChild ) { parent . replaceChild ( newChild , oldChild ) ; } else { parent . removeChild ( oldChild ) ; } } public static replaceFirstChild ( parent , newChild ) { const oldChild = parent . firstChild ; if ( oldChild ) { Dom . replaceOrRemoveChild ( parent , newChild , oldChild ) ; } else if ( newChild ) { Dom . prependChild ( parent , newChild ) ; } } public static ensureFirstChild ( parent , desiredChild ) { if ( parent . firstChild !== desiredChild ) { Dom . replaceFirstChild ( parent , desiredChild ) ; } } public static removeAllChildren ( parent ) { let firstChild ; while ( ( firstChild = parent . firstChild ) !== null ) { firstChild . remove ( ) ; } } /**
     * Inserts the given 'newChild' immediately after the given 'refChild'.  If 'refChild' is undefined,
     * inserts 'newChild' as the first child of 'parent'.
     */ public static insertAfter ( parent , newChild , refChild ) { parent . insertBefore ( newChild , refChild ? refChild . nextSibling : parent . firstChild ) ; } public static prependChild ( parent , newChild ) { parent . insertBefore ( newChild , parent . firstChild ) ; } public static getClientRect ( node , nodeOffset ) { if ( isElement ( node ) ) { console . assert ( ! nodeOffset ) ; return node . getBoundingClientRect ( ) ; } const measurementRange = document . createRange ( ) ; measurementRange . setStart ( node , nodeOffset ) ; measurementRange . setEnd ( node , nodeOffset ) ; return measurementRange . getClientRects ( ) [ 0 ] ; } public static findNodeOffset ( node , x , yMin , yMax ) { const domRange = document . createRange ( ) ; return bsearch2 ( ( m ) => { domRange . setStart ( node , m ) ; domRange . setEnd ( node , m ) ; const bounds = domRange . getClientRects ( ) [ 0 ] ; const cy = ( bounds . top + bounds . bottom ) / 2 ; return ( ( cy < yMin ) || ( cy < yMax && bounds . left < x ) ) ; } , 0 , node . textContent . length ) ; } }