export interface Props { [ key ] : any ; } export type VChild = VNode | string ; export interface VNode { type : string ; props : Props ; children : VChild [ ] ; key : any ; } export const options = { renderKeys : false , } ; type Node = Element | Text ; const EMPTY_ARR = [ ] ; const SVG_NS = 'http://www.w3.org/2000/svg' ; const KEY_ATTR = 'data-key' ; const domProps = new Set ( [ 'value' , 'selected' , 'checked' , 'contentEditable' ] ) ; const getKey = ( vdom ) => ( vdom == null ? vdom : ( vdom as any ) . key ) ; const setKey = ( dom , key ? ) => { if ( key && key !== dom . key ) { dom . key = key ; options . renderKeys && dom . setAttribute ( KEY_ATTR , key ) ; } if ( ! key && dom . key ) { delete dom . key ; options . renderKeys && dom . removeAttribute ( KEY_ATTR ) ; } } ; const listener = ( event ) => { ( event . currentTarget as any ) . events [ event . type ] ( event ) ; } ; const patchProp = ( dom , key , oldVal , newVal , isSvg ? ) => { if ( key === 'key' ) { } else if ( key [ 0 ] === 'o' && key [ 1 ] === 'n' ) { if ( ! ( ( ( dom as any ) . events || ( ( dom as any ) . events = { } ) ) [ ( key = key . slice ( 2 ) ) ] = newVal ) ) { dom . removeEventListener ( key , listener ) ; } else if ( ! oldVal ) { dom . addEventListener ( key , listener ) ; } } else if ( newVal == null ) { dom . removeAttribute ( key ) ; } else if ( ! isSvg && key !== 'list' && key !== 'form' && key in dom ) { dom [ key ] = newVal == null ? '' : newVal ; } else { dom . setAttribute ( key , newVal ) ; } } ; const createNode = ( vdom , isSvg ? ) => { if ( typeof vdom === 'string' ) { return document . createTextNode ( vdom ) ; } var props = ( vdom as VNode ) . props ; var dom = ( isSvg = ( isSvg || vdom . type === 'svg' ) ) ? document . createElementNS ( SVG_NS , vdom . type , { is : props . is } ) : document . createElement ( vdom . type , { is : props . is } ) ; for ( var k in props ) patchProp ( dom as Element , k , null , props [ k ] , isSvg ) ; setKey ( dom , getKey ( vdom ) ) ; vdom . children . forEach ( kid => dom . appendChild ( createNode ( vdomify ( kid ) , isSvg ) ) ) ; return dom ; } ; const getDomProps = ( dom , isSvg ? ) : Props => { const props = { } ; for ( let i = 0 ; i < dom . attributes . length ; i ++ ) { const { name , value } = dom . attributes [ i ] ; if ( name in dom && name !== 'list' && ! isSvg ) { props [ name ] = dom [ name ] ; } else if ( ! options . renderKeys || name !== KEY_ATTR ) { props [ name ] = value === '' ? true : value ; } } return props ; } ; const patchDom = ( parent , dom , oldDom , newVdom , isSvg ? ) => { if ( typeof newVdom === 'string' ) { if ( oldDom != null && oldDom . nodeType === Node . TEXT_NODE ) { if ( oldDom . nodeValue !== newVdom ) dom . nodeValue = newVdom ; } else { dom = parent . insertBefore ( createNode ( newVdom , isSvg ) , dom ) ; if ( oldDom != null ) { parent . removeChild ( oldDom ) ; } } } else if ( oldDom == null || oldDom . nodeName . toLowerCase ( ) !== ( newVdom as VNode ) . type ) { dom = parent . insertBefore ( createNode ( vdomify ( newVdom ) , isSvg ) , dom ) ; if ( oldDom != null ) { parent . removeChild ( oldDom ) ; } } else { var oldProps = getDomProps ( oldDom as Element , isSvg ) , newProps = newVdom . props ; isSvg = isSvg || newVdom . type === 'svg' ; for ( var i in { ... oldProps , ... newProps } ) { if ( ( domProps . has ( i ) ? dom [ i ] : oldProps [ i ] ) !== newProps [ i ] ) { patchProp ( dom as Element , i , oldProps [ i ] , newProps [ i ] , isSvg ) ; } } setKey ( dom , newVdom . key ) ; patchChildren ( dom , newVdom . children , isSvg ) ; } return dom ; } ; const patchChildren = ( dom , newVKids , isSvg ? , oldKids = Array . from ( dom . childNodes ) as Node [ ] ) => { var tmpKid , oldKid , oldKey , newKey , oldHead = 0 , newHead = 0 , oldTail = oldKids . length - 1 , newTail = newVKids . length - 1 ; while ( newHead <= newTail && oldHead <= oldTail ) { if ( ( oldKey = getKey ( oldKids [ oldHead ] ) ) == null || oldKey !== getKey ( newVKids [ newHead ] ) ) { break ; } patchDom ( dom , oldKids [ oldHead ] , oldKids [ oldHead ++ ] , ( newVKids [ newHead ] = vdomify ( newVKids [ newHead ++ ] ) ) , isSvg ) ; } while ( newHead <= newTail && oldHead <= oldTail ) { if ( ( oldKey = getKey ( oldKids [ oldTail ] ) ) == null || oldKey !== getKey ( newVKids [ newTail ] ) ) { break ; } oldKids [ oldTail ] = patchDom ( dom , oldKids [ oldTail ] , oldKids [ oldTail -- ] , ( newVKids [ newTail ] = vdomify ( newVKids [ newTail -- ] ) ) , isSvg ) ; } if ( oldHead > oldTail ) { const insertBefore = oldKids [ oldHead ] || ( oldKids [ oldHead - 1 ] && oldKids [ oldHead - 1 ] . nextSibling || null ) ; while ( newHead <= newTail ) { dom . insertBefore ( createNode ( ( newVKids [ newHead ] = vdomify ( newVKids [ newHead ++ ] ) ) , isSvg ) , insertBefore ) ; } } else if ( newHead > newTail ) { while ( oldHead <= oldTail ) { dom . removeChild ( oldKids [ oldHead ++ ] ) ; } } else { const oldKeyed = new Map < any , Node > ( ) ; const newKeyed = new Set < any > ( ) ; for ( let i = oldHead ; i <= oldTail ; i ++ ) { if ( ( oldKey = getKey ( oldKids [ i ] ) ) != null ) { oldKeyed . set ( oldKey , oldKids [ i ] ) ; } } while ( newHead <= newTail ) { oldKey = getKey ( ( oldKid = oldKids [ oldHead ] ) ) ; newKey = getKey ( ( newVKids [ newHead ] = vdomify ( newVKids [ newHead ] ) ) ) ; if ( newKeyed . has ( oldKey ) || ( newKey != null && newKey === getKey ( oldKids [ oldHead + 1 ] ) ) ) { if ( oldKey == null ) { dom . removeChild ( oldKid ) ; } oldHead ++ ; continue ; } if ( newKey == null ) { if ( oldKey == null ) { patchDom ( dom , oldKid , oldKid , newVKids [ newHead ] , isSvg ) ; newHead ++ ; } oldHead ++ ; } else { if ( oldKey === newKey ) { patchDom ( dom , oldKid , oldKid , newVKids [ newHead ] , isSvg ) ; newKeyed . add ( newKey ) ; oldHead ++ ; } else { if ( ( tmpKid = oldKeyed . get ( newKey ) as Node ) != null ) { patchDom ( dom , dom . insertBefore ( tmpKid , oldKid ) , tmpKid , newVKids [ newHead ] , isSvg ) ; newKeyed . add ( newKey ) ; } else { patchDom ( dom , oldKid , null , newVKids [ newHead ] , isSvg ) ; } } newHead ++ ; } } while ( oldHead <= oldTail ) { if ( getKey ( ( oldKid = oldKids [ oldHead ++ ] ) ) == null ) { dom . removeChild ( oldKid ) ; } } for ( const [ key , node ] of oldKeyed ) { if ( ! newKeyed . has ( key ) ) { dom . removeChild ( node ) ; } } } return dom ; } ; const vdomify = ( vdom ) : VChild => vdom !== true && vdom !== false && vdom ? vdom : '' ; const createVdom = ( type , props , children , key ? ) : VNode => ( { type , props , children , key , } ) ; export const recycleNode = ( dom ) => dom . nodeType === Node . TEXT_NODE ? dom . nodeValue as string : createVdom ( dom . nodeName . toLowerCase ( ) , getDomProps ( dom as Element ) , EMPTY_ARR . map . call ( dom . childNodes , recycleNode ) as VNode [ ] , getKey ( dom ) ) ; export const h = ( type , props ? , ch ? ) => typeof type === 'function' ? type ( props || { } , ch ) : createVdom ( type , props || { } , Array . isArray ( ch ) ? ch : ch == null ? [ ] : [ ch ] , props ?. key ) ; export const React = { createElement : h } ; export const patch = ( dom , vdom , oldKids ? ) => { if ( Array . isArray ( vdom ) ) { dom = patchChildren ( dom , vdom , dom instanceof window . SVGElement , oldKids as Node [ ] ) ; } else { dom = patchDom ( dom . parentNode as Node , dom , dom , vdom ) ; } return dom ; } ;