license import { assertInPatch , assertNoChildrenDeclaredYet , assertNotInAttributes , assertNoUnclosedTags , assertPatchElementNoExtras , assertPatchOuterHasParentNode , assertVirtualAttributesClosed , setInAttributes , setInSkip , updatePatchContext } from "./assertions" ; import { Context } from "./context" ; import { getFocusedPath , moveBefore } from "./dom_util" ; import { DEBUG } from "./global" ; import { getData } from "./node_data" ; import { createElement , createText } from "./nodes" ; import { Key , MatchFnDef , NameOrCtorDef , PatchConfig , PatchFunction } from "./types" ; param matchNode param nameOrCtor param expectedNameOrCtor param key param expectedKey returns function defaultMatchFn ( matchNode , nameOrCtor , expectedNameOrCtor , key , expectedKey ) { return nameOrCtor == expectedNameOrCtor && key == expectedKey ; } let context = null ; let currentNode = null ; let currentParent = null ; let doc = null ; let focusPath = [ ] ; let matchFn = defaultMatchFn ; /**
 * Used to build up call arguments. Each patch call gets a separate copy, so
 * this works with nested calls to patch.
 */ let argsBuilder = [ ] ; /**
 * Used to build up attrs for the an element.
 */ let attrsBuilder = [ ] ; returns function getArgsBuilder ( ) { return argsBuilder ; } returns function getAttrsBuilder ( ) { return attrsBuilder ; } param matchNode param nameOrCtor param key return function matches ( matchNode , nameOrCtor , key ) { const data = getData ( matchNode , key ) ; return matchFn ( matchNode , nameOrCtor , data . nameOrCtor , key , data . key ) ; } param matchNode param nameOrCtor param key returns function getMatchingNode ( matchNode , nameOrCtor , key ) { if ( ! matchNode ) { return null ; } let cur = matchNode ; do { if ( matches ( cur , nameOrCtor , key ) ) { return cur ; } } while ( key && ( cur = cur . nextSibling ) ) ; return null ; } param el function alwaysDiffAttributes ( el ) { getData ( el ) . alwaysDiffAttributes = true ; } param maybeParentNode param startNode param endNode function clearUnvisitedDOM ( maybeParentNode , startNode , endNode ) { const parentNode = maybeParentNode ! ; let child = startNode ; while ( child !== endNode ) { const next = child ! . nextSibling ; parentNode . removeChild ( child ! ) ; context ! . markDeleted ( child ! ) ; child = next ; } } return function getNextNode ( ) { if ( currentNode ) { return currentNode . nextSibling ; } else { return currentParent ! . firstChild ; } } /**
 * Changes to the first child of the current node.
 */ function enterNode ( ) { currentParent = currentNode ; currentNode = null ; } /**
 * Changes to the parent of the current node, removing any unvisited children.
 */ function exitNode ( ) { clearUnvisitedDOM ( currentParent , getNextNode ( ) , null ) ; currentNode = currentParent ; currentParent = currentParent ! . parentNode ; } /**
 * Changes to the next sibling of the current node.
 */ function nextNode ( ) { currentNode = getNextNode ( ) ; } param nameOrCtor param key param nonce return function createNode ( nameOrCtor , key , nonce ? ) { let node ; if ( nameOrCtor === "#text" ) { node = createText ( doc ! ) ; } else { node = createElement ( doc ! , currentParent ! , nameOrCtor , key ) ; if ( nonce ) { node . setAttribute ( "nonce" , nonce ) ; } } context ! . markCreated ( node ) ; return node ; } param nameOrCtor param key param nonce function alignWithDOM ( nameOrCtor , key , nonce ? ) { nextNode ( ) ; const existingNode = getMatchingNode ( currentNode , nameOrCtor , key ) ; const node = existingNode || createNode ( nameOrCtor , key , nonce ) ; if ( node === currentNode ) { return ; } if ( focusPath . indexOf ( node ) >= 0 ) { moveBefore ( currentParent ! , node , currentNode ) ; } else { currentParent ! . insertBefore ( node , currentNode ) ; } currentNode = node ; } param nameOrCtor param key param nonce return function open ( nameOrCtor , key ? , nonce ? ) { alignWithDOM ( nameOrCtor , key , nonce ) ; enterNode ( ) ; return currentParent as HTMLElement ; } returns function close ( ) { if ( DEBUG ) { setInSkip ( false ) ; } exitNode ( ) ; return currentNode as Element ; } returns function text ( ) { alignWithDOM ( "#text" , null ) ; return currentNode as Text ; } returns function currentElement ( ) { if ( DEBUG ) { assertInPatch ( "currentElement" ) ; assertNotInAttributes ( "currentElement" ) ; } return currentParent as Element ; } return function currentPointer ( ) { if ( DEBUG ) { assertInPatch ( "currentPointer" ) ; assertNotInAttributes ( "currentPointer" ) ; } return getNextNode ( ) ! ; } function currentContext ( ) { return context ; } /**
 * Skips the children in a subtree, allowing an Element to be closed without
 * clearing out the children.
 */ function skip ( ) { if ( DEBUG ) { assertNoChildrenDeclaredYet ( "skip" , currentNode ) ; setInSkip ( true ) ; } currentNode = currentParent ! . lastChild ; } param run param patchConfig returns function createPatcher < T , R > ( run , patchConfig = { } ) { const { matches = defaultMatchFn } = patchConfig ; const f = ( node , fn , data ) => { const prevContext = context ; const prevDoc = doc ; const prevFocusPath = focusPath ; const prevArgsBuilder = argsBuilder ; const prevAttrsBuilder = attrsBuilder ; const prevCurrentNode = currentNode ; const prevCurrentParent = currentParent ; const prevMatchFn = matchFn ; let previousInAttributes = false ; let previousInSkip = false ; doc = node . ownerDocument ; context = new Context ( node ) ; matchFn = matches ; argsBuilder = [ ] ; attrsBuilder = [ ] ; currentNode = null ; currentParent = node . parentNode ; focusPath = getFocusedPath ( node , currentParent ) ; if ( DEBUG ) { previousInAttributes = setInAttributes ( false ) ; previousInSkip = setInSkip ( false ) ; updatePatchContext ( context ) ; } try { const retVal = run ( node , fn , data ) ; if ( DEBUG ) { assertVirtualAttributesClosed ( ) ; } return retVal ; } finally { context . notifyChanges ( ) ; doc = prevDoc ; context = prevContext ; matchFn = prevMatchFn ; argsBuilder = prevArgsBuilder ; attrsBuilder = prevAttrsBuilder ; currentNode = prevCurrentNode ; currentParent = prevCurrentParent ; focusPath = prevFocusPath ; if ( DEBUG ) { setInAttributes ( previousInAttributes ) ; setInSkip ( previousInSkip ) ; updatePatchContext ( context ) ; } } } ; return f ; } param patchConfig returns function createPatchInner < T > ( patchConfig ? ) { return createPatcher ( ( node , fn , data ) => { currentNode = node ; enterNode ( ) ; fn ( data ) ; exitNode ( ) ; if ( DEBUG ) { assertNoUnclosedTags ( currentNode , node ) ; } return node ; } , patchConfig ) ; } param patchConfig returns function createPatchOuter < T > ( patchConfig ? ) { return createPatcher ( ( node , fn , data ) => { const startNode = ( { nextSibling : node } as any ) as Element ; let expectedNextNode = null ; let expectedPrevNode = null ; if ( DEBUG ) { expectedNextNode = node . nextSibling ; expectedPrevNode = node . previousSibling ; } currentNode = startNode ; fn ( data ) ; if ( DEBUG ) { if ( getData ( node ) . key ) { assertPatchOuterHasParentNode ( currentParent ) ; } assertPatchElementNoExtras ( startNode , currentNode , expectedNextNode , expectedPrevNode ) ; } if ( currentParent ) { clearUnvisitedDOM ( currentParent , getNextNode ( ) , node . nextSibling ) ; } return startNode === currentNode ? null : currentNode ; } , patchConfig ) ; } const patchInner = createPatchInner ( ) ; const patchOuter = createPatchOuter ( ) ; export { alignWithDOM , alwaysDiffAttributes , getArgsBuilder , getAttrsBuilder , text , createPatchInner , createPatchOuter , patchInner , patchOuter , open , close , currentElement , currentContext , currentPointer , skip , nextNode as skipNode } ;