function id ( element ) { return element . id ? `#${ element . id }` : '' ; } function classes ( element ) { let classSelector = '' ; const classList = element . classList ; for ( const c of classList ) { classSelector += '.' + c ; } return classSelector ; } function nthChild ( element ) { let childNumber = 0 ; const childNodes = element . parentNode ! . childNodes ; for ( const node of childNodes ) { if ( node . nodeType === Node . ELEMENT_NODE ) ++ childNumber ; if ( node === element ) return `:nth-child('${ childNumber }')` ; } } function attributes ( element ) { let attributes = '' ; for ( const attr of element . attributes ) { attributes += `[${ attr . name }="${ attr . value }"]` ; } return attributes ; } export function path ( el , rootNode = document . documentElement ) { const selector = el . tagName . toLowerCase ( ) + id ( el ) + classes ( el ) + attributes ( el ) + nthChild ( el ) ; const hasParent = el . parentNode && el . parentNode !== rootNode && ( el . parentNode as any ) . tagName ; return hasParent ? path ( el . parentNode as HTMLElement , rootNode ) + ' > ' + selector : selector ; } export function hash ( el ) { const cssPath = path ( el ) ; const type = ( el as HTMLInputElement ) . type ; const checked = ( el as HTMLInputElement ) . checked ; const value = ( el as HTMLInputElement ) . value ; const textContent = ( el as HTMLInputElement ) . textContent ; } export function traverseChildElements ( node , each , bind ? , level = 0 ) { level ++ ; for ( let child = node . firstChild ; child ; child = child . nextSibling ) { if ( child . nodeType === Node . ELEMENT_NODE ) { const el = child as Element ; if ( each . call ( bind , el , level ) ) { traverseChildElements ( el , each , bind , level ) ; } } } } export function addCSSRule ( sheet , selector , rules , index ) { if ( 'insertRule' in sheet ) { sheet . insertRule ( selector + '{' + rules + '}' , index ) ; } else if ( 'addRule' in sheet ) { sheet . addRule ( selector , rules , index ) ; } } export class Bounds { left = 0 ; top = 0 ; width = 0 ; height = 0 ; copy ( rect ) { this . top = rect . top ; this . left = rect . left ; this . width = rect . width ; this . height = rect . height ; return this ; } } export class Edges { left = 0 ; top = 0 ; right = 0 ; bottom = 0 ; copy ( rect ) { this . top = rect . top ; this . left = rect . left ; this . right = rect . right ; this . bottom = rect . bottom ; return this ; } } export function getBounds ( element , bounds = new Bounds ( ) , referenceElement ? ) { const doc = element . ownerDocument ! ; const defaultView = element . ownerDocument ! . defaultView ! ; const docEl = doc . documentElement ; const body = doc . body ; if ( element === docEl ) { return getDocumentBounds ( doc , bounds ) ; } if ( referenceElement === element ) { bounds . left = 0 ; bounds . top = 0 ; bounds . width = ( element as HTMLElement ) . offsetWidth ; bounds . height = ( element as HTMLElement ) . offsetHeight ; return ; } let el = element as HTMLElement ; let computedStyle ; let offsetParent = el . offsetParent as HTMLElement ; let prevComputedStyle = defaultView . getComputedStyle ( el , null ) ; let top = el . offsetTop ; let left = el . offsetLeft ; if ( offsetParent && referenceElement && offsetParent . contains ( referenceElement ) && offsetParent !== referenceElement ) { getBounds ( referenceElement , bounds , offsetParent ) ; left -= bounds . left ; top -= bounds . top ; } while ( ( el = el . parentNode as HTMLElement ) && el !== body && el !== docEl && el !== referenceElement ) { if ( prevComputedStyle . position === 'fixed' ) { break ; } computedStyle = defaultView . getComputedStyle ( el , null ) ; top -= el . scrollTop ; left -= el . scrollLeft ; if ( el === offsetParent ) { top += el . offsetTop ; left += el . offsetLeft ; top += parseFloat ( computedStyle . borderTopWidth ) || 0 ; left += parseFloat ( computedStyle . borderLeftWidth ) || 0 ; offsetParent = el . offsetParent as HTMLElement ; } prevComputedStyle = computedStyle ; } if ( prevComputedStyle . position === 'fixed' ) { top += Math . max ( docEl . scrollTop , body . scrollTop ) ; left += Math . max ( docEl . scrollLeft , body . scrollLeft ) ; } bounds . left = left ; bounds . top = top ; bounds . width = ( element as HTMLElement ) . offsetWidth ; bounds . height = ( element as HTMLElement ) . offsetHeight ; return bounds ; } export function getMargin ( element , margin ) { let style = getComputedStyle ( element ) ; margin . left = parseFloat ( style . marginLeft ) || 0 ; margin . right = parseFloat ( style . marginRight ) || 0 ; margin . top = parseFloat ( style . marginTop ) || 0 ; margin . bottom = parseFloat ( style . marginBottom ) || 0 ; } export function getBorder ( element , border ) { let style = getComputedStyle ( element ) ; border . left = parseFloat ( style . borderLeftWidth ) || 0 ; border . right = parseFloat ( style . borderRightWidth ) || 0 ; border . top = parseFloat ( style . borderTopWidth ) || 0 ; border . bottom = parseFloat ( style . borderBottomWidth ) || 0 ; } export function getPadding ( element , padding ) { let style = getComputedStyle ( element ) ; padding . left = parseFloat ( style . paddingLeft ) || 0 ; padding . right = parseFloat ( style . paddingRight ) || 0 ; padding . top = parseFloat ( style . paddingTop ) || 0 ; padding . bottom = parseFloat ( style . paddingBottom ) || 0 ; } export function getViewportBounds ( bounds ) { if ( ! viewportTester . parentNode ) document . documentElement . append ( viewportTester ) ; bounds . left = pageXOffset ; bounds . top = pageYOffset ; bounds . width = viewportTester . offsetWidth ; bounds . height = viewportTester . offsetHeight ; return bounds ; } const viewportTester = document . createElement ( 'div' ) ; viewportTester . id = 'VIEWPORT' ; viewportTester . style . position = 'fixed' ; viewportTester . style . width = '100vw' ; viewportTester . style . height = '100vh' ; viewportTester . style . visibility = 'hidden' ; viewportTester . style . pointerEvents = 'none' ; export function getDocumentBounds ( document , bounds ) { const documentElement = document . documentElement ; const body = document . body ; const documentElementStyle = getComputedStyle ( documentElement ) ; const bodyStyle = getComputedStyle ( body ) ; bounds . top = body . offsetTop + parseFloat ( documentElementStyle . marginTop as '' ) || 0 + parseFloat ( bodyStyle . marginTop as '' ) || 0 ; bounds . left = body . offsetLeft + parseFloat ( documentElementStyle . marginLeft as '' ) || 0 + parseFloat ( bodyStyle . marginLeft as '' ) || 0 ; bounds . width = Math . max ( Math . max ( body . scrollWidth , documentElement . scrollWidth ) , Math . max ( body . offsetWidth , documentElement . offsetWidth ) , Math . max ( body . clientWidth , documentElement . clientWidth ) ) ; bounds . height = Math . max ( Math . max ( body . scrollHeight , documentElement . scrollHeight ) , Math . max ( body . offsetHeight , documentElement . offsetHeight ) , Math . max ( body . clientHeight , documentElement . clientHeight ) ) ; return bounds ; }