/**
 * Virtual DOM patching algorithm based on Snabbdom by
 * Simon Friis Vindum (@paldepind)
 * Licensed under the MIT License
 * https://github.com/snabbdom/snabbdom/blob/master/LICENSE
 *
 * Modified for Stencil's renderer and slot projection
 */ import type * as d from '../../declarations' ; import { BUILD } from '@app-data' ; import { CMP_FLAGS , HTML_NS , SVG_NS , isDef } from '@utils' ; import { consoleDevError , doc , plt , supportsShadow } from '@platform' ; import { h , isHost , newVNode } from './h' ; import { NODE_TYPE , PLATFORM_FLAGS , VNODE_FLAGS } from '../runtime-constants' ; import { updateElement } from './update-element' ; let scopeId ; let contentRef ; let hostTagName ; let useNativeShadowDom = false ; let checkSlotFallbackVisibility = false ; let checkSlotRelocate = false ; let isSvgMode = false ; const createElm = ( oldParentVNode , newParentVNode , childIndex , parentElm ) => { let newVNode = newParentVNode . $children$ [ childIndex ] ; let i = 0 ; let elm ; let childNode ; let oldVNode ; if ( BUILD . slotRelocation && ! useNativeShadowDom ) { checkSlotRelocate = true ; if ( newVNode . $tag$ === 'slot' ) { if ( scopeId ) { parentElm . classList . add ( scopeId + '-s' ) ; } newVNode . $flags$ |= newVNode . $children$ ? VNODE_FLAGS . isSlotFallback : VNODE_FLAGS . isSlotReference ; } } if ( BUILD . isDev && newVNode . $elm$ ) { consoleDevError ( `The JSX ${ newVNode . $text$ !== null ? `"${ newVNode . $text$ }" text` : `"${ newVNode . $tag$ }" element` } node should not be shared within the same renderer. The renderer caches element lookups in order to improve performance. However, a side effect from this is that the exact same JSX node should not be reused. For more information please see https://stenciljs.com/docs/templating-jsx#avoid-shared-jsx-nodes` ) ; } if ( BUILD . vdomText && newVNode . $text$ !== null ) { elm = newVNode . $elm$ = doc . createTextNode ( newVNode . $text$ ) as any ; } else if ( BUILD . slotRelocation && newVNode . $flags$ & VNODE_FLAGS . isSlotReference ) { elm = newVNode . $elm$ = BUILD . isDebug || BUILD . hydrateServerSide ? slotReferenceDebugNode ( newVNode ) : ( doc . createTextNode ( '' ) as any ) ; } else { if ( BUILD . svg && ! isSvgMode ) { isSvgMode = newVNode . $tag$ === 'svg' ; } elm = newVNode . $elm$ = ( BUILD . svg ? doc . createElementNS ( isSvgMode ? SVG_NS : HTML_NS , BUILD . slotRelocation && newVNode . $flags$ & VNODE_FLAGS . isSlotFallback ? 'slot-fb' : ( newVNode . $tag$ as string ) ) : doc . createElement ( BUILD . slotRelocation && newVNode . $flags$ & VNODE_FLAGS . isSlotFallback ? 'slot-fb' : ( newVNode . $tag$ as string ) ) ) as any ; if ( BUILD . svg && isSvgMode && newVNode . $tag$ === 'foreignObject' ) { isSvgMode = false ; } if ( BUILD . vdomAttribute ) { updateElement ( null , newVNode , isSvgMode ) ; } if ( ( BUILD . shadowDom || BUILD . scoped ) && isDef ( scopeId ) && elm [ 's-si' ] !== scopeId ) { elm . classList . add ( ( elm [ 's-si' ] = scopeId ) ) ; } if ( newVNode . $children$ ) { for ( i = 0 ; i < newVNode . $children$ . length ; ++ i ) { childNode = createElm ( oldParentVNode , newVNode , i , elm ) ; if ( childNode ) { elm . appendChild ( childNode ) ; } } } if ( BUILD . svg ) { if ( newVNode . $tag$ === 'svg' ) { isSvgMode = false ; } else if ( elm . tagName === 'foreignObject' ) { isSvgMode = true ; } } } if ( BUILD . slotRelocation ) { elm [ 's-hn' ] = hostTagName ; if ( newVNode . $flags$ & ( VNODE_FLAGS . isSlotFallback | VNODE_FLAGS . isSlotReference ) ) { elm [ 's-sr' ] = true ; elm [ 's-cr' ] = contentRef ; elm [ 's-sn' ] = newVNode . $name$ || '' ; oldVNode = oldParentVNode && oldParentVNode . $children$ && oldParentVNode . $children$ [ childIndex ] ; if ( oldVNode && oldVNode . $tag$ === newVNode . $tag$ && oldParentVNode . $elm$ ) { putBackInOriginalLocation ( oldParentVNode . $elm$ , false ) ; } } } return elm ; } ; const putBackInOriginalLocation = ( parentElm , recursive ) => { plt . $flags$ |= PLATFORM_FLAGS . isTmpDisconnected ; const oldSlotChildNodes = parentElm . childNodes ; for ( let i = oldSlotChildNodes . length - 1 ; i >= 0 ; i -- ) { const childNode = oldSlotChildNodes [ i ] as any ; if ( childNode [ 's-hn' ] !== hostTagName && childNode [ 's-ol' ] ) { parentReferenceNode ( childNode ) . insertBefore ( childNode , referenceNode ( childNode ) ) ; childNode [ 's-ol' ] . remove ( ) ; childNode [ 's-ol' ] = undefined ; checkSlotRelocate = true ; } if ( recursive ) { putBackInOriginalLocation ( childNode , recursive ) ; } } plt . $flags$ &= ~ PLATFORM_FLAGS . isTmpDisconnected ; } ; const addVnodes = ( parentElm , before , parentVNode , vnodes , startIdx , endIdx ) => { let containerElm = ( ( BUILD . slotRelocation && parentElm [ 's-cr' ] && parentElm [ 's-cr' ] . parentNode ) || parentElm ) as any ; let childNode ; if ( BUILD . shadowDom && ( containerElm as any ) . shadowRoot && containerElm . tagName === hostTagName ) { containerElm = ( containerElm as any ) . shadowRoot ; } for ( ; startIdx <= endIdx ; ++ startIdx ) { if ( vnodes [ startIdx ] ) { childNode = createElm ( null , parentVNode , startIdx , parentElm ) ; if ( childNode ) { vnodes [ startIdx ] . $elm$ = childNode as any ; containerElm . insertBefore ( childNode , BUILD . slotRelocation ? referenceNode ( before ) : before ) ; } } } } ; const removeVnodes = ( vnodes , startIdx , endIdx , vnode ? , elm ? ) => { for ( ; startIdx <= endIdx ; ++ startIdx ) { if ( ( vnode = vnodes [ startIdx ] ) ) { elm = vnode . $elm$ ; callNodeRefs ( vnode ) ; if ( BUILD . slotRelocation ) { checkSlotFallbackVisibility = true ; if ( elm [ 's-ol' ] ) { elm [ 's-ol' ] . remove ( ) ; } else { putBackInOriginalLocation ( elm , true ) ; } } elm . remove ( ) ; } } } ; const updateChildren = ( parentElm , oldCh , newVNode , newCh ) => { let oldStartIdx = 0 ; let newStartIdx = 0 ; let idxInOld = 0 ; let i = 0 ; let oldEndIdx = oldCh . length - 1 ; let oldStartVnode = oldCh [ 0 ] ; let oldEndVnode = oldCh [ oldEndIdx ] ; let newEndIdx = newCh . length - 1 ; let newStartVnode = newCh [ 0 ] ; let newEndVnode = newCh [ newEndIdx ] ; let node ; let elmToMove ; while ( oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx ) { if ( oldStartVnode == null ) { oldStartVnode = oldCh [ ++ oldStartIdx ] ; } else if ( oldEndVnode == null ) { oldEndVnode = oldCh [ -- oldEndIdx ] ; } else if ( newStartVnode == null ) { newStartVnode = newCh [ ++ newStartIdx ] ; } else if ( newEndVnode == null ) { newEndVnode = newCh [ -- newEndIdx ] ; } else if ( isSameVnode ( oldStartVnode , newStartVnode ) ) { patch ( oldStartVnode , newStartVnode ) ; oldStartVnode = oldCh [ ++ oldStartIdx ] ; newStartVnode = newCh [ ++ newStartIdx ] ; } else if ( isSameVnode ( oldEndVnode , newEndVnode ) ) { patch ( oldEndVnode , newEndVnode ) ; oldEndVnode = oldCh [ -- oldEndIdx ] ; newEndVnode = newCh [ -- newEndIdx ] ; } else if ( isSameVnode ( oldStartVnode , newEndVnode ) ) { if ( BUILD . slotRelocation && ( oldStartVnode . $tag$ === 'slot' || newEndVnode . $tag$ === 'slot' ) ) { putBackInOriginalLocation ( oldStartVnode . $elm$ . parentNode , false ) ; } patch ( oldStartVnode , newEndVnode ) ; parentElm . insertBefore ( oldStartVnode . $elm$ , oldEndVnode . $elm$ . nextSibling as any ) ; oldStartVnode = oldCh [ ++ oldStartIdx ] ; newEndVnode = newCh [ -- newEndIdx ] ; } else if ( isSameVnode ( oldEndVnode , newStartVnode ) ) { if ( BUILD . slotRelocation && ( oldStartVnode . $tag$ === 'slot' || newEndVnode . $tag$ === 'slot' ) ) { putBackInOriginalLocation ( oldEndVnode . $elm$ . parentNode , false ) ; } patch ( oldEndVnode , newStartVnode ) ; parentElm . insertBefore ( oldEndVnode . $elm$ , oldStartVnode . $elm$ ) ; oldEndVnode = oldCh [ -- oldEndIdx ] ; newStartVnode = newCh [ ++ newStartIdx ] ; } else { idxInOld = - 1 ; if ( BUILD . vdomKey ) { for ( i = oldStartIdx ; i <= oldEndIdx ; ++ i ) { if ( oldCh [ i ] && oldCh [ i ] . $key$ !== null && oldCh [ i ] . $key$ === newStartVnode . $key$ ) { idxInOld = i ; break ; } } } if ( BUILD . vdomKey && idxInOld >= 0 ) { elmToMove = oldCh [ idxInOld ] ; if ( elmToMove . $tag$ !== newStartVnode . $tag$ ) { node = createElm ( oldCh && oldCh [ newStartIdx ] , newVNode , idxInOld , parentElm ) ; } else { patch ( elmToMove , newStartVnode ) ; oldCh [ idxInOld ] = undefined ; node = elmToMove . $elm$ ; } newStartVnode = newCh [ ++ newStartIdx ] ; } else { node = createElm ( oldCh && oldCh [ newStartIdx ] , newVNode , newStartIdx , parentElm ) ; newStartVnode = newCh [ ++ newStartIdx ] ; } if ( node ) { if ( BUILD . slotRelocation ) { parentReferenceNode ( oldStartVnode . $elm$ ) . insertBefore ( node , referenceNode ( oldStartVnode . $elm$ ) ) ; } else { oldStartVnode . $elm$ . parentNode . insertBefore ( node , oldStartVnode . $elm$ ) ; } } } } if ( oldStartIdx > oldEndIdx ) { addVnodes ( parentElm , newCh [ newEndIdx + 1 ] == null ? null : newCh [ newEndIdx + 1 ] . $elm$ , newVNode , newCh , newStartIdx , newEndIdx ) ; } else if ( BUILD . updatable && newStartIdx > newEndIdx ) { removeVnodes ( oldCh , oldStartIdx , oldEndIdx ) ; } } ; export const isSameVnode = ( vnode1 , vnode2 ) => { if ( vnode1 . $tag$ === vnode2 . $tag$ ) { if ( BUILD . slotRelocation && vnode1 . $tag$ === 'slot' ) { return vnode1 . $name$ === vnode2 . $name$ ; } if ( BUILD . vdomKey ) { return vnode1 . $key$ === vnode2 . $key$ ; } return true ; } return false ; } ; const referenceNode = ( node ) => { return ( node && node [ 's-ol' ] ) || node ; } ; const parentReferenceNode = ( node ) => ( node [ 's-ol' ] ? node [ 's-ol' ] : node ) . parentNode ; export const patch = ( oldVNode , newVNode ) => { const elm = ( newVNode . $elm$ = oldVNode . $elm$ ) ; const oldChildren = oldVNode . $children$ ; const newChildren = newVNode . $children$ ; const tag = newVNode . $tag$ ; const text = newVNode . $text$ ; let defaultHolder ; if ( ! BUILD . vdomText || text === null ) { if ( BUILD . svg ) { isSvgMode = tag === 'svg' ? true : tag === 'foreignObject' ? false : isSvgMode ; } if ( BUILD . vdomAttribute || BUILD . reflect ) { if ( BUILD . slot && tag === 'slot' ) { } else { updateElement ( oldVNode , newVNode , isSvgMode ) ; } } if ( BUILD . updatable && oldChildren !== null && newChildren !== null ) { updateChildren ( elm , oldChildren , newVNode , newChildren ) ; } else if ( newChildren !== null ) { if ( BUILD . updatable && BUILD . vdomText && oldVNode . $text$ !== null ) { elm . textContent = '' ; } addVnodes ( elm , null , newVNode , newChildren , 0 , newChildren . length - 1 ) ; } else if ( BUILD . updatable && oldChildren !== null ) { removeVnodes ( oldChildren , 0 , oldChildren . length - 1 ) ; } if ( BUILD . svg && isSvgMode && tag === 'svg' ) { isSvgMode = false ; } } else if ( BUILD . vdomText && BUILD . slotRelocation && ( defaultHolder = elm [ 's-cr' ] as any ) ) { defaultHolder . parentNode . textContent = text ; } else if ( BUILD . vdomText && oldVNode . $text$ !== text ) { elm . data = text ; } } ; const updateFallbackSlotVisibility = ( elm ) => { let childNodes = elm . childNodes as any ; let childNode ; let i ; let ilen ; let j ; let slotNameAttr ; let nodeType ; for ( i = 0 , ilen = childNodes . length ; i < ilen ; i ++ ) { childNode = childNodes [ i ] ; if ( childNode . nodeType === NODE_TYPE . ElementNode ) { if ( childNode [ 's-sr' ] ) { slotNameAttr = childNode [ 's-sn' ] ; childNode . hidden = false ; for ( j = 0 ; j < ilen ; j ++ ) { nodeType = childNodes [ j ] . nodeType ; if ( childNodes [ j ] [ 's-hn' ] !== childNode [ 's-hn' ] || slotNameAttr !== '' ) { if ( nodeType === NODE_TYPE . ElementNode && slotNameAttr === childNodes [ j ] . getAttribute ( 'slot' ) ) { childNode . hidden = true ; break ; } } else { if ( nodeType === NODE_TYPE . ElementNode || ( nodeType === NODE_TYPE . TextNode && childNodes [ j ] . textContent . trim ( ) !== '' ) ) { childNode . hidden = true ; break ; } } } } updateFallbackSlotVisibility ( childNode ) ; } } } ; const relocateNodes = [ ] ; const relocateSlotContent = ( elm ) => { let childNode ; let node ; let hostContentNodes ; let slotNameAttr ; let relocateNodeData ; let j ; let i = 0 ; let childNodes = elm . childNodes as any ; let ilen = childNodes . length ; for ( ; i < ilen ; i ++ ) { childNode = childNodes [ i ] ; if ( childNode [ 's-sr' ] && ( node = childNode [ 's-cr' ] ) && node . parentNode ) { hostContentNodes = node . parentNode . childNodes ; slotNameAttr = childNode [ 's-sn' ] ; for ( j = hostContentNodes . length - 1 ; j >= 0 ; j -- ) { node = hostContentNodes [ j ] as d . RenderNode ; if ( ! node [ 's-cn' ] && ! node [ 's-nr' ] && node [ 's-hn' ] !== childNode [ 's-hn' ] ) { if ( isNodeLocatedInSlot ( node , slotNameAttr ) ) { relocateNodeData = relocateNodes . find ( ( r ) => r . $nodeToRelocate$ === node ) ; checkSlotFallbackVisibility = true ; node [ 's-sn' ] = node [ 's-sn' ] || slotNameAttr ; if ( relocateNodeData ) { relocateNodeData . $slotRefNode$ = childNode ; } else { relocateNodes . push ( { $slotRefNode$ : childNode , $nodeToRelocate$ : node , } ) ; } if ( node [ 's-sr' ] ) { relocateNodes . map ( ( relocateNode ) => { if ( isNodeLocatedInSlot ( relocateNode . $nodeToRelocate$ , node [ 's-sn' ] ) ) { relocateNodeData = relocateNodes . find ( ( r ) => r . $nodeToRelocate$ === node ) ; if ( relocateNodeData && ! relocateNode . $slotRefNode$ ) { relocateNode . $slotRefNode$ = relocateNodeData . $slotRefNode$ ; } } } ) ; } } else if ( ! relocateNodes . some ( ( r ) => r . $nodeToRelocate$ === node ) ) { relocateNodes . push ( { $nodeToRelocate$ : node , } ) ; } } } } if ( childNode . nodeType === NODE_TYPE . ElementNode ) { relocateSlotContent ( childNode ) ; } } } ; const isNodeLocatedInSlot = ( nodeToRelocate , slotNameAttr ) => { if ( nodeToRelocate . nodeType === NODE_TYPE . ElementNode ) { if ( nodeToRelocate . getAttribute ( 'slot' ) === null && slotNameAttr === '' ) { return true ; } if ( nodeToRelocate . getAttribute ( 'slot' ) === slotNameAttr ) { return true ; } return false ; } if ( nodeToRelocate [ 's-sn' ] === slotNameAttr ) { return true ; } return slotNameAttr === '' ; } ; export const callNodeRefs = ( vNode ) => { if ( BUILD . vdomRef ) { vNode . $attrs$ && vNode . $attrs$ . ref && vNode . $attrs$ . ref ( null ) ; vNode . $children$ && vNode . $children$ . map ( callNodeRefs ) ; } } ; interface RelocateNodeData { $slotRefNode$ ? : d . RenderNode ; $nodeToRelocate$ : d . RenderNode ; } export const renderVdom = ( hostRef , renderFnResults ) => { const hostElm = hostRef . $hostElement$ ; const cmpMeta = hostRef . $cmpMeta$ ; const oldVNode = hostRef . $vnode$ || newVNode ( null , null ) ; const rootVnode = isHost ( renderFnResults ) ? renderFnResults : h ( null , null , renderFnResults as any ) ; hostTagName = hostElm . tagName ; if ( BUILD . isDev && Array . isArray ( renderFnResults ) && renderFnResults . some ( isHost ) ) { throw new Error ( `The <Host> must be the single root component.
Looks like the render() function of "${ hostTagName . toLowerCase ( ) }" is returning an array that contains the <Host>.

The render() function should look like this instead:

render() {
  // Do not return an array
  return (
    <Host>{content}</Host>
  );
}
  ` ) ; } if ( BUILD . reflect && cmpMeta . $attrsToReflect$ ) { rootVnode . $attrs$ = rootVnode . $attrs$ || { } ; cmpMeta . $attrsToReflect$ . map ( ( [ propName , attribute ] ) => ( rootVnode . $attrs$ [ attribute ] = ( hostElm as any ) [ propName ] ) ) ; } rootVnode . $tag$ = null ; rootVnode . $flags$ |= VNODE_FLAGS . isHost ; hostRef . $vnode$ = rootVnode ; rootVnode . $elm$ = oldVNode . $elm$ = ( BUILD . shadowDom ? hostElm . shadowRoot || hostElm : hostElm ) as any ; if ( BUILD . scoped || BUILD . shadowDom ) { scopeId = hostElm [ 's-sc' ] ; } if ( BUILD . slotRelocation ) { contentRef = hostElm [ 's-cr' ] ; useNativeShadowDom = supportsShadow && ( cmpMeta . $flags$ & CMP_FLAGS . shadowDomEncapsulation ) !== 0 ; checkSlotFallbackVisibility = false ; } patch ( oldVNode , rootVnode ) ; if ( BUILD . slotRelocation ) { plt . $flags$ |= PLATFORM_FLAGS . isTmpDisconnected ; if ( checkSlotRelocate ) { relocateSlotContent ( rootVnode . $elm$ ) ; let relocateData ; let nodeToRelocate ; let orgLocationNode ; let parentNodeRef ; let insertBeforeNode ; let refNode ; let i = 0 ; for ( ; i < relocateNodes . length ; i ++ ) { relocateData = relocateNodes [ i ] ; nodeToRelocate = relocateData . $nodeToRelocate$ ; if ( ! nodeToRelocate [ 's-ol' ] ) { orgLocationNode = BUILD . isDebug || BUILD . hydrateServerSide ? originalLocationDebugNode ( nodeToRelocate ) : ( doc . createTextNode ( '' ) as any ) ; orgLocationNode [ 's-nr' ] = nodeToRelocate ; nodeToRelocate . parentNode . insertBefore ( ( nodeToRelocate [ 's-ol' ] = orgLocationNode ) , nodeToRelocate ) ; } } for ( i = 0 ; i < relocateNodes . length ; i ++ ) { relocateData = relocateNodes [ i ] ; nodeToRelocate = relocateData . $nodeToRelocate$ ; if ( relocateData . $slotRefNode$ ) { parentNodeRef = relocateData . $slotRefNode$ . parentNode ; insertBeforeNode = relocateData . $slotRefNode$ . nextSibling ; orgLocationNode = nodeToRelocate [ 's-ol' ] as any ; while ( ( orgLocationNode = orgLocationNode . previousSibling as any ) ) { refNode = orgLocationNode [ 's-nr' ] ; if ( refNode && refNode [ 's-sn' ] === nodeToRelocate [ 's-sn' ] && parentNodeRef === refNode . parentNode ) { refNode = refNode . nextSibling as any ; if ( ! refNode || ! refNode [ 's-nr' ] ) { insertBeforeNode = refNode ; break ; } } } if ( ( ! insertBeforeNode && parentNodeRef !== nodeToRelocate . parentNode ) || nodeToRelocate . nextSibling !== insertBeforeNode ) { if ( nodeToRelocate !== insertBeforeNode ) { if ( ! nodeToRelocate [ 's-hn' ] && nodeToRelocate [ 's-ol' ] ) { nodeToRelocate [ 's-hn' ] = nodeToRelocate [ 's-ol' ] . parentNode . nodeName ; } parentNodeRef . insertBefore ( nodeToRelocate , insertBeforeNode ) ; } } } else { if ( nodeToRelocate . nodeType === NODE_TYPE . ElementNode ) { nodeToRelocate . hidden = true ; } } } } if ( checkSlotFallbackVisibility ) { updateFallbackSlotVisibility ( rootVnode . $elm$ ) ; } plt . $flags$ &= ~ PLATFORM_FLAGS . isTmpDisconnected ; relocateNodes . length = 0 ; } } ; const slotReferenceDebugNode = ( slotVNode ) => doc . createComment ( `<slot${ slotVNode . $name$ ? ' name="' + slotVNode . $name$ + '"' : '' }> (host=${ hostTagName . toLowerCase ( ) })` ) ; const originalLocationDebugNode = ( nodeToRelocate ) : any => doc . createComment ( `org-location for ` + ( nodeToRelocate . localName ? `<${ nodeToRelocate . localName }> (host=${ nodeToRelocate [ 's-hn' ] })` : `[${ nodeToRelocate . textContent }]` ) ) ;