import { EventEmitter } from "./EventEmitter" ; import { UkuleleUtil } from "../util/UkuleleUtil" ; import { BoundItemAttributeFactory } from './BoundItemAttributeFactory' ; import { BoundItemExpression } from '../model/BoundItemExpression' ; import { BoundItemInnerText } from '../model/BoundItemInnerText' ; import { BoundItemRepeat } from '../model/BoundItemRepeat' ; import { BoundItemComponentAttribute } from "../model/BoundItemComponentAttribute" ; import { elementChangedBinder } from "./ElementActionBinder" ; import { IUkulele } from "./IUkulele" ; import { EventListener } from "../extend/EventListener" ; import { Selector } from "../extend/Selector" ; import { Event as UkuEvent } from "./Event" ; export class Analyzer extends EventEmitter { private uku : IUkulele ; private defMgr ; static ANALYIZE_COMPLETED : string = 'analyizeCompleted' ; constructor ( _uku ) { super ( ) ; this . uku = _uku ; this . defMgr = this . uku . _internal_getDefinitionManager ( ) ; } public analyizeElement ( ele ) { this . searchComponent ( ele ) . then ( ( element ) => { this . searchExpression ( element ) ; this . searchUkuAttribute ( element ) ; if ( this . hasListener ( Analyzer . ANALYIZE_COMPLETED ) ) { this . dispatchEvent ( new UkuEvent ( Analyzer . ANALYIZE_COMPLETED , element ) ) ; } } ) ; } private sortAttributes ( subElement ) { let orderAttrs = [ ] ; let listenerAttrs = [ ] ; for ( let i = 0 ; i < subElement . attributes . length ; i ++ ) { let attribute = subElement . attributes [ i ] ; if ( attribute . nodeName . search ( "uku-on" ) !== - 1 ) { listenerAttrs . push ( attribute ) ; } else { orderAttrs . push ( attribute ) ; } } orderAttrs = orderAttrs . concat ( listenerAttrs ) ; return orderAttrs ; } private searchUkuAttribute ( element ) { let subElements = [ ] ; let isSelfHasUkuTag = Selector . fuzzyFind ( element , 'uku-' ) ; if ( isSelfHasUkuTag ) { subElements . push ( isSelfHasUkuTag ) ; } let allChildren = Selector . querySelectorAll ( element , "*" ) ; for ( let i = 0 ; i < allChildren . length ; i ++ ) { let child = allChildren [ i ] as HTMLElement ; let matchElement = Selector . fuzzyFind ( child , 'uku-' ) ; if ( matchElement && ! UkuleleUtil . isInRepeat ( matchElement ) ) { subElements . push ( matchElement ) ; } } for ( let n = 0 ; n < subElements . length ; n ++ ) { let subElement = subElements [ n ] ; let orderAttrs = this . sortAttributes ( subElement ) ; for ( let j = 0 ; j < orderAttrs . length ; j ++ ) { let attribute = orderAttrs [ j ] ; if ( UkuleleUtil . searchUkuAttrTag ( attribute . nodeName ) > - 1 ) { let tempArr = attribute . nodeName . split ( '-' ) ; tempArr . shift ( ) ; let attrName = tempArr . join ( '-' ) ; if ( attrName !== "application" ) { if ( attrName . search ( 'on' ) === 0 ) { if ( ! UkuleleUtil . isRepeat ( subElement ) && ! UkuleleUtil . isInRepeat ( subElement ) ) { this . dealWithEvent ( subElement , attrName ) ; } } else if ( attrName . search ( 'repeat' ) !== - 1 ) { this . dealWithRepeat ( subElement ) ; } else { if ( ! UkuleleUtil . isRepeat ( subElement ) && ! UkuleleUtil . isInRepeat ( subElement ) ) { if ( attrName !== "text" ) { this . dealWithAttribute ( subElement , attrName ) ; } else { this . dealWithInnerText ( subElement ) ; } } } } } } } } private async searchComponent ( element ) { let comp = this . defMgr . getComponent ( element . localName ) ; if ( comp ) { if ( ! comp . lazy ) { let attrs = element . attributes ; let compDef = this . defMgr . getComponentsDefinition ( ) [ comp . tagName ] ; if ( ! UkuleleUtil . isRepeat ( element ) && ! UkuleleUtil . isInRepeat ( element ) ) { return this . dealWithComponent ( element , compDef . template , compDef . controllerClazz , attrs ) ; } else { return element ; } } else { await this . defMgr . addLazyComponentDefinition ( comp . tagName , comp . templateUrl ) ; let attrs = element . attributes ; let compDef = this . defMgr . getComponentsDefinition ( ) [ comp . tagName ] ; if ( ! UkuleleUtil . isRepeat ( element ) && ! UkuleleUtil . isInRepeat ( element ) ) { return this . dealWithComponent ( element , compDef . template , compDef . controllerClazz , attrs ) ; } else { return element ; } } } else { if ( element . children && element . children . length > 0 ) { for ( let i = 0 ; i < element . children . length ; i ++ ) { let child = element . children [ i ] ; await this . searchComponent ( child ) ; } return element ; } else { return element ; } } } private async dealWithComponent ( tag , template , Clazz , attrs ) { let time = new Date ( ) . getTime ( ) ; let randomAlias = 'cc_' + Math . floor ( time * Math . random ( ) ) . toString ( ) ; template = template . replace ( new RegExp ( "\'cc\\." , 'gm' ) , "'" + randomAlias + '.' ) ; template = template . replace ( new RegExp ( '"cc\\.' , 'gm' ) , '"' + randomAlias + '.' ) ; template = template . replace ( new RegExp ( '\{\{cc\\.' , 'gm' ) , "{{" + randomAlias + '.' ) ; template = template . replace ( new RegExp ( ' cc\\.' , 'gm' ) , ' ' + randomAlias + '.' ) ; template = template . replace ( new RegExp ( '\\(cc\\.' , 'gm' ) , '(' + randomAlias + '.' ) ; template = template . replace ( new RegExp ( '\\,cc\\.' , 'gm' ) , ',' + randomAlias + '.' ) ; template = template . replace ( new RegExp ( '\\.cc\\.' , 'gm' ) , '.' + randomAlias + '.' ) ; template = template . replace ( new RegExp ( '\\!cc\\.' , 'gm' ) , '!' + randomAlias + '.' ) ; let tempFragment = document . createElement ( 'div' ) ; tempFragment . insertAdjacentHTML ( 'afterBegin' as InsertPosition , template ) ; if ( tempFragment . children . length > 1 ) { template = tempFragment . outerHTML ; } tag . insertAdjacentHTML ( 'beforeBegin' , template ) ; let htmlDom = tag . previousElementSibling ; htmlDom . classList . add ( tag . localName ) ; let cc ; if ( Clazz ) { cc = new Clazz ( this . uku ) ; cc . _dom = htmlDom ; cc . fire = ( eventType , data , bubbles = false , cancelable = true ) => { let event = new CustomEvent ( eventType . toLowerCase ( ) , { "bubbles" : bubbles , "cancelable" : cancelable } ) ; event [ 'data' ] = data ; cc . _dom . dispatchEvent ( event ) ; } ; this . uku . registerController ( randomAlias , cc ) ; for ( let i = 0 ; i < attrs . length ; i ++ ) { let attr = attrs [ i ] ; if ( UkuleleUtil . searchUkuAttrTag ( attr . nodeName ) !== 0 || attr . nodeName . search ( "uku-on" ) !== - 1 || attr . nodeName === "uku-render" || attr . nodeName === "uku-visible" ) { htmlDom . setAttribute ( attr . nodeName , attr . nodeValue ) ; } else { let tagName = UkuleleUtil . getAttrFromUkuTag ( attr . nodeName , true ) ; let controllerModels = this . defMgr . getControllerModelByName ( attr . nodeValue ) ; if ( controllerModels && controllerModels . length > 0 ) { let boundItem = new BoundItemComponentAttribute ( attr . nodeValue , tagName , cc , this . uku ) ; let controllers = [ ] ; controllerModels . forEach ( controllerModel => { controllerModel . addBoundItem ( boundItem ) ; controllers . push ( controllerModel . controllerInstance ) ; } ) ; boundItem . render ( controllers ) ; } else { let boundItem = new BoundItemComponentAttribute ( attr . nodeValue , tagName , cc , this . uku ) ; boundItem . render ( [ ] ) ; } } } } else { for ( let i = 0 ; i < attrs . length ; i ++ ) { let attr = attrs [ i ] ; if ( UkuleleUtil . searchUkuAttrTag ( attr . nodeName ) !== 0 || attr . nodeName . search ( "uku-on" ) !== - 1 || attr . nodeName === "uku-render" || attr . nodeName === "uku-visible" ) { htmlDom . setAttribute ( attr . nodeName , attr . nodeValue ) ; } } } tag . parentNode . removeChild ( tag ) ; if ( htmlDom . children && htmlDom . children . length > 0 ) { for ( let j = 0 ; j < htmlDom . children . length ; j ++ ) { let child = htmlDom . children [ j ] ; await this . searchComponent ( child ) ; } if ( cc && cc . _initialized && typeof ( cc . _initialized ) === 'function' ) { cc . _initialized ( randomAlias , cc . _dom ) ; } return htmlDom ; } else { if ( cc && cc . _initialized && typeof ( cc . _initialized ) === 'function' ) { cc . _initialized ( randomAlias , cc . _dom ) ; } return htmlDom ; } } private searchExpression ( element ) { if ( UkuleleUtil . searchUkuExpTag ( Selector . directText ( element ) ) !== - 1 ) { if ( ! UkuleleUtil . isRepeat ( element ) && ! UkuleleUtil . isInRepeat ( element ) ) { this . dealWithExpression ( element ) ; } } for ( let i = 0 ; i < element . children . length ; i ++ ) { this . searchExpression ( element . children [ i ] as HTMLElement ) ; } } private dealWithExpression ( element ) { let expression = Selector . directText ( element ) ; if ( UkuleleUtil . searchUkuExpTag ( expression ) !== - 1 ) { let attr = expression . slice ( 2 , - 2 ) ; let controllerModels = this . defMgr . getControllerModelByName ( attr ) ; if ( controllerModels && controllerModels . length > 0 ) { let boundItem = new BoundItemExpression ( attr , expression , element , this . uku ) ; let controllers = [ ] ; controllerModels . forEach ( controllerModel => { controllerModel . addBoundItem ( boundItem ) ; controllers . push ( controllerModel . controllerInstance ) ; } ) ; boundItem . render ( controllers ) ; } } } private dealWithAttribute : Function = function ( element , tagName ) { let attr = element . getAttribute ( "uku-" + tagName ) ; let controllerModels = this . defMgr . getControllerModelByName ( attr ) ; if ( controllerModels && controllerModels . length > 0 ) { let boundItem = BoundItemAttributeFactory . getInstance ( ) . generateInstance ( attr , tagName , element , this . uku ) ; let controllers = [ ] ; controllerModels . forEach ( controllerModel => { controllerModel . addBoundItem ( boundItem ) ; elementChangedBinder ( element , tagName , controllerModel , this . uku . refresh , this . uku ) ; controllers . push ( controllerModel . controllerInstance ) ; } ) ; boundItem . render ( controllers ) ; } } ; private dealWithInnerText ( element ) { let attr = element . getAttribute ( "uku-text" ) ; if ( attr ) { let controllerModels = this . defMgr . getControllerModelByName ( attr ) ; if ( controllerModels && controllerModels . length > 0 ) { let boundItem = new BoundItemInnerText ( attr , element , this . uku ) ; let controllers = [ ] ; controllerModels . forEach ( controllerModel => { controllerModel . addBoundItem ( boundItem ) ; controllers . push ( controllerModel . controllerInstance ) ; } ) ; boundItem . render ( controllers ) ; } } } private dealWithEvent ( element , eventName ) { let expression = element . getAttribute ( "uku-" + eventName ) ; let eventNameInListener = eventName . substring ( 2 ) ; eventNameInListener = eventNameInListener . toLowerCase ( ) ; let controllerModels = this . defMgr . getControllerModelByName ( expression ) ; if ( ! controllerModels || controllerModels . length === 0 ) { controllerModels = [ ] ; } EventListener . addEventListener ( element , eventNameInListener , ( event ) => { let alias_list = [ ] ; controllerModels . forEach ( controllerModel => { alias_list . push ( controllerModel . alias ) ; } ) ; let index = UkuleleUtil . searchUkuFuncArg ( expression ) ; if ( index === - 1 ) { let handler = new Function ( "event" , "return " + expression ) ; console . log ( handler . toString ( ) ) ; handler ( event ) ; } else { let i = expression . search ( /\(/ ) ; let arg = 'event' ; if ( expression [ i + 1 ] !== ')' ) { arg = 'event,' ; } let arr = expression . split ( '(' ) ; arr [ 1 ] = arg + arr [ 1 ] ; let new_expression = arr . join ( "(" ) ; ( function ( e ) { let tempScope = { } ; tempScope [ 'event' ] = e ; eval ( new_expression ) ; tempScope = null ; } ) ( event ) ; } this . uku . refresh ( alias_list , element ) ; } ) ; } private dealWithRepeat ( element ) { let repeatExpression = element . getAttribute ( "uku-repeat" ) ; let tempArr = repeatExpression . split ( ' in ' ) ; let itemName = tempArr [ 0 ] ; let attr = tempArr [ 1 ] ; let controllerModels = this . defMgr . getControllerModelByName ( attr ) ; if ( controllerModels && controllerModels . length > 0 ) { let controllers = [ ] ; let boundItem = new BoundItemRepeat ( attr , itemName , element , this . uku ) ; controllerModels . forEach ( controllerModel => { controllerModel . addBoundItem ( boundItem ) ; controllers . push ( controllerModel . controllerInstance ) ; } ) ; boundItem . render ( controllers ) ; } } }