import { IComponent } from '../types' ; import { Vnode , TAttributes } from "../vnode" ; import { isFromVM , buildProps , argumentsIsReady , getVMFunctionArguments , getValueByValue , getVMVal , getVMFunction , setVMVal , valueIsReady } from './utils' ; import { ChangeDetectionStrategy } from '../component' ; import { buildPipeScope } from './compiler-utils' ; export class export class CompileRepeatUtil { public fragment ? : Vnode [ ] ; param { Vnode [ ] } fragment memberof constructor ( fragment ? ) { this . fragment = fragment ; } param { * } val param { string } exp param { string } key returns { * } memberof public _getVMRepeatVal ( val , exp , key ) { let value ; const valueList = exp . replace ( /\(.*\)/ , '' ) . split ( '.' ) ; valueList . forEach ( ( v , index ) => { if ( v === key && index === 0 ) { value = val ; return ; } value = value [ v ] ; } ) ; return value ; } param { * } vm param { string } exp param { string } key param { * } setValue returns { * } memberof public _setValueByValue ( vm , exp , key , setValue ) { const valueList = exp . replace ( /\(.*\)/ , '' ) . split ( '.' ) ; let value = vm ; let lastKey ; valueList . forEach ( ( v , index ) => { if ( v === key && index === 0 ) return lastKey = v ; if ( index < valueList . length ) lastKey = v ; if ( index < valueList . length - 1 ) value = value [ v ] ; } ) ; if ( lastKey ) value [ lastKey ] = setValue ; } param { Vnode } vnode param { string } key param { string } dir param { string } exp param { number } index param { * } vm param { * } watchValue memberof public bind ( vnode , key ? , dir ? , exp ? , index ? , vm ? , watchValue ? , val ? ) { const repeatValue = vnode . repeatData [ key ] ; let value ; const needCompileStringList = exp . split ( '|' ) . map ( v => v . trim ( ) ) ; const needCompileValue = needCompileStringList [ 0 ] ; if ( /^.*\(.*\)$/ . test ( needCompileValue ) ) { if ( dir === 'model' ) throw new Error ( `directive: nv-model can't use ${ needCompileValue } as value` ) ; const fn = getVMFunction ( vm , needCompileValue ) ; const argsList = getVMFunctionArguments ( vm , needCompileValue , vnode ) ; value = this . pipeHandler ( exp , fn . apply ( vm , argsList ) , needCompileStringList , vm , vnode ) ; } else if ( needCompileValue === key || needCompileValue . indexOf ( `${ key }.` ) === 0 ) { value = this . pipeHandler ( exp , this . _getVMRepeatVal ( repeatValue , needCompileValue , key ) , needCompileStringList , vm , vnode ) ; } else if ( isFromVM ( vm , needCompileValue ) ) value = this . pipeHandler ( exp , getVMVal ( vm , needCompileValue ) , needCompileStringList , vm , vnode ) ; else if ( needCompileValue === '$index' ) value = index ; else if ( /^\'.*\'$/ . test ( needCompileValue ) ) value = this . pipeHandler ( exp , needCompileValue . match ( /^\'(.*)\'$/ ) [ 1 ] , needCompileStringList , vm , vnode ) ; else if ( /^\".*\"$/ . test ( needCompileValue ) ) value = this . pipeHandler ( exp , needCompileValue . match ( /^\"(.*)\"$/ ) [ 1 ] , needCompileStringList , vm , vnode ) ; else if ( ! /^\'.*\'$/ . test ( needCompileValue ) && ! /^\".*\"$/ . test ( needCompileValue ) && /(^[-,+]?\d+$)|(^[-, +]?\d+\.\d+$)/g . test ( needCompileValue ) ) value = this . pipeHandler ( exp , Number ( needCompileValue ) , needCompileStringList , vm , vnode ) ; else if ( needCompileValue === 'true' || needCompileValue === 'false' ) value = this . pipeHandler ( exp , ( needCompileValue === 'true' ) , needCompileStringList , vm , vnode ) ; else if ( needCompileValue === 'null' ) value = this . pipeHandler ( exp , null , needCompileStringList , vm , vnode ) ; else if ( needCompileValue === 'undefined' ) value = this . pipeHandler ( exp , undefined , needCompileStringList , vm , vnode ) ; else if ( vnode . repeatData ) { Object . keys ( vnode . repeatData ) . forEach ( data => { if ( needCompileValue === data || needCompileValue . indexOf ( `${ data }.` ) === 0 ) value = this . pipeHandler ( exp , getValueByValue ( vnode . repeatData [ data ] , needCompileValue , data ) , needCompileStringList , vm , vnode ) ; } ) ; } else throw new Error ( `directive: nv-${ dir } can't use recognize this value ${ needCompileValue }` ) ; if ( ! vnode . childNodes || vnode . childNodes . length === 0 ) this . templateUpdater ( vnode , repeatValue , key , vm ) ; switch ( dir ) { case 'model' : { let watchData ; if ( needCompileValue === key || needCompileValue . indexOf ( `${ key }.` ) === 0 ) { watchData = watchValue ; this . modelUpdater ( vnode , value , needCompileValue , key , index , watchData , vm ) ; } else if ( isFromVM ( vm , needCompileValue ) ) { watchData = this . pipeHandler ( exp , getVMVal ( vm , needCompileValue ) , needCompileStringList , vm , vnode ) ; this . modelUpdater ( vnode , value , needCompileValue , key , index , watchData , vm ) ; } break ; } case 'text' : { this . textUpdater ( vnode , value ) ; break ; } case 'if' : { this . ifUpdater ( vnode , value , vm ) ; break ; } case 'if-not' : { this . ifNotUpdater ( vnode , value , vm ) ; break ; } case 'class' : { this . classUpdater ( vnode , value ) ; break ; } case 'key' : { this . keyUpdater ( vnode , value ) ; break ; } case 'value' : { this . valueUpdater ( vnode , value ) ; break ; } default : this . commonUpdater ( vnode , value , dir ) ; } } param { Vnode } vnode param { * } val param { string } key param { * } vm memberof public templateUpdater ( vnode , val ? , key ? , vm ? ) { const text = vnode . nodeValue ; const reg = /\{\{(.*)\}\}/g ; if ( reg . test ( text ) ) { const textList = text . match ( /(\{\{[^\{\}]+?\}\})/g ) ; if ( textList && textList . length > 0 ) { for ( let i = 0 ; i < textList . length ; i ++ ) { const exp = textList [ i ] . replace ( '{{' , '' ) . replace ( '}}' , '' ) ; const needCompileStringList = exp . split ( '|' ) . map ( v => v . trim ( ) ) ; const needCompileValue = needCompileStringList [ 0 ] ; if ( /^.*\(.*\)$/ . test ( needCompileValue ) && argumentsIsReady ( needCompileValue , vnode , vm ) ) { const fn = getVMFunction ( vm , needCompileValue ) ; const argsList = getVMFunctionArguments ( vm , needCompileValue , vnode ) ; const fromVmValue = this . pipeHandler ( textList [ i ] , fn . apply ( vm , argsList ) , needCompileStringList , vm , vnode ) ; vnode . nodeValue = vnode . nodeValue . replace ( textList [ i ] , fromVmValue ) ; } else if ( needCompileValue === key || needCompileValue . indexOf ( `${ key }.` ) === 0 ) { const fromVmValue = this . pipeHandler ( textList [ i ] , this . _getVMRepeatVal ( val , needCompileValue , key ) , needCompileStringList , vm , vnode ) ; vnode . nodeValue = vnode . nodeValue . replace ( textList [ i ] , fromVmValue ) ; } else if ( isFromVM ( vm , needCompileValue ) ) { const fromVmValue = this . pipeHandler ( textList [ i ] , getVMVal ( vm , needCompileValue ) , needCompileStringList , vm , vnode ) ; vnode . nodeValue = vnode . nodeValue . replace ( textList [ i ] , fromVmValue ) ; } else if ( vnode . repeatData ) { Object . keys ( vnode . repeatData ) . forEach ( data => { if ( needCompileValue === data || needCompileValue . indexOf ( `${ data }.` ) === 0 ) { const fromVmValue = this . pipeHandler ( textList [ i ] , getValueByValue ( vnode . repeatData [ data ] , needCompileValue , data ) , needCompileStringList , vm , vnode ) ; vnode . nodeValue = vnode . nodeValue . replace ( textList [ i ] , fromVmValue ) ; } } ) ; } else throw new Error ( `directive: {{${ needCompileValue }}} can\'t use recognize ${ needCompileValue }` ) ; } } } } param { Vnode } vnode param { * } value param { string } exp param { string } key param { number } index param { * } watchData param { * } vm memberof public modelUpdater ( vnode , value , exp , key , index , watchData , vm ) { vnode . value = typeof value === 'undefined' ? '' : value ; const findAttribute = vnode . attributes . find ( attr => attr . name === 'nv-model' ) ; findAttribute . nvValue = ( typeof value === 'undefined' ? '' : value ) ; const utilVm = this ; const func = function ( event ) : void { event . preventDefault ( ) ; if ( isFromVM ( vm , exp ) ) { if ( ( event . target as HTMLInputElement ) . value === watchData ) return ; setVMVal ( vm , exp , ( event . target as HTMLInputElement ) . value ) ; } else if ( exp === key || exp . indexOf ( `${ key }.` ) === 0 ) { if ( typeof watchData [ index ] !== 'object' ) watchData [ index ] = ( event . target as HTMLInputElement ) . value ; if ( typeof watchData [ index ] === 'object' ) { let vals = getValueByValue ( watchData [ index ] , exp , key ) ; vals = ( event . target as HTMLInputElement ) . value ; utilVm . _setValueByValue ( watchData [ index ] , exp , key , vals ) ; } } else throw new Error ( `directive: nv-model can\'t use recognize this prop ${ exp }` ) ; if ( ( vm as IComponent ) . $nvChangeDetection === ChangeDetectionStrategy . OnPush ) { if ( ( vm as IComponent ) . nvDoCheck ) ( vm as IComponent ) . nvDoCheck ( ) ; ( vm as IComponent ) . render ( ) ; } } ; const sameEventType = vnode . eventTypes . find ( _eventType => _eventType . type === 'input' ) ; if ( sameEventType ) sameEventType . handler = func ; if ( ! sameEventType ) vnode . eventTypes . push ( { type : 'input' , handler : func , token : value , } ) ; } param { Vnode } vnode param { * } value returns { void } memberof public textUpdater ( vnode , value ) { const findAttribute = vnode . attributes . find ( attr => attr . name === 'nv-text' ) ; findAttribute . nvValue = ( typeof value === 'undefined' ? '' : value ) ; vnode . nodeValue = typeof value === 'undefined' ? '' : value ; if ( ! vnode . childNodes || ( vnode . childNodes && vnode . childNodes . length > 0 ) ) vnode . childNodes = [ ] ; vnode . childNodes . push ( new Vnode ( { type : 'text' , nodeValue : typeof value === 'undefined' ? '' : value , parentVnode : vnode , template : typeof value === 'undefined' ? '' : value , voidElement : true , } ) ) ; vnode . voidElement = true ; } param { Vnode } vnode param { * } value param { * } vm memberof public ifUpdater ( vnode , value , vm ) { const valueOfBoolean = Boolean ( value ) ; if ( ! valueOfBoolean && vnode . parentVnode . childNodes . indexOf ( vnode ) !== - 1 ) vnode . parentVnode . childNodes . splice ( vnode . parentVnode . childNodes . indexOf ( vnode ) , 1 ) ; if ( valueOfBoolean ) { const findAttribute = vnode . attributes . find ( attr => attr . name === 'nv-if' ) ; findAttribute . nvValue = valueOfBoolean ; } } param { Vnode } vnode param { * } value param { * } vm memberof public ifNotUpdater ( vnode , value , vm ) { const valueOfBoolean = ! Boolean ( value ) ; if ( ! valueOfBoolean && vnode . parentVnode . childNodes . indexOf ( vnode ) !== - 1 ) vnode . parentVnode . childNodes . splice ( vnode . parentVnode . childNodes . indexOf ( vnode ) , 1 ) ; if ( valueOfBoolean ) { const findAttribute = vnode . attributes . find ( attr => attr . name === 'nv-if-not' ) ; findAttribute . nvValue = valueOfBoolean ; } } param { Vnode } vnode param { * } value returns { void } memberof public classUpdater ( vnode , value ) { const findAttribute = vnode . attributes . find ( attr => attr . name === 'nv-class' ) ; findAttribute . nvValue = value ; } param { Vnode } vnode param { * } value memberof public keyUpdater ( vnode , value ) { const findAttribute = vnode . attributes . find ( attr => attr . name === 'nv-key' ) ; findAttribute . nvValue = value ; vnode . key = value ; } param { Vnode } vnode param { * } value memberof public valueUpdater ( vnode , value ) { const findAttribute = vnode . attributes . find ( attr => attr . name === 'nv-value' ) ; findAttribute . nvValue = value ; vnode . value = value ; } param { Vnode } vnode param { * } value param { string } dir memberof public commonUpdater ( vnode , value , dir ) { const findAttribute = vnode . attributes . find ( attr => attr . name === `nv-${ dir }` ) ; findAttribute . nvValue = value ; } param { Vnode } vnode param { * } vm param { string } exp param { string } eventName param { string } key param { * } val memberof public eventHandler ( vnode , vm , exp , eventName , key , val ) { const eventType = eventName . split ( ':' ) [ 1 ] ; const fn = getVMFunction ( vm , exp ) ; const args = exp . match ( /\((.*)\)/ ) [ 1 ] . replace ( /\s+/g , '' ) . split ( ',' ) ; const utilVm = this ; const func = function ( event ) : any { const argsList = [ ] ; args . forEach ( arg => { if ( arg === '' ) return false ; if ( arg === '$event' ) return argsList . push ( event ) ; if ( arg === '$element' ) return argsList . push ( event . target ) ; if ( arg === 'true' || arg === 'false' ) return argsList . push ( arg === 'true' ) ; if ( arg === 'null' ) return argsList . push ( null ) ; if ( arg === 'undefined' ) return argsList . push ( undefined ) ; if ( isFromVM ( vm , arg ) ) return argsList . push ( getVMVal ( vm , arg ) ) ; if ( /^\'.*\'$/ . test ( arg ) ) return argsList . push ( arg . match ( /^\'(.*)\'$/ ) [ 1 ] ) ; if ( /^\".*\"$/ . test ( arg ) ) return argsList . push ( arg . match ( /^\"(.*)\"$/ ) [ 1 ] ) ; if ( ! /^\'.*\'$/ . test ( arg ) && ! /^\".*\"$/ . test ( arg ) && /(^[-,+]?\d+$)|(^[-, +]?\d+\.\d+$)/ . test ( arg ) ) return argsList . push ( Number ( arg ) ) ; if ( arg === key || arg . indexOf ( `${ key }.` ) === 0 ) return argsList . push ( utilVm . _getVMRepeatVal ( val , arg , key ) ) ; if ( vnode . repeatData ) { Object . keys ( vnode . repeatData ) . forEach ( data => { if ( arg === data || arg . indexOf ( `${ data }.` ) === 0 ) return argsList . push ( getValueByValue ( vnode . repeatData [ data ] , arg , data ) ) ; } ) ; } } ) ; const saveWatchStatus = ( vm as IComponent ) . $watchStatus ; if ( saveWatchStatus === 'available' ) ( vm as IComponent ) . $watchStatus = 'pending' ; fn . apply ( vm , argsList ) ; if ( saveWatchStatus === 'available' ) { ( vm as IComponent ) . $watchStatus = 'available' ; if ( ( vm as IComponent ) . $isWaitingRender && ( vm as IComponent ) . nvDoCheck ) ( vm as IComponent ) . nvDoCheck ( ) ; if ( ( vm as IComponent ) . $isWaitingRender ) { ( vm as IComponent ) . render ( ) ; ( vm as IComponent ) . $isWaitingRender = false ; } } } ; if ( eventType && fn ) { const sameEventType = vnode . eventTypes . find ( _eventType => _eventType . type === eventType ) ; if ( sameEventType ) { sameEventType . handler = func ; sameEventType . token = fn ; } if ( ! sameEventType ) vnode . eventTypes . push ( { type : eventType , handler : func , token : fn , } ) ; } } param { Vnode } vnode param { * } vm param { TAttributes } attr param { string } prop memberof public propHandler ( vnode , vm , attr ) { const prop = /^\{(.+)\}$/ . exec ( attr . value ) ; if ( prop ) { const propValue = prop [ 1 ] ; let _prop = null ; if ( /^.*\(.*\)$/ . test ( propValue ) ) { const fn = getVMFunction ( vm , propValue ) ; const args = propValue . match ( /\((.*)\)/ ) [ 1 ] . replace ( /\s+/g , '' ) . split ( ',' ) ; const argsList = [ ] ; args . forEach ( arg => { if ( arg === '' ) return false ; if ( arg === '$element' ) return argsList . push ( vnode . nativeElement ) ; if ( arg === 'true' || arg === 'false' ) return argsList . push ( arg === 'true' ) ; if ( arg === 'null' ) return argsList . push ( null ) ; if ( arg === 'undefined' ) return argsList . push ( undefined ) ; if ( isFromVM ( vm , arg ) ) return argsList . push ( getVMVal ( vm , arg ) ) ; if ( /^\'.*\'$/ . test ( arg ) ) return argsList . push ( arg . match ( /^\'(.*)\'$/ ) [ 1 ] ) ; if ( /^\".*\"$/ . test ( arg ) ) return argsList . push ( arg . match ( /^\"(.*)\"$/ ) [ 1 ] ) ; if ( ! /^\'.*\'$/ . test ( arg ) && ! /^\".*\"$/ . test ( arg ) && /(^[-,+]?\d+$)|(^[-, +]?\d+\.\d+$)/g . test ( arg ) ) return argsList . push ( Number ( arg ) ) ; if ( vnode . repeatData ) { Object . keys ( vnode . repeatData ) . forEach ( data => { if ( arg === data || arg . indexOf ( `${ data }.` ) === 0 ) return argsList . push ( getValueByValue ( vnode . repeatData [ data ] , arg , data ) ) ; } ) ; } } ) ; const value = fn . apply ( vm , argsList ) ; attr . nvValue = value ; return ; } const valueList = propValue . split ( '.' ) ; const key = valueList [ 0 ] ; if ( isFromVM ( vm , propValue ) ) { _prop = getVMVal ( vm , propValue ) ; attr . nvValue = buildProps ( _prop , vm ) ; return ; } if ( vnode . repeatData && vnode . repeatData . hasOwnProperty ( key ) ) { _prop = getValueByValue ( vnode . repeatData [ key ] , propValue , key ) ; attr . nvValue = buildProps ( _prop , vm ) ; return ; } if ( /^\'.*\'$/ . test ( propValue ) ) { attr . nvValue = propValue . match ( /^\'(.*)\'$/ ) [ 1 ] ; return ; } if ( /^\".*\"$/ . test ( propValue ) ) { attr . nvValue = propValue . match ( /^\"(.*)\"$/ ) [ 1 ] ; return ; } if ( ! /^\'.*\'$/ . test ( propValue ) && ! /^\".*\"$/ . test ( propValue ) && /(^[-,+]?\d+$)|(^[-, +]?\d+\.\d+$)/ . test ( propValue ) ) { attr . nvValue = Number ( propValue ) ; return ; } if ( propValue === 'true' || propValue === 'false' ) { attr . nvValue = ( propValue === 'true' ) ; return ; } if ( propValue === 'null' ) { attr . nvValue = null ; return ; } if ( propValue === 'undefined' ) { attr . nvValue = undefined ; return ; } } } public pipeHandler ( oldExp , value , needCompileStringList , vm , vnode ) { let canCompileFlag = true ; let fromVmValue = value ; if ( needCompileStringList . length > 1 ) { needCompileStringList . forEach ( ( need , index ) => { if ( index !== 0 ) { const pipeArgusList = [ ] ; let pipeName = '' ; need . split ( ':' ) . forEach ( ( v , i ) => { if ( i === 0 ) pipeName = v . trim ( ) ; else pipeArgusList . push ( v . trim ( ) ) ; } ) ; const argList = [ ] ; pipeArgusList . forEach ( pipeArgu => { if ( ! valueIsReady ( pipeArgu , vnode , vm ) ) { canCompileFlag = false ; return ; } let pipeArguValue = null ; if ( isFromVM ( vm , pipeArgu ) ) pipeArguValue = getVMVal ( vm , pipeArgu ) ; else if ( /^\'.*\'$/ . test ( pipeArgu ) ) pipeArguValue = pipeArgu . match ( /^\'(.*)\'$/ ) [ 1 ] ; else if ( /^\".*\"$/ . test ( pipeArgu ) ) pipeArguValue = pipeArgu . match ( /^\"(.*)\"$/ ) [ 1 ] ; else if ( ! /^\'.*\'$/ . test ( pipeArgu ) && ! /^\".*\"$/ . test ( pipeArgu ) && /(^[-,+]?\d+$)|(^[-, +]?\d+\.\d+$)/g . test ( pipeArgu ) ) pipeArguValue = Number ( pipeArgu ) ; else if ( pipeArgu === 'true' || pipeArgu === 'false' ) pipeArguValue = ( pipeArgu === 'true' ) ; else if ( pipeArgu === 'null' ) pipeArguValue = null ; else if ( pipeArgu === 'undefined' ) pipeArguValue = undefined ; else if ( vnode . repeatData ) { Object . keys ( vnode . repeatData ) . forEach ( data => { if ( pipeArgu === data || pipeArgu . indexOf ( `${ data }.` ) === 0 ) pipeArguValue = getValueByValue ( vnode . repeatData [ data ] , pipeArgu , data ) ; } ) ; } argList . push ( pipeArguValue ) ; } ) ; if ( canCompileFlag ) { const PipeClass = vm . $declarationMap . get ( pipeName ) ; const pipeInstance = buildPipeScope ( PipeClass , vm . $nativeElement , vm ) ; if ( ! pipeInstance . transform ) throw Error ( `Pipe ${ pipeName } don't implement the method 'transform'` ) ; fromVmValue = pipeInstance . transform ( value , ... argList ) ; } } } ) ; } if ( canCompileFlag ) return fromVmValue ; else return oldExp ; } }